diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 66d35884..c4d1f624 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -108,29 +108,6 @@ private_lane :build_android do end end -desc "Build Android apk" -private_lane :build_android_apk do - sh("echo #{ENV["ANDROID_KEYSTORE"]} | base64 -d | tee #{File.expand_path('..', Dir.pwd)}/android/tooot.jks >/dev/null", log: false) - - prepare_playstore_android - - build_android_app( - task: 'assemble', - build_type: 'release', - project_dir: "./android", - print_command: true, - print_command_output: true, - properties: { - "android.injected.signing.store.file" => "#{File.expand_path('..', Dir.pwd)}/android/tooot.jks", - "android.injected.signing.store.password" => ENV["ANDROID_KEYSTORE_PASSWORD"], - "android.injected.signing.key.alias" => ENV["ANDROID_KEYSTORE_ALIAS"], - "android.injected.signing.key.password" => ENV["ANDROID_KEYSTORE_KEY_PASSWORD"], - } - ) - - sh "mv #{lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]} #{File.expand_path('..', Dir.pwd)}/tooot-#{GITHUB_RELEASE}.apk" -end - lane :ios do cocoapods(clean_install: true, podfile: "./ios/Podfile") build_ios @@ -144,15 +121,13 @@ end lane :release do if ENVIRONMENT == 'release' - build_android_apk set_github_release( repository_name: GITHUB_REPO, name: GITHUB_RELEASE, tag_name: GITHUB_RELEASE, description: "No changelog provided", commitish: git_branch, - is_prerelease: false, - upload_assets: ["#{File.expand_path('..', Dir.pwd)}/tooot-#{GITHUB_RELEASE}.apk"] + is_prerelease: false ) end rocket diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2d853f48..c57eaa54 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -23,7 +23,7 @@ PODS: - ExpoModulesCore - EXNotifications (0.17.0): - ExpoModulesCore - - Expo (47.0.6): + - Expo (47.0.7): - ExpoModulesCore - ExpoCrypto (12.0.0): - ExpoModulesCore @@ -419,7 +419,7 @@ PODS: - React-Core - react-native-netinfo (9.3.6): - React-Core - - react-native-pager-view (6.1.0): + - react-native-pager-view (6.1.1): - React-Core - react-native-paste-input (0.5.1): - React-Core @@ -830,7 +830,7 @@ SPEC CHECKSUMS: EXFirebaseCore: d0d88cb904e893af07f809ab08c0892489bc6956 EXFont: 319606bfe48c33b5b5063fb0994afdc496befe80 EXNotifications: babce2a87b7922051354fcfe7a74dd279b7e272a - Expo: a581e979539bae3e49cd02d9dee2432947383d3b + Expo: a37d568e9ae87645b74ed597dd0f0fd89e2daf2d ExpoCrypto: 51e7662c7f5bfeab25b7909b8a5d545ec15d4877 ExpoHaptics: 5a56d30a87ea213dd00b09566dc4b441a4dff97f ExpoKeepAwake: 69b59d0a8d2b24de9f82759c39b3821fec030318 @@ -884,7 +884,7 @@ SPEC CHECKSUMS: react-native-language-detection: 0e43195ad014974f1b7a31b64820eff34a243f2d react-native-live-text-image-view: 483bacfdba464162b8cf176bba555364f18b584c react-native-netinfo: f80db8cac2151405633324cb645c60af098ee461 - react-native-pager-view: 7abf89f9834d9a4021b2fb6a5ef2abff570b46fb + react-native-pager-view: 3c66c4e2f3ab423643d07b2c7041f8ac48395f72 react-native-paste-input: 183ad7dc224e192719616f4258dde5b548627d08 react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a react-native-segmented-control: 65df6cd0619b780b3843d574a72d4c7cec396097 diff --git a/package.json b/package.json index 5179551f..0d3c4101 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tooot", - "version": "4.6.2", + "version": "4.6.3", "description": "tooot for Mastodon", "author": "xmflsct ", "license": "GPL-3.0-or-later", @@ -33,15 +33,15 @@ "@react-native-community/blur": "^4.3.0", "@react-native-community/netinfo": "9.3.6", "@react-native-community/segmented-control": "^2.2.2", - "@react-navigation/bottom-tabs": "^6.4.0", - "@react-navigation/native": "^6.0.13", - "@react-navigation/native-stack": "^6.9.1", - "@react-navigation/stack": "^6.3.4", + "@react-navigation/bottom-tabs": "^6.4.1", + "@react-navigation/native": "^6.0.14", + "@react-navigation/native-stack": "^6.9.2", + "@react-navigation/stack": "^6.3.5", "@reduxjs/toolkit": "^1.9.0", "@sentry/react-native": "4.8.0", "@sharcoux/slider": "^6.0.3", "axios": "^0.27.2", - "expo": "^47.0.6", + "expo": "^47.0.7", "expo-auth-session": "^3.7.2", "expo-av": "^13.0.1", "expo-constants": "^14.0.2", @@ -80,7 +80,7 @@ "react-native-image-picker": "^4.10.1", "react-native-language-detection": "^0.1.0", "react-native-live-text-image-view": "^0.4.0", - "react-native-pager-view": "^6.1.0", + "react-native-pager-view": "^6.1.1", "react-native-reanimated": "^2.13.0", "react-native-reanimated-zoom": "^0.3.3", "react-native-safe-area-context": "^4.4.1", diff --git a/src/components/ContextMenu/account.ts b/src/components/ContextMenu/account.ts index 4fe00925..cd5e4793 100644 --- a/src/components/ContextMenu/account.ts +++ b/src/components/ContextMenu/account.ts @@ -131,6 +131,9 @@ const contextMenuAccount = ({ actions, type, queryKey, rootQueryKey, id: account } return (index: number) => { + if (typeof index !== 'number' || !actions[index]) { + return // For Android + } if (actions[index].id === 'account-mute') { analytics('timeline_shared_headeractions_account_mute_press', { page: queryKey && queryKey[1].page diff --git a/src/components/ContextMenu/instance.ts b/src/components/ContextMenu/instance.ts index 862a09e6..92df562e 100644 --- a/src/components/ContextMenu/instance.ts +++ b/src/components/ContextMenu/instance.ts @@ -1,9 +1,6 @@ import analytics from '@components/analytics' import { displayMessage } from '@components/Message' -import { - QueryKeyTimeline, - useTimelineMutation -} from '@utils/queryHooks/timeline' +import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' import { getInstanceUrl } from '@utils/slices/instancesSlice' import { useTheme } from '@utils/styles/ThemeManager' import { useTranslation } from 'react-i18next' @@ -19,12 +16,7 @@ export interface Props { rootQueryKey?: QueryKeyTimeline } -const contextMenuInstance = ({ - actions, - status, - queryKey, - rootQueryKey -}: Props) => { +const contextMenuInstance = ({ actions, status, queryKey, rootQueryKey }: Props) => { const { t } = useTranslation('componentContextMenu') const { theme } = useTheme() @@ -72,10 +64,12 @@ const contextMenuInstance = ({ } return (index: number) => { + if (typeof index !== 'number' || !actions[index]) { + return // For Android + } if ( actions[index].id === 'instance-block' || - (actions[index].id === 'instance' && - actions[index].actions?.[0].id === 'instance-block') + (actions[index].id === 'instance' && actions[index].actions?.[0].id === 'instance-block') ) { analytics('timeline_shared_headeractions_domain_block_press', { page: queryKey[1].page diff --git a/src/components/ContextMenu/share.ts b/src/components/ContextMenu/share.ts index 5d7052c6..03401876 100644 --- a/src/components/ContextMenu/share.ts +++ b/src/components/ContextMenu/share.ts @@ -25,7 +25,8 @@ const contextMenuShare = ({ copiableContent, actions, type, url }: Props) => { title: t(`share.${type}.action`), systemIcon: 'square.and.arrow.up' }) - Platform.OS !== 'android' && type === 'status' && + Platform.OS !== 'android' && + type === 'status' && actions.push({ id: 'copy', title: t(`copy.action`), @@ -34,6 +35,9 @@ const contextMenuShare = ({ copiableContent, actions, type, url }: Props) => { }) return (index: number) => { + if (typeof index !== 'number' || !actions[index]) { + return // For Android + } if (actions[index].id === 'copy') { analytics('timeline_shared_headeractions_copy_press') Clipboard.setString(copiableContent?.current.content || '') diff --git a/src/components/ContextMenu/status.ts b/src/components/ContextMenu/status.ts index 5caac672..8e98f7fc 100644 --- a/src/components/ContextMenu/status.ts +++ b/src/components/ContextMenu/status.ts @@ -9,10 +9,7 @@ import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' -import { - checkInstanceFeature, - getInstanceAccount -} from '@utils/slices/instancesSlice' +import { checkInstanceFeature, getInstanceAccount } from '@utils/slices/instancesSlice' import { useTheme } from '@utils/styles/ThemeManager' import { useTranslation } from 'react-i18next' import { Alert } from 'react-native' @@ -27,16 +24,8 @@ export interface Props { rootQueryKey?: QueryKeyTimeline } -const contextMenuStatus = ({ - actions, - status, - queryKey, - rootQueryKey -}: Props) => { - const navigation = - useNavigation< - NativeStackNavigationProp - >() +const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) => { + const navigation = useNavigation>() const { theme } = useTheme() const { t } = useTranslation('componentContextMenu') @@ -44,8 +33,7 @@ const contextMenuStatus = ({ const mutation = useTimelineMutation({ onMutate: true, onError: (err: any, params, oldData) => { - const theFunction = (params as MutationVarsTimelineUpdateStatusProperty) - .payload + const theFunction = (params as MutationVarsTimelineUpdateStatusProperty).payload ? (params as MutationVarsTimelineUpdateStatusProperty).payload.property : 'delete' displayMessage({ @@ -59,17 +47,14 @@ const contextMenuStatus = ({ err.data && err.data.error && typeof err.data.error === 'string' && { - description: err.data.error - }) + description: err.data.error + }) }) queryClient.setQueryData(queryKey, oldData) } }) - const instanceAccount = useSelector( - getInstanceAccount, - (prev, next) => prev.id === next.id - ) + const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev.id === next.id) const ownAccount = instanceAccount?.id === status?.account?.id if (ownAccount) { @@ -118,83 +103,75 @@ const contextMenuStatus = ({ } return async (index: number) => { + if (typeof index !== 'number' || !actions[index]) { + return // For Android + } if (actions[index].id === 'status-delete') { analytics('timeline_shared_headeractions_status_delete_press', { page: queryKey && queryKey[1].page }) - Alert.alert( - t('status.delete.alert.title'), - t('status.delete.alert.message'), - [ - { - text: t('status.delete.alert.buttons.confirm'), - style: 'destructive', - onPress: async () => { - analytics('timeline_shared_headeractions_status_delete_confirm', { - page: queryKey && queryKey[1].page - }) - mutation.mutate({ - type: 'deleteItem', - source: 'statuses', - queryKey, - rootQueryKey, - id: status.id - }) - } - }, - { - text: t('common:buttons.cancel') + Alert.alert(t('status.delete.alert.title'), t('status.delete.alert.message'), [ + { + text: t('status.delete.alert.buttons.confirm'), + style: 'destructive', + onPress: async () => { + analytics('timeline_shared_headeractions_status_delete_confirm', { + page: queryKey && queryKey[1].page + }) + mutation.mutate({ + type: 'deleteItem', + source: 'statuses', + queryKey, + rootQueryKey, + id: status.id + }) } - ] - ) + }, + { + text: t('common:buttons.cancel') + } + ]) } if (actions[index].id === 'status-delete-edit') { analytics('timeline_shared_headeractions_status_deleteedit_press', { page: queryKey && queryKey[1].page }) - Alert.alert( - t('status.deleteEdit.alert.title'), - t('status.deleteEdit.alert.message'), - [ - { - text: t('status.deleteEdit.alert.buttons.confirm'), - style: 'destructive', - onPress: async () => { - analytics( - 'timeline_shared_headeractions_status_deleteedit_confirm', - { - page: queryKey && queryKey[1].page - } - ) - let replyToStatus: Mastodon.Status | undefined = undefined - if (status.in_reply_to_id) { - replyToStatus = await apiInstance({ - method: 'get', - url: `statuses/${status.in_reply_to_id}` - }).then(res => res.body) - } - mutation - .mutateAsync({ - type: 'deleteItem', - source: 'statuses', - queryKey, - id: status.id - }) - .then(res => { - navigation.navigate('Screen-Compose', { - type: 'deleteEdit', - incomingStatus: res.body as Mastodon.Status, - ...(replyToStatus && { replyToStatus }), - queryKey - }) - }) + Alert.alert(t('status.deleteEdit.alert.title'), t('status.deleteEdit.alert.message'), [ + { + text: t('status.deleteEdit.alert.buttons.confirm'), + style: 'destructive', + onPress: async () => { + analytics('timeline_shared_headeractions_status_deleteedit_confirm', { + page: queryKey && queryKey[1].page + }) + let replyToStatus: Mastodon.Status | undefined = undefined + if (status.in_reply_to_id) { + replyToStatus = await apiInstance({ + method: 'get', + url: `statuses/${status.in_reply_to_id}` + }).then(res => res.body) } - }, - { - text: t('common:buttons.cancel') + mutation + .mutateAsync({ + type: 'deleteItem', + source: 'statuses', + queryKey, + id: status.id + }) + .then(res => { + navigation.navigate('Screen-Compose', { + type: 'deleteEdit', + incomingStatus: res.body as Mastodon.Status, + ...(replyToStatus && { replyToStatus }), + queryKey + }) + }) } - ] - ) + }, + { + text: t('common:buttons.cancel') + } + ]) } if (actions[index].id === 'status-mute') { analytics('timeline_shared_headeractions_status_mute_press', { diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index 18d88803..e861298a 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -157,15 +157,6 @@ const TimelineDefault: React.FC = ({ return disableOnPress ? ( {main()} - ) : Platform.OS === 'android' ? ( - {}} - > - {main()} - ) : ( = ({ }, []) const _handleAppStateChange = async (nextAppState: AppStateStatus) => { if (appState.current.match(/active/) && nextAppState.match(/inactive/)) { - await videoPlayer.current?.stopAsync() + // await videoPlayer.current?.stopAsync() } else if (gifv && appState.current.match(/background/) && nextAppState.match(/active/)) { await videoPlayer.current?.setIsMutedAsync(true) await videoPlayer.current?.playAsync() diff --git a/src/components/Timeline/Shared/HeaderDefault.android.tsx b/src/components/Timeline/Shared/HeaderDefault.android.tsx index 98b7a99c..9869f450 100644 --- a/src/components/Timeline/Shared/HeaderDefault.android.tsx +++ b/src/components/Timeline/Shared/HeaderDefault.android.tsx @@ -3,13 +3,14 @@ import contextMenuInstance from '@components/ContextMenu/instance' import contextMenuShare from '@components/ContextMenu/share' import contextMenuStatus from '@components/ContextMenu/status' import Icon from '@components/Icon' +import { useActionSheet } from '@expo/react-native-action-sheet' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React from 'react' import { useTranslation } from 'react-i18next' import { Pressable, View } from 'react-native' -import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view' +import { ContextMenuAction } from 'react-native-context-menu-view' import HeaderSharedAccount from './HeaderShared/Account' import HeaderSharedApplication from './HeaderShared/Application' import HeaderSharedCreated from './HeaderShared/Created' @@ -55,6 +56,8 @@ const TimelineHeaderDefault = ({ queryKey, status, highlighted }: Props) => { queryKey }) + const { showActionSheetWithOptions } = useActionSheet() + return ( @@ -82,25 +85,26 @@ const TimelineHeaderDefault = ({ queryKey, status, highlighted }: Props) => { null} - > - { - for (const on of [shareOnPress, statusOnPress, accountOnPress, instanceOnPress]) { - on && on(index) + onPress={() => + showActionSheetWithOptions( + { + options: actions.map(action => action.title), + cancelButtonIndex: 999, + destructiveButtonIndex: actions + .map((action, index) => (action.destructive ? index : 999)) + .filter(num => num !== 999) + }, + index => { + if (index !== undefined) { + for (const on of [shareOnPress, statusOnPress, accountOnPress, instanceOnPress]) { + on && on(index) + } + } } - }} - children={ - - } - /> + ) + } + > + ) : null} diff --git a/src/components/Timeline/Shared/HeaderNotification.android.tsx b/src/components/Timeline/Shared/HeaderNotification.android.tsx index dc94a54d..de62abd0 100644 --- a/src/components/Timeline/Shared/HeaderNotification.android.tsx +++ b/src/components/Timeline/Shared/HeaderNotification.android.tsx @@ -4,12 +4,13 @@ import contextMenuShare from '@components/ContextMenu/share' import contextMenuStatus from '@components/ContextMenu/status' import Icon from '@components/Icon' import { RelationshipIncoming, RelationshipOutgoing } from '@components/Relationship' +import { useActionSheet } from '@expo/react-native-action-sheet' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { useMemo } from 'react' import { Pressable, View } from 'react-native' -import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view' +import { ContextMenuAction } from 'react-native-context-menu-view' import HeaderSharedAccount from './HeaderShared/Account' import HeaderSharedApplication from './HeaderShared/Application' import HeaderSharedCreated from './HeaderShared/Created' @@ -57,6 +58,8 @@ const TimelineHeaderNotification = ({ queryKey, notification }: Props) => { queryKey }) + const { showActionSheetWithOptions } = useActionSheet() + const actions = useMemo(() => { switch (notification.type) { case 'follow': @@ -68,29 +71,34 @@ const TimelineHeaderNotification = ({ queryKey, notification }: Props) => { return ( null} - children={ - { - for (const on of [ - shareOnPress, - statusOnPress, - accountOnPress, - instanceOnPress - ]) { - on && on(index) + onPress={() => + showActionSheetWithOptions( + { + options: contextMenuActions.map(action => action.title), + cancelButtonIndex: 999, + destructiveButtonIndex: contextMenuActions + .map((action, index) => (action.destructive ? index : 999)) + .filter(num => num !== 999) + }, + index => { + if (index !== undefined) { + for (const on of [ + shareOnPress, + statusOnPress, + accountOnPress, + instanceOnPress + ]) { + on && on(index) + } } - }} - children={ - } + ) + } + children={ + } /> diff --git a/src/components/Timeline/Shared/HeaderShared/Application.tsx b/src/components/Timeline/Shared/HeaderShared/Application.tsx index cbb18b64..b61043a1 100644 --- a/src/components/Timeline/Shared/HeaderShared/Application.tsx +++ b/src/components/Timeline/Shared/HeaderShared/Application.tsx @@ -26,7 +26,6 @@ const HeaderSharedApplication = React.memo( application.website && (await openLink(application.website)) }} style={{ - flex: 1, marginLeft: StyleConstants.Spacing.S, color: colors.secondary }} diff --git a/src/i18n/de/components/contextMenu.json b/src/i18n/de/components/contextMenu.json index 47b74923..10371cc1 100644 --- a/src/i18n/de/components/contextMenu.json +++ b/src/i18n/de/components/contextMenu.json @@ -36,7 +36,7 @@ "action": "Tröt teilen" }, "account": { - "action": "User verlinken" + "action": "Link zum Profil teilen" } }, "status": { @@ -55,17 +55,17 @@ } }, "deleteEdit": { - "action": "Tröt und Boost entfernen", + "action": "Tröt neu entwerfen", "alert": { - "title": "Löschen und Boost bestätigen?", - "message": "Alle Boosts und Favoriten inklusive der Antworten werden gelöscht.", + "title": "Beitrag wirklich entfernen?", + "message": "Alle Boosts und Likes inklusive der Antworten werden gelöscht.", "buttons": { "confirm": "Bestätigen" } } }, "mute": { - "action_false": "Diesen Tröt sowie die Antworten stummschalten", + "action_false": "Diskussion stummschalten", "action_true": "Tröt und Antworten nicht mehr stummschalten" }, "pin": { diff --git a/src/i18n/de/components/instance.json b/src/i18n/de/components/instance.json index 56aadcae..e2a8925e 100644 --- a/src/i18n/de/components/instance.json +++ b/src/i18n/de/components/instance.json @@ -11,7 +11,7 @@ "domains": "Domains" }, "disclaimer": { - "base": "Der Login erfolgt über den Browser, so dass Ihre Kontoinformationen für die Toot-App nicht sichtbar sind." + "base": "Der Login erfolgt über den Browser, sodass deine Kontoinformationen für die App nicht sichtbar sind." }, "terms": { "base": "Mit dem Login stimmst du der <0>Datenschutzrichtlinie und den <1>Nutzungsbedingungen zu." diff --git a/src/i18n/de/components/timeline.json b/src/i18n/de/components/timeline.json index 657a65d2..3bd00501 100644 --- a/src/i18n/de/components/timeline.json +++ b/src/i18n/de/components/timeline.json @@ -47,7 +47,7 @@ }, "favourited": { "accessibilityLabel": "Favorit", - "function": "Sternen" + "function": "Liken" }, "bookmarked": { "accessibilityLabel": "Lesezeichen hinzufügen", @@ -67,7 +67,7 @@ }, "history": { "accessibilityLabel": "Dieser Tröt wurde {{count}} mal bearbeitet", - "accessibilityHint": "Für den vollständigen Verlauf auswählen", + "accessibilityHint": "Bearbeitungsverlauf anzeigen", "text_one": "{{count}} Bearbeitung", "text_other": "{{count}} mal bearbeitet" } diff --git a/src/i18n/de/screens/actions.json b/src/i18n/de/screens/actions.json index b753d0e6..5cd3ac4c 100644 --- a/src/i18n/de/screens/actions.json +++ b/src/i18n/de/screens/actions.json @@ -12,7 +12,7 @@ "reblog": "$t(screenTabs:me.push.reblog.heading)", "mention": "$t(screenTabs:me.push.mention.heading)", "poll": "$t(screenTabs:me.push.poll.heading)", - "status": "Toot eines abonnierten Nutzers", + "status": "Tröt eines abonnierten Nutzers", "update": "Boost wurde bearbeitet" } } diff --git a/src/i18n/de/screens/tabs.json b/src/i18n/de/screens/tabs.json index c2808f00..4485a8e0 100644 --- a/src/i18n/de/screens/tabs.json +++ b/src/i18n/de/screens/tabs.json @@ -125,7 +125,7 @@ "options": { "public": "Öffentlich", "unlisted": "Ungelistet", - "private": "Nur für Folgende", + "private": "Nur für Follower", "cancel": "$t(common:buttons.cancel)" } }, diff --git a/src/i18n/ja/screens/tabs.json b/src/i18n/ja/screens/tabs.json index 8cdb69e9..aa1ac6ca 100644 --- a/src/i18n/ja/screens/tabs.json +++ b/src/i18n/ja/screens/tabs.json @@ -312,7 +312,7 @@ }, "hashtag": { "follow": "フォロー", - "unfollow": "フォローをやめる" + "unfollow": "フォロー解除" }, "history": { "name": "編集履歴" diff --git a/src/i18n/sv/screens/compose.json b/src/i18n/sv/screens/compose.json index 39869ede..daa26e73 100644 --- a/src/i18n/sv/screens/compose.json +++ b/src/i18n/sv/screens/compose.json @@ -39,7 +39,7 @@ "header": { "postingAs": "Skriver som @{{acct}}@{{domain}}", "spoilerInput": { - "placeholder": "Spoilervarningsmeddelande" + "placeholder": "Innehållsvarning" }, "textInput": { "placeholder": "Vad tänker du på", @@ -71,8 +71,8 @@ "option": { "placeholder": { "accessibilityLabel": "Omröstningsalternativ {{index}}", - "single": "Enval", - "multiple": "Flera val" + "single": "Alternativ", + "multiple": "Alternativ" } }, "quantity": { @@ -89,7 +89,7 @@ "heading": "Typ av val", "options": { "single": "Enval", - "multiple": "Flera val", + "multiple": "Flerval", "cancel": "$t(common:buttons.cancel)" } }, @@ -135,7 +135,7 @@ } }, "spoiler": { - "accessibilityLabel": "Spoiler" + "accessibilityLabel": "Innehållsvarning" }, "emoji": { "accessibilityLabel": "Lägg till emoji", diff --git a/src/i18n/sv/screens/tabs.json b/src/i18n/sv/screens/tabs.json index 32626622..e10287fd 100644 --- a/src/i18n/sv/screens/tabs.json +++ b/src/i18n/sv/screens/tabs.json @@ -78,7 +78,7 @@ } }, "fontSize": { - "demo": "

