diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt index 699973d8..9e267a57 100644 --- a/fastlane/metadata/en-US/release_notes.txt +++ b/fastlane/metadata/en-US/release_notes.txt @@ -1,5 +1,3 @@ Enjoy toooting! This version includes following improvements and fixes: -- Align filter experience with v4.0 and above -- Supports enlarging user's avatar and banner -- Fix iPad weird sizing (not optimisation) -- Experiment (!) support of Pleroma +- Fixed wrongly update notification +- Fix some random crashes diff --git a/fastlane/metadata/zh-Hans/release_notes.txt b/fastlane/metadata/zh-Hans/release_notes.txt index ee10414f..8ba28658 100644 --- a/fastlane/metadata/zh-Hans/release_notes.txt +++ b/fastlane/metadata/zh-Hans/release_notes.txt @@ -1,5 +1,3 @@ toooting愉快!此版本包括以下改进和修复: -- 改进过滤体验,与v4.0以上版本一致 -- 支持查看用户的头像和横幅图片 -- 修复iPad部分尺寸问题(非优化) -- 试验性(!)支持Pleroma +- 修复错误的升级通知 +- 修复部分应用崩溃 diff --git a/package.json b/package.json index 82ef675d..1a0f20db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tooot", - "version": "4.7.1", + "version": "4.7.2", "description": "tooot for Mastodon", "author": "xmflsct ", "license": "GPL-3.0-or-later", diff --git a/src/api/general.ts b/src/api/general.ts index f13e698f..d8433546 100644 --- a/src/api/general.ts +++ b/src/api/general.ts @@ -44,7 +44,10 @@ const apiGeneral = async ({ ...userAgent, ...headers }, - ...(body && { data: body }) + ...(body && + (body instanceof FormData + ? (body as (FormData & { _parts: [][] }) | undefined)?._parts?.length + : Object.keys(body).length) && { data: body }) }) .then(response => { let links: { diff --git a/src/api/instance.ts b/src/api/instance.ts index d6446e8f..71cfa348 100644 --- a/src/api/instance.ts +++ b/src/api/instance.ts @@ -64,7 +64,7 @@ const apiInstance = async ({ Authorization: `Bearer ${token}` }) }, - ...(body && { data: body }), + ...((body as (FormData & { _parts: [][] }) | undefined)?._parts.length && { data: body }), ...extras }) .then(response => { diff --git a/src/api/tooot.ts b/src/api/tooot.ts index 0ebb5b57..c491438a 100644 --- a/src/api/tooot.ts +++ b/src/api/tooot.ts @@ -47,7 +47,10 @@ const apiTooot = async ({ ...userAgent, ...headers }, - ...(body && { data: body }) + ...(body && + (body instanceof FormData + ? (body as (FormData & { _parts: [][] }) | undefined)?._parts?.length + : Object.keys(body).length) && { data: body }) }) .then(response => { return Promise.resolve({ diff --git a/src/components/Hashtag.tsx b/src/components/Hashtag.tsx index 90ab7ef7..07b3c029 100644 --- a/src/components/Hashtag.tsx +++ b/src/components/Hashtag.tsx @@ -52,22 +52,24 @@ const ComponentHashtag: React.FC = ({ > #{hashtag.name} - setHeight(height)} - > - parseInt(h.uses)).reverse()} - width={width} - height={height} - margin={children ? StyleConstants.Spacing.S : undefined} - /> - {children} - + {hashtag.history?.length ? ( + setHeight(height)} + > + parseInt(h.uses)).reverse()} + width={width} + height={height} + margin={children ? StyleConstants.Spacing.S : undefined} + /> + {children} + + ) : null} ) } diff --git a/src/components/Parse/HTML.tsx b/src/components/Parse/HTML.tsx index 4652fa10..7d6212cb 100644 --- a/src/components/Parse/HTML.tsx +++ b/src/components/Parse/HTML.tsx @@ -218,7 +218,7 @@ const ParseHTML = React.memo( if (children) { return ( { {` Source: ${ detected?.language }; Confidence: ${ - detected?.confidence.toString().slice(0, 5) || 'null' + detected?.confidence?.toString().slice(0, 5) || 'null' }; Target: ${targetLanguage}`} ) : null } diff --git a/src/components/openLink.ts b/src/components/openLink.ts index 4e566f7d..cf0f9f68 100644 --- a/src/components/openLink.ts +++ b/src/components/openLink.ts @@ -7,6 +7,7 @@ import { SearchResult } from '@utils/queryHooks/search' import { getSettingsBrowser } from '@utils/slices/settingsSlice' import * as Linking from 'expo-linking' import * as WebBrowser from 'expo-web-browser' +import validUrl from 'valid-url' export let loadingLink = false @@ -86,18 +87,21 @@ const openLink = async (url: string, navigation?: any) => { } loadingLink = false - switch (getSettingsBrowser(store.getState())) { - // Some links might end with an empty space at the end that triggers an error - case 'internal': - await WebBrowser.openBrowserAsync(encodeURI(url), { - dismissButtonStyle: 'close', - enableBarCollapsing: true, - ...(await browserPackage()) - }) - break - case 'external': - await Linking.openURL(encodeURI(url)) - break + const validatedUrl = validUrl.isWebUri(url) + if (validatedUrl) { + switch (getSettingsBrowser(store.getState())) { + // Some links might end with an empty space at the end that triggers an error + case 'internal': + await WebBrowser.openBrowserAsync(validatedUrl, { + dismissButtonStyle: 'close', + enableBarCollapsing: true, + ...(await browserPackage()) + }) + break + case 'external': + await Linking.openURL(validatedUrl) + break + } } } diff --git a/src/i18n/ca/components/contextMenu.json b/src/i18n/ca/components/contextMenu.json index 298a5954..af58d16d 100644 --- a/src/i18n/ca/components/contextMenu.json +++ b/src/i18n/ca/components/contextMenu.json @@ -15,13 +15,13 @@ "action_false": "Bloqueja l'usuari", "action_true": "Deixa de bloquejar l'usuari", "alert": { - "title": "" + "title": "Vols bloquejar a @{{username}}?" } }, "reports": { "action": "Denuncia i bloqueja l'usuari", "alert": { - "title": "" + "title": "Vols denunciar i bloquejar a @{{username}}?" } } }, diff --git a/src/i18n/ca/components/timeline.json b/src/i18n/ca/components/timeline.json index cbdd782d..fb18a673 100644 --- a/src/i18n/ca/components/timeline.json +++ b/src/i18n/ca/components/timeline.json @@ -32,7 +32,7 @@ }, "update": "L'impuls ha sigut editat", "admin.sign_up": "{{name}} s'ha unit a la instància", - "admin.report": "" + "admin.report": "{{name}} ha reportat:" }, "actions": { "reply": { @@ -100,7 +100,7 @@ "fullConversation": "Llegeix conversacions", "translate": { "default": "Tradueix", - "succeed": "Traduït per {{provider}} amb {{source}}", + "succeed": "Traduït per {{provider}} des de l'idioma {{source}}", "failed": "Error al traduir", "source_not_supported": "L'idioma de la publicació no està suportada", "target_not_supported": "Aquest idioma no està suportat" diff --git a/src/i18n/ca/screens/compose.json b/src/i18n/ca/screens/compose.json index c1c86ac3..95f1a1ae 100644 --- a/src/i18n/ca/screens/compose.json +++ b/src/i18n/ca/screens/compose.json @@ -2,7 +2,7 @@ "heading": { "left": { "alert": { - "title": "Voleu cancel·lar l'edició?", + "title": "Vols cancel·lar l'edició?", "buttons": { "save": "Desa l'esborrany", "delete": "Esborra l'esborrany" @@ -13,9 +13,9 @@ "button": { "default": "Publica", "conversation": "Envia un missatge directe", - "reply": "Publica la resposta", - "deleteEdit": "Publicació", - "edit": "Publica l'edició", + "reply": "Respon", + "deleteEdit": "Torna-ho a publicar", + "edit": "Edita", "share": "Publicació" }, "alert": { diff --git a/src/i18n/ca/screens/tabs.json b/src/i18n/ca/screens/tabs.json index f247af79..ecff2fe6 100644 --- a/src/i18n/ca/screens/tabs.json +++ b/src/i18n/ca/screens/tabs.json @@ -133,7 +133,7 @@ "listDelete": { "heading": "Esborra la llista", "confirm": { - "title": "Vol esborrar la llista \"{{list}}\"?", + "title": "Vols esborrar la llista \"{{list}}\"?", "message": "Aquesta acció no es pot desfer." } }, @@ -254,13 +254,10 @@ "content_true": "Habilitat", "content_false": "Deshabilitat" }, - "update": { - "title": "Actualitza a la última versió" - }, "logout": { "button": "Tanca la sessió", "alert": { - "title": "Vol tancar la sessió?", + "title": "Vols tancar la sessió?", "message": "Després de tancar la sessió, hauràs de tornar a iniciar la sessió", "buttons": { "logout": "Tanca la sessió" diff --git a/src/i18n/cs/screens/tabs.json b/src/i18n/cs/screens/tabs.json index 0decff11..bf14709e 100644 --- a/src/i18n/cs/screens/tabs.json +++ b/src/i18n/cs/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "", "content_false": "" }, - "update": { - "title": "" - }, "logout": { "button": "", "alert": { diff --git a/src/i18n/de/components/contextMenu.json b/src/i18n/de/components/contextMenu.json index 5b322c1d..49dd4965 100644 --- a/src/i18n/de/components/contextMenu.json +++ b/src/i18n/de/components/contextMenu.json @@ -15,13 +15,13 @@ "action_false": "Nutzer blockieren", "action_true": "User entblocken", "alert": { - "title": "" + "title": "{{username}} wirklich blockieren?" } }, "reports": { "action": "Nutzer melden und blockieren", "alert": { - "title": "" + "title": "{{username}} wirklich blockieren und melden?" } } }, diff --git a/src/i18n/de/screens/tabs.json b/src/i18n/de/screens/tabs.json index a650e306..d797aa21 100644 --- a/src/i18n/de/screens/tabs.json +++ b/src/i18n/de/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Aktiviert", "content_false": "Deaktiviert" }, - "update": { - "title": "Auf neueste Version aktualisiert" - }, "logout": { "button": "Abmelden", "alert": { diff --git a/src/i18n/en/screens/tabs.json b/src/i18n/en/screens/tabs.json index d0f8d2e7..e8785d5d 100644 --- a/src/i18n/en/screens/tabs.json +++ b/src/i18n/en/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Enabled", "content_false": "Disabled" }, - "update": { - "title": "Update to latest version" - }, "logout": { "button": "Log out", "alert": { diff --git a/src/i18n/es/common.json b/src/i18n/es/common.json index bf65854e..702afe3a 100644 --- a/src/i18n/es/common.json +++ b/src/i18n/es/common.json @@ -1,6 +1,6 @@ { "buttons": { - "OK": "OK", + "OK": "De acuerdo", "apply": "Aplicar", "cancel": "Cancelar", "discard": "Descartar", diff --git a/src/i18n/es/components/contextMenu.json b/src/i18n/es/components/contextMenu.json index fea2de9f..c13a3384 100644 --- a/src/i18n/es/components/contextMenu.json +++ b/src/i18n/es/components/contextMenu.json @@ -15,13 +15,13 @@ "action_false": "Bloquear usuario", "action_true": "Desbloquear usuario", "alert": { - "title": "" + "title": "¿Quieres bloquear a @{{username}}?" } }, "reports": { "action": "Reportar y bloquear usuario", "alert": { - "title": "" + "title": "¿Quieres denunciar y bloquear a @{{username}}?" } } }, @@ -38,7 +38,7 @@ "block": { "action": "Bloquear instancia {{instance}}", "alert": { - "title": "¿Confirmar bloqueo de la instancia {{instance}}?", + "title": "¿Quieres bloquear la instancia {{instance}}?", "message": "Puedes silenciar o bloquear a un usuario.\n\nTras bloquear una instancia, todo su contenido junto con sus seguidores se eliminarán." } } @@ -59,14 +59,14 @@ "delete": { "action": "Eliminar toot", "alert": { - "title": "¿Confirmar eliminación?", + "title": "¿Quieres eliminar?", "message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas." } }, "deleteEdit": { "action": "Eliminar toot y volver a publicar", "alert": { - "title": "¿Confirmar eliminación y volver a publicar?", + "title": "¿Quieres eliminar y volver a publicar?", "message": "Todos los boosts y favoritos se eliminarán, incluidas todas las respuestas." } }, diff --git a/src/i18n/es/components/timeline.json b/src/i18n/es/components/timeline.json index 86b9ce1c..5df572d2 100644 --- a/src/i18n/es/components/timeline.json +++ b/src/i18n/es/components/timeline.json @@ -32,7 +32,7 @@ }, "update": "El impulso ha sido editado", "admin.sign_up": "{{name}} se unió a la instancia", - "admin.report": "" + "admin.report": "{{name}} reportó:" }, "actions": { "reply": { @@ -145,7 +145,7 @@ "refresh": "Actualizar" }, "count": { - "voters_one": "{{count}} usuarios ha votado", + "voters_one": "{{count}} usuario ha votado", "voters_other": "{{count}} usuarios han votado", "votes_one": "{{count}} voto", "votes_other": "{{count}} votos" diff --git a/src/i18n/es/screens/compose.json b/src/i18n/es/screens/compose.json index b46e89d3..cdb378c2 100644 --- a/src/i18n/es/screens/compose.json +++ b/src/i18n/es/screens/compose.json @@ -2,7 +2,7 @@ "heading": { "left": { "alert": { - "title": "¿Cancelar edición?", + "title": "¿Quieres cancelar la edición?", "buttons": { "save": "Guardar borrador", "delete": "Eliminar borrador" @@ -13,9 +13,9 @@ "button": { "default": "Toot", "conversation": "Mensaje privado", - "reply": "Respuesta al toot", - "deleteEdit": "Toot", - "edit": "Edita el toot", + "reply": "Responde", + "deleteEdit": "Vuelve a publicar", + "edit": "Edita", "share": "Toot" }, "alert": { @@ -39,7 +39,7 @@ "placeholder": "Mensaje de aviso de spoiler" }, "textInput": { - "placeholder": "Qué está pasando", + "placeholder": "¿Qué está pasando?", "keyboardImage": { "exceedMaximum": { "title": "Número máximo de adjuntos alcanzado", diff --git a/src/i18n/es/screens/tabs.json b/src/i18n/es/screens/tabs.json index 163cbb73..c05a7192 100644 --- a/src/i18n/es/screens/tabs.json +++ b/src/i18n/es/screens/tabs.json @@ -11,7 +11,7 @@ "segments": { "federated": "Federado", "local": "Local", - "trending": "En tendencia" + "trending": "Tendencia" } }, "notifications": { @@ -133,7 +133,7 @@ "listDelete": { "heading": "Borrar lista", "confirm": { - "title": "¿Eliminar lista \"{{list}}\"?", + "title": "¿Quieres eliminar la lista \"{{list}}\"?", "message": "Esta acción no se podrá deshacer." } }, @@ -254,9 +254,6 @@ "content_true": "Habilitado", "content_false": "Deshabilitado" }, - "update": { - "title": "Actualizar a la última versión" - }, "logout": { "button": "Cerrar sesión", "alert": { diff --git a/src/i18n/fr/screens/tabs.json b/src/i18n/fr/screens/tabs.json index 14504196..36805b63 100644 --- a/src/i18n/fr/screens/tabs.json +++ b/src/i18n/fr/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Activé", "content_false": "Désactivé" }, - "update": { - "title": "Mettre à jour vers la dernière version" - }, "logout": { "button": "Se déconnecter", "alert": { diff --git a/src/i18n/it/screens/tabs.json b/src/i18n/it/screens/tabs.json index d2b98efe..0fdb55af 100644 --- a/src/i18n/it/screens/tabs.json +++ b/src/i18n/it/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Abilitato", "content_false": "Disabilitato" }, - "update": { - "title": "Aggiorna all'ultima versione" - }, "logout": { "button": "Esci dall'account", "alert": { diff --git a/src/i18n/ja/screens/tabs.json b/src/i18n/ja/screens/tabs.json index c5aba370..c3bf23e1 100644 --- a/src/i18n/ja/screens/tabs.json +++ b/src/i18n/ja/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "有効", "content_false": "無効" }, - "update": { - "title": "最新バージョンへのアップデート" - }, "logout": { "button": "ログアウト", "alert": { diff --git a/src/i18n/ko/screens/tabs.json b/src/i18n/ko/screens/tabs.json index 292eef98..465eb18b 100644 --- a/src/i18n/ko/screens/tabs.json +++ b/src/i18n/ko/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "활성화됨", "content_false": "비활성화됨" }, - "update": { - "title": "최신 버전으로 업데이트" - }, "logout": { "button": "로그아웃", "alert": { diff --git a/src/i18n/nl/screens/tabs.json b/src/i18n/nl/screens/tabs.json index 218d6b52..c7a0d686 100644 --- a/src/i18n/nl/screens/tabs.json +++ b/src/i18n/nl/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Ingeschakeld", "content_false": "Uitgeschakeld" }, - "update": { - "title": "Bijwerken naar de laatste versie" - }, "logout": { "button": "Uitloggen", "alert": { diff --git a/src/i18n/pl/screens/tabs.json b/src/i18n/pl/screens/tabs.json index 5d450a79..427b3ef4 100644 --- a/src/i18n/pl/screens/tabs.json +++ b/src/i18n/pl/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "", "content_false": "" }, - "update": { - "title": "" - }, "logout": { "button": "", "alert": { diff --git a/src/i18n/pt_BR/screens/tabs.json b/src/i18n/pt_BR/screens/tabs.json index 6cead637..03f78724 100644 --- a/src/i18n/pt_BR/screens/tabs.json +++ b/src/i18n/pt_BR/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Habilitado", "content_false": "Desabilitado" }, - "update": { - "title": "Atualize para a versão mais recente" - }, "logout": { "button": "Sair", "alert": { diff --git a/src/i18n/ru/components/emojis.json b/src/i18n/ru/components/emojis.json index 9e26dfee..a955fa0d 100644 --- a/src/i18n/ru/components/emojis.json +++ b/src/i18n/ru/components/emojis.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "frequentUsed": "Часто используемые" +} \ No newline at end of file diff --git a/src/i18n/ru/components/instance.json b/src/i18n/ru/components/instance.json index 6de39ff0..d2a8d7e2 100644 --- a/src/i18n/ru/components/instance.json +++ b/src/i18n/ru/components/instance.json @@ -1,27 +1,27 @@ { "server": { "textInput": { - "placeholder": "" + "placeholder": "Домен инстанса" }, - "whitelisted": "", - "button": "", + "whitelisted": "Это сервер может быть из белого списка, из которого tooot не может получить данные перед входом.", + "button": "Войти", "information": { - "name": "", - "accounts": "", - "statuses": "", + "name": "Название", + "accounts": "Пользователей", + "statuses": "Постов", "domains": "" }, "disclaimer": { - "base": "" + "base": "Вход в систему использует системный браузер, информация о вашем аккаунте не будет видна приложению." }, "terms": { - "base": "" + "base": "Продолжая, вы соглашаетесь с <0>политикой конфиденциальности и <1>условиями использования." } }, "update": { "alert": { - "title": "", - "message": "" + "title": "Вход выполнен", + "message": "Вы можете войти в другую учетную запись, сохранив существующую учетную запись" } } } \ No newline at end of file diff --git a/src/i18n/ru/components/timeline.json b/src/i18n/ru/components/timeline.json index c18b9d6b..37042734 100644 --- a/src/i18n/ru/components/timeline.json +++ b/src/i18n/ru/components/timeline.json @@ -20,11 +20,11 @@ }, "shared": { "actioned": { - "pinned": "", + "pinned": "Закрепить", "favourite": "", - "status": "", - "follow": "", - "follow_request": "", + "status": "{{name}} только что запостил", + "follow": "{{name}} подписался (-лась) на вас", + "follow_request": "{{name}} хочет подписаться на вас", "poll": "", "reblog": { "default": "", @@ -92,7 +92,7 @@ "expandHint": "" }, "filtered": { - "reveal": "", + "reveal": "Все равно показать", "match_v1": "", "match_v2_one": "", "match_v2_other": "" diff --git a/src/i18n/ru/screens.json b/src/i18n/ru/screens.json index 35d8636e..52dc2b2c 100644 --- a/src/i18n/ru/screens.json +++ b/src/i18n/ru/screens.json @@ -1,17 +1,17 @@ { "screenshot": { - "title": "", - "message": "" + "title": "Защита Персональных Данных", + "message": "Пожалуйста, не раскрывайте личность других пользователей, таких как имя пользователя, аватар и т.д. Спасибо!" }, "localCorrupt": { - "message": "" + "message": "Время входа истекло, повторите вход" }, "pushError": { - "message": "", - "description": "" + "message": "Ошибка службы Push", + "description": "Пожалуйста, повторно включите push-уведомления в настройках" }, "shareError": { - "imageNotSupported": "", - "videoNotSupported": "" + "imageNotSupported": "Формат изображения {{type}} не поддерживается", + "videoNotSupported": "Формат видео {{type}} не поддерживается" } } \ No newline at end of file diff --git a/src/i18n/ru/screens/accountSelection.json b/src/i18n/ru/screens/accountSelection.json index b16795a6..f60d8950 100644 --- a/src/i18n/ru/screens/accountSelection.json +++ b/src/i18n/ru/screens/accountSelection.json @@ -1,6 +1,6 @@ { - "heading": "", + "heading": "Поделиться...", "content": { - "select_account": "" + "select_account": "Выберите аккаунт" } } \ No newline at end of file diff --git a/src/i18n/ru/screens/actions.json b/src/i18n/ru/screens/actions.json index 96dce3a8..7ac65578 100644 --- a/src/i18n/ru/screens/actions.json +++ b/src/i18n/ru/screens/actions.json @@ -1,7 +1,7 @@ { "content": { "altText": { - "heading": "" + "heading": "Альтернативный текст" } } } \ No newline at end of file diff --git a/src/i18n/ru/screens/announcements.json b/src/i18n/ru/screens/announcements.json index 95737a45..d1d25a02 100644 --- a/src/i18n/ru/screens/announcements.json +++ b/src/i18n/ru/screens/announcements.json @@ -1,10 +1,10 @@ { - "heading": "", + "heading": "Объявления", "content": { - "published": "", + "published": "Опубликовано <0 />", "button": { - "read": "", - "unread": "" + "read": "Прочитано", + "unread": "Отметить как прочитанное" } } } \ No newline at end of file diff --git a/src/i18n/ru/screens/compose.json b/src/i18n/ru/screens/compose.json index 2a078c68..6cd968cd 100644 --- a/src/i18n/ru/screens/compose.json +++ b/src/i18n/ru/screens/compose.json @@ -2,31 +2,31 @@ "heading": { "left": { "alert": { - "title": "", + "title": "Отменить изменения?", "buttons": { - "save": "", - "delete": "" + "save": "Сохранить черновик", + "delete": "Удалить черновик" } } }, "right": { "button": { - "default": "", - "conversation": "", - "reply": "", - "deleteEdit": "", - "edit": "", - "share": "" + "default": "Пост", + "conversation": "Пост DM", + "reply": "Ответить", + "deleteEdit": "Пост", + "edit": "Пост", + "share": "Пост" }, "alert": { "default": { - "title": "", - "button": "" + "title": "Не удалось опубликовать пост", + "button": "Попробовать снова" }, "removeReply": { - "title": "", + "title": "Пост для ответа не найдет", "description": "", - "confirm": "" + "confirm": "Удалить ссылку" } } } @@ -34,7 +34,7 @@ "content": { "root": { "header": { - "postingAs": "", + "postingAs": "Запостить как @{{acct}}@{{domain}}", "spoilerInput": { "placeholder": "" }, @@ -50,7 +50,7 @@ }, "footer": { "attachments": { - "sensitive": "", + "sensitive": "Пометить вложения как чувствительные", "remove": { "accessibilityLabel": "" }, @@ -58,32 +58,32 @@ "accessibilityLabel": "" }, "upload": { - "accessibilityLabel": "" + "accessibilityLabel": "Загрузить больше вложений" } }, "emojis": { - "accessibilityHint": "" + "accessibilityHint": "Нажмите, чтобы добавить эмодзи к посту" }, "poll": { "option": { "placeholder": { - "accessibilityLabel": "", - "single": "", - "multiple": "" + "accessibilityLabel": "Вариант опроса {{index}}", + "single": "Единичный выбор", + "multiple": "Множественный выбор" } }, "quantity": { "reduce": { - "accessibilityLabel": "", + "accessibilityLabel": "Уменьшить количество вариантов ответа до {{amount}}", "accessibilityHint": "" }, "increase": { - "accessibilityLabel": "", + "accessibilityLabel": "Уменьшить количество вариантов ответа до {{amount}}", "accessibilityHint": "" } }, "multiple": { - "heading": "", + "heading": "Тип выбора", "options": { "single": "", "multiple": "" @@ -92,25 +92,25 @@ "expiration": { "heading": "", "options": { - "300": "", - "1800": "", - "3600": "", - "21600": "", - "86400": "", - "259200": "", - "604800": "" + "300": "5 минут", + "1800": "30 минут", + "3600": "1 час", + "21600": "6 часов", + "86400": "1 день", + "259200": "3 дня", + "604800": "7 дней" } } } }, "actions": { "attachment": { - "accessibilityLabel": "", + "accessibilityLabel": "Загрузить вложение", "accessibilityHint": "", "failed": { "alert": { "title": "", - "button": "" + "button": "Попробовать снова" } } }, @@ -120,7 +120,7 @@ }, "visibility": { "accessibilityLabel": "", - "title": "", + "title": "Видимость поста", "options": { "public": "", "unlisted": "", diff --git a/src/i18n/ru/screens/tabs.json b/src/i18n/ru/screens/tabs.json index 0decff11..6b53529c 100644 --- a/src/i18n/ru/screens/tabs.json +++ b/src/i18n/ru/screens/tabs.json @@ -4,7 +4,7 @@ "name": "", "options": { "showBoosts": "", - "showReplies": "" + "showReplies": "Показать ответы" } }, "public": { @@ -26,8 +26,8 @@ }, "notifications": { "filters": { - "accessibilityLabel": "", - "accessibilityHint": "", + "accessibilityLabel": "Фильтр", + "accessibilityHint": "Фильтр отображаемых типов уведомлений", "title": "", "options": { "follow": "", @@ -254,9 +254,6 @@ "content_true": "", "content_false": "" }, - "update": { - "title": "" - }, "logout": { "button": "", "alert": { diff --git a/src/i18n/sv/screens/tabs.json b/src/i18n/sv/screens/tabs.json index 6febc9c1..96220df9 100644 --- a/src/i18n/sv/screens/tabs.json +++ b/src/i18n/sv/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Aktiverad", "content_false": "Inaktiverad" }, - "update": { - "title": "Uppdatera till senaste versionen" - }, "logout": { "button": "Logga ut", "alert": { diff --git a/src/i18n/uk/screens/tabs.json b/src/i18n/uk/screens/tabs.json index aafa1ffc..ac212dc0 100644 --- a/src/i18n/uk/screens/tabs.json +++ b/src/i18n/uk/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Увімкнено", "content_false": "Вимкнено" }, - "update": { - "title": "Оновити до останньої версії" - }, "logout": { "button": "Вийти", "alert": { diff --git a/src/i18n/vi/screens/tabs.json b/src/i18n/vi/screens/tabs.json index 16318833..2df2acbc 100644 --- a/src/i18n/vi/screens/tabs.json +++ b/src/i18n/vi/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "Bật", "content_false": "Tắt" }, - "update": { - "title": "Cập nhật phiên bản mới" - }, "logout": { "button": "Đăng xuất", "alert": { diff --git a/src/i18n/zh-Hans/screens/tabs.json b/src/i18n/zh-Hans/screens/tabs.json index 38d68b4c..11a1a337 100644 --- a/src/i18n/zh-Hans/screens/tabs.json +++ b/src/i18n/zh-Hans/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "已启用", "content_false": "未启用" }, - "update": { - "title": "更新至最新版本" - }, "logout": { "button": "退出当前账号", "alert": { diff --git a/src/i18n/zh-Hant/screens/tabs.json b/src/i18n/zh-Hant/screens/tabs.json index 0e66884b..b98de156 100644 --- a/src/i18n/zh-Hant/screens/tabs.json +++ b/src/i18n/zh-Hant/screens/tabs.json @@ -254,9 +254,6 @@ "content_true": "啟用", "content_false": "關閉" }, - "update": { - "title": "更新到最新版本" - }, "logout": { "button": "登出", "alert": { diff --git a/src/screens/Compose/DraftsList.tsx b/src/screens/Compose/DraftsList.tsx index cbb2a5b2..28947637 100644 --- a/src/screens/Compose/DraftsList.tsx +++ b/src/screens/Compose/DraftsList.tsx @@ -196,7 +196,7 @@ const ComposeDraftsList: React.FC item.timestamp.toString()} + keyExtractor={item => item.timestamp?.toString()} /> e && e.length && formData.append('poll[options][]', e) ) formData.append('poll[expires_in]', composeState.poll.expire) - formData.append('poll[multiple]', composeState.poll.multiple.toString()) + formData.append('poll[multiple]', composeState.poll.multiple?.toString()) } if (composeState.attachments.uploads.filter(upload => upload.remote && upload.remote.id).length) { - formData.append('sensitive', composeState.attachments.sensitive.toString()) + formData.append('sensitive', composeState.attachments.sensitive?.toString()) composeState.attachments.uploads.forEach(e => formData.append('media_ids[]', e.remote!.id!)) } diff --git a/src/screens/Tabs.tsx b/src/screens/Tabs.tsx index f0b2ce9f..9a5a84a2 100644 --- a/src/screens/Tabs.tsx +++ b/src/screens/Tabs.tsx @@ -2,13 +2,11 @@ import GracefullyImage from '@components/GracefullyImage' import haptics from '@components/haptics' import Icon from '@components/Icon' import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' -import { useAppDispatch } from '@root/store' import { RootStackScreenProps, ScreenTabsStackParamList } from '@utils/navigation/navigators' -import { getVersionUpdate, retrieveVersionLatest } from '@utils/slices/appSlice' import { getPreviousTab } from '@utils/slices/contextsSlice' import { getInstanceAccount, getInstanceActive } from '@utils/slices/instancesSlice' import { useTheme } from '@utils/styles/ThemeManager' -import React, { useCallback, useEffect, useMemo } from 'react' +import React, { useCallback, useMemo } from 'react' import { Platform } from 'react-native' import { useSelector } from 'react-redux' import TabLocal from './Tabs/Local' @@ -55,17 +53,6 @@ const ScreenTabs = React.memo( const previousTab = useSelector(getPreviousTab, () => true) - const versionUpdate = useSelector(getVersionUpdate) - const dispatch = useAppDispatch() - useEffect(() => { - dispatch(retrieveVersionLatest()) - }, []) - const tabMeOptions = useMemo(() => { - if (versionUpdate) { - return { tabBarBadge: 1 } - } - }, [versionUpdate]) - return ( - + ) }, diff --git a/src/screens/Tabs/Me/Push.tsx b/src/screens/Tabs/Me/Push.tsx index 2cdc4622..242b6156 100644 --- a/src/screens/Tabs/Me/Push.tsx +++ b/src/screens/Tabs/Me/Push.tsx @@ -8,12 +8,7 @@ import { isDevelopment } from '@utils/checkEnvironment' import { useAppsQuery } from '@utils/queryHooks/apps' import { useProfileQuery } from '@utils/queryHooks/profile' import { getExpoToken, retrieveExpoToken } from '@utils/slices/appSlice' -import { - PUSH_ADMIN, - PUSH_DEFAULT, - setChannels, - usePushFeatures -} from '@utils/slices/instances/push/utils' +import { PUSH_ADMIN, PUSH_DEFAULT, usePushFeatures } from '@utils/slices/instances/push/utils' import { updateInstancePush } from '@utils/slices/instances/updatePush' import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert' import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode' @@ -25,7 +20,7 @@ import * as Notifications from 'expo-notifications' import * as WebBrowser from 'expo-web-browser' import React, { useState, useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { AppState, Linking, Platform, ScrollView, View } from 'react-native' +import { AppState, Linking, ScrollView, View } from 'react-native' import { useSelector } from 'react-redux' const TabMePush: React.FC = () => { @@ -50,11 +45,7 @@ const TabMePush: React.FC = () => { setPushEnabled(permissions.granted) setPushCanAskAgain(permissions.canAskAgain) layoutAnimation() - - if (Platform.OS === 'android') { - await setChannels(instance) - dispatch(retrieveExpoToken()) - } + dispatch(retrieveExpoToken()) } if (appsQuery.data?.vapid_key) { diff --git a/src/screens/Tabs/Me/Root.tsx b/src/screens/Tabs/Me/Root.tsx index 5da98fe8..fa58a517 100644 --- a/src/screens/Tabs/Me/Root.tsx +++ b/src/screens/Tabs/Me/Root.tsx @@ -12,12 +12,8 @@ import accountReducer from '@screens/Tabs/Shared/Account/utils/reducer' import { useProfileQuery } from '@utils/queryHooks/profile' import { getInstanceActive } from '@utils/slices/instancesSlice' import React, { useReducer, useRef } from 'react' -import Animated, { - useAnimatedScrollHandler, - useSharedValue -} from 'react-native-reanimated' +import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated' import { useSelector } from 'react-redux' -import Update from './Root/Update' const TabMeRoot: React.FC = () => { const instanceActive = useSelector(getInstanceActive) @@ -29,10 +25,7 @@ const TabMeRoot: React.FC = () => { const scrollRef = useRef(null) useScrollToTop(scrollRef) - const [accountState, accountDispatch] = useReducer( - accountReducer, - accountInitialState - ) + const [accountState, accountDispatch] = useReducer(accountReducer, accountInitialState) const scrollY = useSharedValue(0) const onScroll = useAnimatedScrollHandler(event => { @@ -41,22 +34,15 @@ const TabMeRoot: React.FC = () => { return ( - {instanceActive !== -1 && data ? ( - - ) : null} + {instanceActive !== -1 && data ? : null} - {instanceActive !== -1 ? ( - - ) : ( - - )} + {instanceActive !== -1 ? : } {instanceActive !== -1 ? : null} - {instanceActive !== -1 ? : null} {instanceActive !== -1 ? : null} diff --git a/src/screens/Tabs/Me/Root/Collections.tsx b/src/screens/Tabs/Me/Root/Collections.tsx index 0988df92..e23a1baf 100644 --- a/src/screens/Tabs/Me/Root/Collections.tsx +++ b/src/screens/Tabs/Me/Root/Collections.tsx @@ -115,7 +115,11 @@ const Collections: React.FC = () => { iconFront={instancePush ? 'Bell' : 'BellOff'} iconBack='ChevronRight' title={t('me.stacks.push.name')} - content={t('me.root.push.content', { context: instancePush.global.toString() })} + content={ + typeof instancePush.global === 'boolean' + ? t('me.root.push.content', { context: instancePush.global.toString() }) + : undefined + } onPress={() => navigation.navigate('Tab-Me-Push')} /> diff --git a/src/screens/Tabs/Me/Root/Update.tsx b/src/screens/Tabs/Me/Root/Update.tsx deleted file mode 100644 index e1d9f6d2..00000000 --- a/src/screens/Tabs/Me/Root/Update.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { MenuContainer, MenuRow } from '@components/Menu' -import { getVersionUpdate } from '@utils/slices/appSlice' -import React from 'react' -import { useTranslation } from 'react-i18next' -import { Linking, Platform } from 'react-native' -import { useSelector } from 'react-redux' - -const Update: React.FC = () => { - const { t } = useTranslation('screenTabs') - - const versionUpdate = useSelector(getVersionUpdate) - - return versionUpdate ? ( - - { - if (Platform.OS === 'ios') { - Linking.openURL('itms-appss://itunes.apple.com/app/id1549772269') - } else { - Linking.openURL('https://tooot.app') - } - }} - /> - - ) : null -} - -export default Update diff --git a/src/screens/Tabs/Me/SettingsLanguage.tsx b/src/screens/Tabs/Me/SettingsLanguage.tsx index ee87aa16..41cfa98f 100644 --- a/src/screens/Tabs/Me/SettingsLanguage.tsx +++ b/src/screens/Tabs/Me/SettingsLanguage.tsx @@ -27,7 +27,7 @@ const TabMeSettingsLanguage: React.FC setChannels(instance, true)) } navigation.pop(1) diff --git a/src/utils/slices/appSlice.ts b/src/utils/slices/appSlice.ts index d65c0d4b..9ce39037 100644 --- a/src/utils/slices/appSlice.ts +++ b/src/utils/slices/appSlice.ts @@ -1,38 +1,43 @@ -import apiTooot from '@api/tooot' import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' import { RootState } from '@root/store' import { isDevelopment } from '@utils/checkEnvironment' -import Constants from 'expo-constants' import * as Notifications from 'expo-notifications' +import { Platform } from 'react-native' +import { setChannels } from './instances/push/utils' +import { getInstance } from './instancesSlice' -export const retrieveExpoToken = createAsyncThunk('app/expoToken', async (): Promise => { - if (isDevelopment) { - return 'ExponentPushToken[DEVELOPMENT_1]' - } +export const retrieveExpoToken = createAsyncThunk( + 'app/expoToken', + async (_, { getState }): Promise => { + const instance = getInstance(getState() as RootState) + const expoToken = getExpoToken(getState() as RootState) - const res = await Notifications.getExpoPushTokenAsync({ - experienceId: '@xmflsct/tooot', - applicationId: 'com.xmflsct.app.tooot' - }) - return res.data -}) + if (Platform.OS === 'android') { + await setChannels(instance) + } -export const retrieveVersionLatest = createAsyncThunk( - 'app/versionUpdate', - async (): Promise => { - const res = await apiTooot<{ latest: string }>({ method: 'get', url: 'version.json' }) - return res.body.latest + if (expoToken?.length) { + return expoToken + } else { + if (isDevelopment) { + return 'ExponentPushToken[DEVELOPMENT_1]' + } + + const res = await Notifications.getExpoPushTokenAsync({ + experienceId: '@xmflsct/tooot', + applicationId: 'com.xmflsct.app.tooot' + }) + return res.data + } } ) export type AppState = { expoToken?: string - versionUpdate: boolean } export const appInitialState: AppState = { - expoToken: undefined, - versionUpdate: false + expoToken: undefined } const appSlice = createSlice({ @@ -40,22 +45,14 @@ const appSlice = createSlice({ initialState: appInitialState, reducers: {}, extraReducers: builder => { - builder - .addCase(retrieveExpoToken.fulfilled, (state, action) => { - if (action.payload) { - state.expoToken = action.payload - } - }) - .addCase(retrieveVersionLatest.fulfilled, (state, action) => { - if (action.payload && Constants.expoConfig?.version) { - state.versionUpdate = - parseFloat(action.payload) > parseFloat(Constants.expoConfig.version) - } - }) + builder.addCase(retrieveExpoToken.fulfilled, (state, action) => { + if (action.payload) { + state.expoToken = action.payload + } + }) } }) export const getExpoToken = (state: RootState) => state.app.expoToken -export const getVersionUpdate = (state: RootState) => state.app.versionUpdate export default appSlice.reducer diff --git a/src/utils/slices/instances/push/register.ts b/src/utils/slices/instances/push/register.ts index 93a339a8..7ef97bcf 100644 --- a/src/utils/slices/instances/push/register.ts +++ b/src/utils/slices/instances/push/register.ts @@ -28,7 +28,7 @@ const subscribe = async ({ }) => { return apiTooot({ method: 'post', - url: `/push/subscribe/${expoToken}/${instanceUrl}/${accountId}`, + url: `push/subscribe/${expoToken}/${instanceUrl}/${accountId}`, body: { accountFull, serverKey, auth } }) } @@ -97,7 +97,7 @@ const pushRegister = async ( }) if (Platform.OS === 'android') { - setChannels(instance) + setChannels(instance, true) } return Promise.resolve(auth) diff --git a/src/utils/slices/instances/push/unregister.ts b/src/utils/slices/instances/push/unregister.ts index 30dc5ac1..5301721e 100644 --- a/src/utils/slices/instances/push/unregister.ts +++ b/src/utils/slices/instances/push/unregister.ts @@ -21,7 +21,7 @@ const pushUnregister = async (state: RootState, expoToken: string) => { await apiTooot<{ endpoint: string; publicKey: string; auth: string }>({ method: 'delete', - url: `/push/unsubscribe/${expoToken}/${instance.url}/${instance.account.id}` + url: `push/unsubscribe/${expoToken}/${instance.url}/${instance.account.id}` }) if (Platform.OS === 'android') { diff --git a/src/utils/slices/instances/push/utils.ts b/src/utils/slices/instances/push/utils.ts index 443fc553..67b4fe28 100644 --- a/src/utils/slices/instances/push/utils.ts +++ b/src/utils/slices/instances/push/utils.ts @@ -62,7 +62,7 @@ export const PUSH_ADMIN = ( } }) as { type: 'admin.sign_up' | 'admin.report'; permission: number }[] -export const setChannels = async (instance: InstanceLatest) => { +export const setChannels = async (instance: InstanceLatest, reset: boolean | undefined = false) => { const account = `@${instance.account.acct}@${instance.uri}` const deleteChannel = async (type: string) => @@ -82,6 +82,9 @@ export const setChannels = async (instance: InstanceLatest) => { const profileQuery = await queryClient.fetchQuery(queryKey, queryFunctionProfile) const channelGroup = await Notifications.getNotificationChannelGroupAsync(account) + if (channelGroup && !reset) { + return + } if (!channelGroup) { await Notifications.setNotificationChannelGroupAsync(account, { name: account }) } diff --git a/src/utils/slices/instances/updatePushDecode.ts b/src/utils/slices/instances/updatePushDecode.ts index 2e8e21a7..7f68f25a 100644 --- a/src/utils/slices/instances/updatePushDecode.ts +++ b/src/utils/slices/instances/updatePushDecode.ts @@ -25,14 +25,14 @@ export const updateInstancePushDecode = createAsyncThunk( await apiTooot({ method: 'put', - url: `/push/update-decode/${expoToken}/${instance.url}/${instance.account.id}`, + url: `push/update-decode/${expoToken}/${instance.url}/${instance.account.id}`, body: { auth: !disable ? null : instance.push.keys.auth } }) if (Platform.OS === 'android') { - setChannels(instance) + setChannels(instance, true) } return Promise.resolve({ disable })