Det här är ett demoinlägg😊. Du kan välja mellan flera alternativ nedan.

Denna inställning påverkar endast huvudinnehållet i inlägg men inte andra teckenstorlekar.

", + "demo": "

Det här är ett demoinlägg 😊. Du kan välja mellan flera alternativ ovan.

Denna inställning påverkar endast huvudinnehållet i inlägg men inte andra teckenstorlekar.

", "sizes": { "S": "S", "M": "M - Standard", @@ -252,7 +252,7 @@ } }, "browser": { - "heading": "Öppnar länk", + "heading": "Öppna länk", "options": { "internal": "Inuti appen", "external": "Använd systemets webbläsare", @@ -320,7 +320,7 @@ "search": { "header": { "prefix": "Söker", - "placeholder": "för..." + "placeholder": "efter..." }, "empty": { "general": "Ange sökord för att söka efter $t(screenTabs:shared.search.sections.accounts), $t(screenTabs:shared.search.sections.hashtags) eller $t(screenTabs:shared.search.sections.statuses)", diff --git a/src/store.ts b/src/store.ts index 06942508..f2635cad 100644 --- a/src/store.ts +++ b/src/store.ts @@ -4,7 +4,7 @@ import { AnyAction, configureStore, Reducer } from '@reduxjs/toolkit' import contextsMigration from '@utils/migrations/contexts/migration' import instancesMigration from '@utils/migrations/instances/migration' import settingsMigration from '@utils/migrations/settings/migration' -import appSlice from '@utils/slices/appSlice' +import appSlice, { AppState } from '@utils/slices/appSlice' import contextsSlice, { ContextsState } from '@utils/slices/contextsSlice' import instancesSlice, { InstancesState } from '@utils/slices/instancesSlice' import settingsSlice, { SettingsState } from '@utils/slices/settingsSlice' @@ -26,6 +26,13 @@ const secureStorage = createSecureStore() const prefix = 'tooot' +const appPersistConfig = { + key: 'app', + prefix, + storage: AsyncStorage, + version: 0 +} + const contextsPersistConfig = { key: 'contexts', prefix, @@ -55,19 +62,19 @@ const settingsPersistConfig = { const store = configureStore({ reducer: { + app: persistReducer(appPersistConfig, appSlice) as Reducer, contexts: persistReducer(contextsPersistConfig, contextsSlice) as Reducer< ContextsState, AnyAction >, - instances: persistReducer( - instancesPersistConfig, - instancesSlice - ) as Reducer, + instances: persistReducer(instancesPersistConfig, instancesSlice) as Reducer< + InstancesState, + AnyAction + >, settings: persistReducer(settingsPersistConfig, settingsSlice) as Reducer< SettingsState, AnyAction - >, - app: appSlice + > }, middleware: getDefaultMiddleware => getDefaultMiddleware({ diff --git a/src/utils/migrations/app/v0.ts b/src/utils/migrations/app/v0.ts new file mode 100644 index 00000000..99a738db --- /dev/null +++ b/src/utils/migrations/app/v0.ts @@ -0,0 +1,4 @@ +export type AppV0 = { + expoToken?: string + versionUpdate: boolean +} diff --git a/src/utils/push/useConnect.ts b/src/utils/push/useConnect.ts index 342a77f2..d519f71d 100644 --- a/src/utils/push/useConnect.ts +++ b/src/utils/push/useConnect.ts @@ -33,53 +33,55 @@ const pushUseConnect = ({ t, instances }: Params) => { method: 'get', url: `push/connect/${expoToken}`, sentry: true - }).catch(error => { - Sentry.setExtras({ - API: 'tooot', - ...(error?.response && { response: error.response }), - ...(error?.request && { request: error.request }) - }) - Sentry.captureException(error) - Notifications.setBadgeCountAsync(0) - if (error?.status == 404) { - displayMessage({ - theme, - type: 'error', - duration: 'long', - message: t('pushError.message'), - description: t('pushError.description'), - onPress: () => { - navigationRef.navigate('Screen-Tabs', { - screen: 'Tab-Me', - params: { - screen: 'Tab-Me-Root' - } - }) - navigationRef.navigate('Screen-Tabs', { - screen: 'Tab-Me', - params: { - screen: 'Tab-Me-Settings' - } - }) - } - }) - - dispatch(disableAllPushes()) - - instances.forEach(instance => { - if (instance.push.global.value) { - apiGeneral<{}>({ - method: 'delete', - domain: instance.url, - url: 'api/v1/push/subscription', - headers: { - Authorization: `Bearer ${instance.token}` - } - }).catch(() => console.log('error!!!')) - } - }) - } }) + .then(() => Notifications.setBadgeCountAsync(0)) + .catch(error => { + Sentry.setExtras({ + API: 'tooot', + ...(error?.response && { response: error.response }), + ...(error?.request && { request: error.request }) + }) + Sentry.captureException(error) + Notifications.setBadgeCountAsync(0) + if (error?.status == 404) { + displayMessage({ + theme, + type: 'error', + duration: 'long', + message: t('pushError.message'), + description: t('pushError.description'), + onPress: () => { + navigationRef.navigate('Screen-Tabs', { + screen: 'Tab-Me', + params: { + screen: 'Tab-Me-Root' + } + }) + navigationRef.navigate('Screen-Tabs', { + screen: 'Tab-Me', + params: { + screen: 'Tab-Me-Settings' + } + }) + } + }) + + dispatch(disableAllPushes()) + + instances.forEach(instance => { + if (instance.push.global.value) { + apiGeneral<{}>({ + method: 'delete', + domain: instance.url, + url: 'api/v1/push/subscription', + headers: { + Authorization: `Bearer ${instance.token}` + } + }).catch(() => console.log('error!!!')) + } + }) + } + }) } const pushEnabled = instances.filter(instance => instance.push.global.value) @@ -89,7 +91,6 @@ const pushUseConnect = ({ t, instances }: Params) => { if (expoToken && pushEnabled.length && state === 'active') { Notifications.getBadgeCountAsync().then(count => { if (count > 0) { - Notifications.setBadgeCountAsync(0) connect() } }) @@ -102,7 +103,6 @@ const pushUseConnect = ({ t, instances }: Params) => { }, [expoToken, pushEnabled.length]) return useEffect(() => { - Notifications.setBadgeCountAsync(0) if (expoToken && pushEnabled.length) { connect() } diff --git a/yarn.lock b/yarn.lock index 43991d45..3aa45a2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1141,13 +1141,13 @@ mv "~2" safe-json-stringify "~1" -"@expo/cli@0.4.9": - version "0.4.9" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.4.9.tgz#24a2714b11a918b588138220c79b4ec9b553adba" - integrity sha512-/9ljfK4eMWXb97XWSJgxcSWpuKXABHFd7Q8Kswp24B2Y5w4MIyaEPhSErIJYTq/FJH7p98Ti1zhk/PhBL0vVTw== +"@expo/cli@0.4.10": + version "0.4.10" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.4.10.tgz#e965b97888c83cecdaddbb8dca3d5827643a0f36" + integrity sha512-c8NJOVa5b8g9CYj8ahdaN21cVE2wPwUaFrtTE0kLeRR5ASy8reWLFEOcstEtt6eufdcN/uGgBWQ0FLovgLZuzw== dependencies: "@babel/runtime" "^7.14.0" - "@expo/code-signing-certificates" "^0.0.2" + "@expo/code-signing-certificates" "0.0.5" "@expo/config" "~7.0.2" "@expo/config-plugins" "~5.0.3" "@expo/dev-server" "0.1.123" @@ -1208,10 +1208,10 @@ uuid "^3.4.0" wrap-ansi "^7.0.0" -"@expo/code-signing-certificates@^0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@expo/code-signing-certificates/-/code-signing-certificates-0.0.2.tgz#65cd615800e6724b54831c966dd1a90145017246" - integrity sha512-vnPHFjwOqxQ1VLztktY+fYCfwvLzjqpzKn09rchcQE7Sdf0wtW5fFtIZBEFOOY5wasp8tXSnp627zrAwazPHzg== +"@expo/code-signing-certificates@0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz#a693ff684fb20c4725dade4b88a6a9f96b02496c" + integrity sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw== dependencies: node-forge "^1.2.1" nullthrows "^1.1.1" @@ -2139,19 +2139,19 @@ resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== -"@react-navigation/bottom-tabs@^6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-6.4.0.tgz#63743874648f92adedf37186cb7cedcd47826ee9" - integrity sha512-90CapiXjiWudbCiki9e6fOr/CECQRguIxv5OD7IBfbAMGX5GGiJpX8aqiHAz2DxpAz31v4JZcUr945+lFhXBfA== +"@react-navigation/bottom-tabs@^6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-6.4.1.tgz#54e469077f7c9f3750e6727ec8f298d5497b6ae5" + integrity sha512-b12o0aVe+A9bUVP31kQLVcZ5l4Inm/7XhpSS5FvwhoI9L2iwofK1tt98uEQd79NIAklfui+IqRqr2X6XKkygbA== dependencies: - "@react-navigation/elements" "^1.3.6" + "@react-navigation/elements" "^1.3.7" color "^4.2.3" warn-once "^0.1.0" -"@react-navigation/core@^6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-6.4.0.tgz#c44d33a8d8ef010a102c7f831fc8add772678509" - integrity sha512-tpc0Ak/DiHfU3LlYaRmIY7vI4sM/Ru0xCet6runLUh9aABf4wiLgxyFJ5BtoWq6xFF8ymYEA/KWtDhetQ24YiA== +"@react-navigation/core@^6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-6.4.1.tgz#2d290cadc23992e914c45b5712efa30e5f37587f" + integrity sha512-M1rTS+qaw84ZItvObwFTjuvXfrJRnXL2uJJeiDVwSvu7/30Wp6bkL3/MfLUEA6I4CzJhL74iRSfCKSVe7kVhlw== dependencies: "@react-navigation/routers" "^6.1.3" escape-string-regexp "^4.0.0" @@ -2160,25 +2160,25 @@ react-is "^16.13.0" use-latest-callback "^0.1.5" -"@react-navigation/elements@^1.3.6": - version "1.3.6" - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.6.tgz#fa700318528db93f05144b1be4b691b9c1dd1abe" - integrity sha512-pNJ8R9JMga6SXOw6wGVN0tjmE6vegwPmJBL45SEMX2fqTfAk2ykDnlJHodRpHpAgsv0DaI8qX76z3A+aqKSU0w== +"@react-navigation/elements@^1.3.7": + version "1.3.7" + resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.7.tgz#40d80a7f34d645b605289da10bc4b6c3a7667215" + integrity sha512-OZg2N/dd2tl6qkfrWvmUjFsYsbEyHEv4NbZSBuT+CR+d1pzmexN2IeVmi4cEMoR7U7GwhFcHRevF36yBsjU/dw== -"@react-navigation/native-stack@^6.9.1": - version "6.9.1" - resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-6.9.1.tgz#6013300e4cd0b33e242aa18593e4dff7db2ab3d1" - integrity sha512-aOuJP97ge6NRz8wH6sDKfLTfdygGmraYh0apKrrVbGvMnflbPX4kpjQiAQcUPUpMeas0betH/Su8QubNL8HEkg== +"@react-navigation/native-stack@^6.9.2": + version "6.9.2" + resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-6.9.2.tgz#8c861a59a2a86aaf25f0f33a990958edc36ddac4" + integrity sha512-W5CYX+mYVyOYUgWoN15O34Pv/JieX2/pU09yh+kacxPHPEm4gYTQM/OIHACOov5T2WyiNSU6iIHjfzQ6e7M9zw== dependencies: - "@react-navigation/elements" "^1.3.6" + "@react-navigation/elements" "^1.3.7" warn-once "^0.1.0" -"@react-navigation/native@^6.0.13": - version "6.0.13" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.0.13.tgz#ec504120e193ea6a7f24ffa765a1338be5a3160a" - integrity sha512-CwaJcAGbhv3p3ECablxBkw8QBCGDWXqVRwQ4QbelajNW623m3sNTC9dOF6kjp8au6Rg9B5e0KmeuY0xWbPk79A== +"@react-navigation/native@^6.0.14": + version "6.0.14" + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.0.14.tgz#3ce3a4176ac7dc5495241d56a28db18449baf754" + integrity sha512-Z95bJrRkZerBJq6Qc/xjA/kibPpB+UvPeMWS1CBhRF8FaX1483UdHqPqSbW879tPwjP2R4XfoA4dtoEHswrOjA== dependencies: - "@react-navigation/core" "^6.4.0" + "@react-navigation/core" "^6.4.1" escape-string-regexp "^4.0.0" fast-deep-equal "^3.1.3" nanoid "^3.1.23" @@ -2190,12 +2190,12 @@ dependencies: nanoid "^3.1.23" -"@react-navigation/stack@^6.3.4": - version "6.3.4" - resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-6.3.4.tgz#c3b7a479aea609c0de609f91be7b2539dbae37c2" - integrity sha512-f4vQcbaDPSFHF1i6CnEYbA0Bnk5jRGMoCIs2/Tq0HwsUI62Mui1q5vvIlRDIi5QomJoHzhfTBp9IzMQ/sUQJlg== +"@react-navigation/stack@^6.3.5": + version "6.3.5" + resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-6.3.5.tgz#c616ef78cbaa55938da993d871d9fc41801d41f4" + integrity sha512-G706Ow+8fhiLT5Cf48566YsjboU5h5ll9nva90xX3br7V6x0JMoGMvXjvbORZyzLuUytQO3mf0T3nVUuO52vZQ== dependencies: - "@react-navigation/elements" "^1.3.6" + "@react-navigation/elements" "^1.3.7" color "^4.2.3" warn-once "^0.1.0" @@ -5728,13 +5728,13 @@ expo-web-browser@~12.0.0: dependencies: compare-urls "^2.0.0" -expo@^47.0.6: - version "47.0.6" - resolved "https://registry.yarnpkg.com/expo/-/expo-47.0.6.tgz#4436bf0f667b6282f9189e987ddf67cd64807140" - integrity sha512-XFcTnkOWEbc5mbrpdgIkPq3Heuem+8OErdjnWshwiDtaCdqK0EKToJtE2ufhCxb/RzfeJ5k07mpk+8CRRayi4w== +expo@^47.0.7: + version "47.0.7" + resolved "https://registry.yarnpkg.com/expo/-/expo-47.0.7.tgz#4fe95529c6b1e6d8026a164847ce1048f6dc5ccd" + integrity sha512-uHTxvPdD4K7W277tXVAdFmgjSZ0Hg+0g5bnez0jXyXQINa+0CTtGmulgFehBOrbrUZBqwosN0K8TsrxADwcXZQ== dependencies: "@babel/runtime" "^7.14.0" - "@expo/cli" "0.4.9" + "@expo/cli" "0.4.10" "@expo/config" "7.0.3" "@expo/config-plugins" "5.0.4" "@expo/vector-icons" "^13.0.0" @@ -10507,10 +10507,10 @@ react-native-live-text-image-view@^0.4.0: resolved "https://registry.yarnpkg.com/react-native-live-text-image-view/-/react-native-live-text-image-view-0.4.0.tgz#d23d5850788609fd1448533213fc6c453c584761" integrity sha512-PhVFE0YogSLrTlnHIxUWL80CrmbDvxk1JvLx4UHiRHRwLda4dV/e/9Q+Pyh7Vq7qwAPE6vGoN6sjbXNaj4WCew== -react-native-pager-view@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.1.0.tgz#6cb13fb6f2db8836c89ee8261b07603e97527ceb" - integrity sha512-wBtc77l+lWRDS1FlpeO+rE0iG/ecRtVhHrERMkm7CACQvhB+ty8NL2zlAzOCCbS83DZrIdtWuPu9VqziKHzJPw== +react-native-pager-view@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-6.1.1.tgz#e396b0c78d74931a92d1c9bf28c450eb356b904d" + integrity sha512-4WqR0YWBjaUtc3iPVk8w6wsTsMq+r0plNiXdqPXOiUdQIhxKfsP1iqb6hRzdeJKOo8UH7RCP6zI7dxdFEAXzrw== react-native-reanimated-zoom@^0.3.3: version "0.3.3"