diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 8e054a59..6d6f9853 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -57,7 +57,7 @@ private_lane :github_release do |options| description: "No changelog provided", commitish: git_branch, is_prerelease: options[:prerelease], - upload_assets: options[:prerelease] ? nil : ["#{File.expand_path('..', Dir.pwd)}/tooot-#{GITHUB_RELEASE}.apk"] + upload_assets: ["#{File.expand_path('..', Dir.pwd)}/tooot-#{GITHUB_RELEASE}.apk"] ) end @@ -133,8 +133,8 @@ private_lane :build_android do task: 'clean bundle', build_type: 'release', project_dir: "./android", - print_command: false, - print_command_output: false, + print_command: true, + print_command_output: true, properties: { "expoSDK" => VERSIONS[:expo], "releaseChannel" => RELEASE_CHANNEL, @@ -151,14 +151,30 @@ private_lane :build_android do skip_upload_images: true, skip_upload_screenshots: true ) + build_android_app( + task: 'assemble', + build_type: 'release', + project_dir: "./android", + print_command: false, + print_command_output: false, + properties: { + "expoSDK" => VERSIONS[:expo], + "releaseChannel" => RELEASE_CHANNEL, + "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" when "release" prepare_playstore_android build_android_app( task: 'clean bundle', build_type: 'release', project_dir: "./android", - print_command: false, - print_command_output: false, + print_command: true, + print_command_output: true, properties: { "expoSDK" => VERSIONS[:expo], "releaseChannel" => RELEASE_CHANNEL, diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f24b1e4f..f004d4df 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -377,9 +377,9 @@ PODS: - React-Core - react-native-cameraroll (4.1.2): - React-Core - - react-native-netinfo (7.1.9): + - react-native-netinfo (8.0.0): - React-Core - - react-native-pager-view (5.4.9): + - react-native-pager-view (5.4.11): - React-Core - react-native-safe-area-context (3.3.2): - React-Core @@ -450,7 +450,7 @@ PODS: - React-jsi (= 0.66.4) - React-logger (= 0.66.4) - React-perflogger (= 0.66.4) - - RNCAsyncStorage (1.15.17): + - RNCAsyncStorage (1.16.1): - React-Core - RNFastImage (8.5.11): - React-Core @@ -458,7 +458,7 @@ PODS: - SDWebImageWebPCoder (~> 0.8.4) - RNGestureHandler (2.2.0): - React-Core - - RNReanimated (2.4.0): + - RNReanimated (2.4.1): - DoubleConversion - FBLazyVector - FBReactNativeSpec @@ -486,7 +486,7 @@ PODS: - React-RCTText - ReactCommon/turbomodule/core - Yoga - - RNScreens (3.10.2): + - RNScreens (3.11.1): - React-Core - React-RCTImage - RNSentry (3.2.13): @@ -833,8 +833,8 @@ SPEC CHECKSUMS: react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c react-native-blurhash: b7ed7b7de81ae1f9ce52d694f43c5b5cddddcae1 react-native-cameraroll: 2957f2bce63ae896a848fbe0d5352c1bd4d20866 - react-native-netinfo: 87e5bfaf21ea5c6c110941290aa481dd8e849f98 - react-native-pager-view: 3ee7d4c7697fb3ef788346e834a60cca97ed8540 + react-native-netinfo: ba4ea50d836c60580c59079233c400753fe5bcb6 + react-native-pager-view: 7f00d63688f7df9fad86dfb0154814419cc5eb8d react-native-safe-area-context: 584dc04881deb49474363f3be89e4ca0e854c057 react-native-segmented-control: 65df6cd0619b780b3843d574a72d4c7cec396097 React-perflogger: 93075d8931c32cd1fce8a98c15d2d5ccc4d891bd @@ -849,11 +849,11 @@ SPEC CHECKSUMS: React-RCTVibration: e3ffca672dd3772536cb844274094b0e2c31b187 React-runtimeexecutor: dec32ee6f2e2a26e13e58152271535fadff5455a ReactCommon: 57b69f6383eafcbd7da625bfa6003810332313c4 - RNCAsyncStorage: 6bd5a7ba3dde1c3facba418aa273f449bdc5437a + RNCAsyncStorage: b49b4e38a1548d03b74b30e558a1d18465b94be7 RNFastImage: cced864a4a2eac27c5c10ac16bd5e8b9d2be4504 RNGestureHandler: bf572f552ea324acd5b5464b8d30755b2d8c1de6 - RNReanimated: fb56abd99a5d7efe91ae3c7bf3cd3663cee1b21b - RNScreens: d6da2b9e29cf523832c2542f47bf1287318b1868 + RNReanimated: e8afbe2a9e08e9e778ea72ddb1a8533445812621 + RNScreens: 4d83613b50b74ed277026375dc0810893b0c347f RNSentry: 0aa1567f66c20390f3834637fc4f73380dcd0774 RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f SDWebImage: 53179a2dba77246efa8a9b85f5c5b21f8f43e38f diff --git a/package.json b/package.json index e511625b..f630b287 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "tooot", "versions": { - "native": "220206", + "native": "220214", "major": 3, - "minor": 4, - "patch": 4, + "minor": 5, + "patch": 0, "expo": "44.0.0" }, "description": "tooot app for Mastodon", @@ -27,14 +27,14 @@ "dependencies": { "@expo/react-native-action-sheet": "3.13.0", "@neverdull-agency/expo-unlimited-secure-store": "1.0.10", - "@react-native-async-storage/async-storage": "1.15.17", + "@react-native-async-storage/async-storage": "1.16.1", "@react-native-community/blur": "3.6.0", "@react-native-community/cameraroll": "4.1.2", - "@react-native-community/netinfo": "7.1.9", + "@react-native-community/netinfo": "8.0.0", "@react-native-community/segmented-control": "2.2.2", "@react-navigation/bottom-tabs": "6.2.0", "@react-navigation/native": "6.0.8", - "@react-navigation/native-stack": "6.4.1", + "@react-navigation/native-stack": "6.5.0", "@react-navigation/stack": "6.1.1", "@reduxjs/toolkit": "1.7.2", "@sentry/react-native": "3.2.13", @@ -67,7 +67,7 @@ "lodash": "4.17.21", "react": "17.0.2", "react-dom": "17.0.2", - "react-i18next": "11.15.3", + "react-i18next": "11.15.4", "react-native": "0.66.4", "react-native-animated-spinkit": "1.5.2", "react-native-base64": "^0.2.1", @@ -77,14 +77,14 @@ "react-native-flash-message": "0.2.1", "react-native-gesture-handler": "2.2.0", "react-native-htmlview": "0.16.0", - "react-native-pager-view": "5.4.9", - "react-native-reanimated": "2.4.0", + "react-native-pager-view": "5.4.11", + "react-native-reanimated": "2.4.1", "react-native-safe-area-context": "3.3.2", - "react-native-screens": "3.10.2", + "react-native-screens": "3.11.1", "react-native-svg": "12.1.1", "react-native-swipe-list-view": "3.2.9", "react-native-tab-view": "3.1.1", - "react-query": "3.34.14", + "react-query": "3.34.15", "react-redux": "7.2.6", "react-timeago": "6.2.1", "redux-persist": "6.0.0", @@ -94,10 +94,10 @@ "valid-url": "1.0.9" }, "devDependencies": { - "@babel/core": "7.17.0", + "@babel/core": "7.17.2", "@babel/plugin-proposal-optional-chaining": "7.16.7", "@babel/preset-typescript": "7.16.7", - "@expo/config": "6.0.16", + "@expo/config": "6.0.18", "@types/lodash": "4.14.178", "@types/react": "17.0.39", "@types/react-dom": "17.0.11", diff --git a/src/Screens.tsx b/src/Screens.tsx index 632452a9..d353ab9b 100644 --- a/src/Screens.tsx +++ b/src/Screens.tsx @@ -1,3 +1,4 @@ +import analytics from '@components/analytics' import { HeaderLeft } from '@components/Header' import { displayMessage, Message } from '@components/Message' import navigationRef from '@helpers/navigationRef' @@ -14,13 +15,13 @@ import pushUseConnect from '@utils/push/useConnect' import pushUseReceive from '@utils/push/useReceive' import pushUseRespond from '@utils/push/useRespond' import { updatePreviousTab } from '@utils/slices/contextsSlice' +import { checkEmojis } from '@utils/slices/instances/checkEmojis' import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences' import { updateConfiguration } from '@utils/slices/instances/updateConfiguration' import { updateFilters } from '@utils/slices/instances/updateFilters' import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice' import { useTheme } from '@utils/styles/ThemeManager' import { themes } from '@utils/styles/themes' -import * as Analytics from 'expo-firebase-analytics' import * as Linking from 'expo-linking' import { addScreenshotListener } from 'expo-screen-capture' import React, { useCallback, useEffect, useRef, useState } from 'react' @@ -92,6 +93,7 @@ const Screens: React.FC = ({ localCorrupt }) => { dispatch(updateConfiguration()) dispatch(updateFilters()) dispatch(updateAccountPreferences()) + dispatch(checkEmojis()) } }, [instanceActive]) @@ -116,7 +118,7 @@ const Screens: React.FC = ({ localCorrupt }) => { } if (previousRoute?.name !== currentRoute?.name) { - Analytics.logEvent('screen_view', { screen_name: currentRoute?.name }) + analytics('screen_view', { screen_name: currentRoute?.name }) Sentry.Native.setContext('page', { previous: previousRoute, current: currentRoute diff --git a/src/api/general.ts b/src/api/general.ts index 20849953..270286ce 100644 --- a/src/api/general.ts +++ b/src/api/general.ts @@ -58,14 +58,14 @@ const apiGeneral = async ({ }) }) .catch(error => { - if (sentry && Math.random() < 0.01) { - Sentry.Native.setExtras({ - API: 'general', - ...(error.response && { response: error.response }), - ...(error.request && { request: error.request }) - }) - Sentry.Native.captureException(error) - } + // if (sentry && Math.random() < 0.01) { + // Sentry.Native.setExtras({ + // API: 'general', + // ...(error.response && { response: error.response }), + // ...(error.request && { request: error.request }) + // }) + // Sentry.Native.captureException(error) + // } if (error.response) { // The request was made and the server responded with a status code diff --git a/src/api/instance.ts b/src/api/instance.ts index 81236aeb..7a2558b7 100644 --- a/src/api/instance.ts +++ b/src/api/instance.ts @@ -98,14 +98,14 @@ const apiInstance = async ({ }) }) .catch(error => { - if (Math.random() < 0.001) { - Sentry.Native.setExtras({ - API: 'instance', - ...(error.response && { response: error.response }), - ...(error.request && { request: error.request }) - }) - Sentry.Native.captureException(error) - } + // if (Math.random() < 0.001) { + // Sentry.Native.setExtras({ + // API: 'instance', + // ...(error.response && { response: error.response }), + // ...(error.request && { request: error.request }) + // }) + // Sentry.Native.captureException(error) + // } if (error.response) { // The request was made and the server responded with a status code diff --git a/src/api/tooot.ts b/src/api/tooot.ts index a2d206b5..90a30717 100644 --- a/src/api/tooot.ts +++ b/src/api/tooot.ts @@ -19,7 +19,7 @@ export type Params = { export const TOOOT_API_DOMAIN = mapEnvironment({ release: 'api.tooot.app', - candidate: 'api-candidate.tooot.app', + candidate: 'api.tooot.app', development: 'api-development.tooot.app' }) diff --git a/src/components/Emojis.tsx b/src/components/Emojis.tsx index 7854eeca..0eafa302 100644 --- a/src/components/Emojis.tsx +++ b/src/components/Emojis.tsx @@ -2,6 +2,7 @@ import EmojisButton from '@components/Emojis/Button' import EmojisList from '@components/Emojis/List' import { useAccessibility } from '@utils/accessibility/AccessibilityManager' import { useEmojisQuery } from '@utils/queryHooks/emojis' +import { getInstanceFrequentEmojis } from '@utils/slices/instancesSlice' import { chunk, forEach, groupBy, sortBy } from 'lodash' import React, { Dispatch, @@ -11,11 +12,16 @@ import React, { useEffect, useReducer } from 'react' +import { useTranslation } from 'react-i18next' import FastImage from 'react-native-fast-image' +import { useSelector } from 'react-redux' import EmojisContext, { emojisReducer } from './Emojis/helpers/EmojisContext' const prefetchEmojis = ( - sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[], + sortedEmojis: { + title: string + data: Pick[][] + }[], reduceMotionEnabled: boolean ) => { const prefetches: { uri: string }[] = [] @@ -101,14 +107,28 @@ const ComponentEmojis: React.FC = ({ [value, selectionRange.current?.start, selectionRange.current?.end] ) + const { t } = useTranslation() const { data } = useEmojisQuery({ options: { enabled } }) + const frequentEmojis = useSelector(getInstanceFrequentEmojis, () => true) useEffect(() => { if (data && data.length) { - let sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[] = [] + let sortedEmojis: { + title: string + data: Pick[][] + }[] = [] forEach( groupBy(sortBy(data, ['category', 'shortcode']), 'category'), (value, key) => sortedEmojis.push({ title: key, data: chunk(value, 5) }) ) + if (frequentEmojis.length) { + sortedEmojis.unshift({ + title: t('componentEmojis:frequentUsed'), + data: chunk( + frequentEmojis.map(e => e.emoji), + 5 + ) + }) + } emojisDispatch({ type: 'load', payload: sortedEmojis diff --git a/src/components/Emojis/List.tsx b/src/components/Emojis/List.tsx index c143e3c8..1f60f338 100644 --- a/src/components/Emojis/List.tsx +++ b/src/components/Emojis/List.tsx @@ -1,4 +1,5 @@ import { useAccessibility } from '@utils/accessibility/AccessibilityManager' +import { countInstanceEmoji } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import layoutAnimation from '@utils/styles/layoutAnimation' import { useTheme } from '@utils/styles/ThemeManager' @@ -14,11 +15,13 @@ import { View } from 'react-native' import FastImage from 'react-native-fast-image' +import { useDispatch } from 'react-redux' import validUrl from 'valid-url' import EmojisContext from './helpers/EmojisContext' const EmojisList = React.memo( () => { + const dispatch = useDispatch() const { reduceMotionEnabled } = useAccessibility() const { t } = useTranslation() @@ -42,12 +45,13 @@ const EmojisList = React.memo( return ( + onPress={() => { emojisDispatch({ type: 'shortcode', payload: `:${emoji.shortcode}:` }) - } + dispatch(countInstanceEmoji(emoji)) + }} > [][] + }[] shortcode: Mastodon.Emoji['shortcode'] | null } diff --git a/src/components/Timeline/Shared/Attachment/Image.tsx b/src/components/Timeline/Shared/Attachment/Image.tsx index 57f136b5..6d14663b 100644 --- a/src/components/Timeline/Shared/Attachment/Image.tsx +++ b/src/components/Timeline/Shared/Attachment/Image.tsx @@ -34,8 +34,8 @@ const AttachmentImage = React.memo( !image.meta?.original?.width || !image.meta?.original?.height ? attachmentAspectRatio({ total, index }) - : image.meta.original.height / image.meta.original.width > 2 - ? 0.5 + : image.meta.original.height / image.meta.original.width > 1 + ? 1 : image.meta.original.width / image.meta.original.height }} /> diff --git a/src/components/Timeline/Shared/Filtered.tsx b/src/components/Timeline/Shared/Filtered.tsx index 6736fda2..9892632f 100644 --- a/src/components/Timeline/Shared/Filtered.tsx +++ b/src/components/Timeline/Shared/Filtered.tsx @@ -41,7 +41,7 @@ export const shouldFilter = ({ }) => { const instance = getInstance(store.getState()) const ownAccount = - getInstanceAccount(store.getState())?.id === status.account.id + getInstanceAccount(store.getState())?.id === status.account?.id let shouldFilter = false if (!ownAccount) { diff --git a/src/components/analytics.ts b/src/components/analytics.ts index b028ce21..dd205a63 100644 --- a/src/components/analytics.ts +++ b/src/components/analytics.ts @@ -1,7 +1,7 @@ import * as Analytics from 'expo-firebase-analytics' const analytics = (event: string, params?: { [key: string]: any }) => { - Analytics.logEvent(event, params) + Analytics.logEvent(event, params).catch(() => {}) } export default analytics diff --git a/src/i18n/en/_all.ts b/src/i18n/en/_all.ts index d4c1b7c5..9032bb7b 100644 --- a/src/i18n/en/_all.ts +++ b/src/i18n/en/_all.ts @@ -8,6 +8,7 @@ export default { screenImageViewer: require('./screens/imageViewer'), screenTabs: require('./screens/tabs'), + componentEmojis: require('./components/emojis'), componentInstance: require('./components/instance'), componentMediaSelector: require('./components/mediaSelector'), componentParse: require('./components/parse'), diff --git a/src/i18n/en/components/emojis.json b/src/i18n/en/components/emojis.json new file mode 100644 index 00000000..579c534b --- /dev/null +++ b/src/i18n/en/components/emojis.json @@ -0,0 +1,3 @@ +{ + "frequentUsed": "Frequent used" +} \ No newline at end of file diff --git a/src/i18n/ko/_all.ts b/src/i18n/ko/_all.ts index d4c1b7c5..9032bb7b 100644 --- a/src/i18n/ko/_all.ts +++ b/src/i18n/ko/_all.ts @@ -8,6 +8,7 @@ export default { screenImageViewer: require('./screens/imageViewer'), screenTabs: require('./screens/tabs'), + componentEmojis: require('./components/emojis'), componentInstance: require('./components/instance'), componentMediaSelector: require('./components/mediaSelector'), componentParse: require('./components/parse'), diff --git a/src/i18n/ko/components/emojis.json b/src/i18n/ko/components/emojis.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/i18n/ko/components/emojis.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/vi/_all.ts b/src/i18n/vi/_all.ts index d4c1b7c5..9032bb7b 100644 --- a/src/i18n/vi/_all.ts +++ b/src/i18n/vi/_all.ts @@ -8,6 +8,7 @@ export default { screenImageViewer: require('./screens/imageViewer'), screenTabs: require('./screens/tabs'), + componentEmojis: require('./components/emojis'), componentInstance: require('./components/instance'), componentMediaSelector: require('./components/mediaSelector'), componentParse: require('./components/parse'), diff --git a/src/i18n/vi/components/emojis.json b/src/i18n/vi/components/emojis.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/i18n/vi/components/emojis.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/vi/screens/tabs.json b/src/i18n/vi/screens/tabs.json index 81622b17..892f3cce 100644 --- a/src/i18n/vi/screens/tabs.json +++ b/src/i18n/vi/screens/tabs.json @@ -232,6 +232,14 @@ "cancel": "$t(common:buttons.cancel)" } }, + "darkTheme": { + "heading": "Độ tối", + "options": { + "lighter": "Thấp", + "darker": "Vừa", + "cancel": "$t(common:buttons.cancel)" + } + }, "browser": { "heading": "Mở liên kết", "options": { @@ -282,12 +290,12 @@ } }, "attachments": { - "name": "<0 /><1>'s media" + "name": "Bộ sưu tập của <1>" }, "search": { "header": { "prefix": "Tìm kiếm", - "placeholder": "về..." + "placeholder": "tất cả mọi thứ" }, "empty": { "general": "Tìm $t(screenTabs:shared.search.sections.accounts), $t(screenTabs:shared.search.sections.hashtags) hoặc $t(screenTabs:shared.search.sections.statuses)", diff --git a/src/i18n/zh-Hans/_all.ts b/src/i18n/zh-Hans/_all.ts index d4c1b7c5..9032bb7b 100644 --- a/src/i18n/zh-Hans/_all.ts +++ b/src/i18n/zh-Hans/_all.ts @@ -8,6 +8,7 @@ export default { screenImageViewer: require('./screens/imageViewer'), screenTabs: require('./screens/tabs'), + componentEmojis: require('./components/emojis'), componentInstance: require('./components/instance'), componentMediaSelector: require('./components/mediaSelector'), componentParse: require('./components/parse'), diff --git a/src/i18n/zh-Hans/components/emojis.json b/src/i18n/zh-Hans/components/emojis.json new file mode 100644 index 00000000..b023ed0d --- /dev/null +++ b/src/i18n/zh-Hans/components/emojis.json @@ -0,0 +1,3 @@ +{ + "frequentUsed": "常用" +} diff --git a/src/screens/Compose/Root.tsx b/src/screens/Compose/Root.tsx index 124d447b..f2270d8a 100644 --- a/src/screens/Compose/Root.tsx +++ b/src/screens/Compose/Root.tsx @@ -30,7 +30,11 @@ import FastImage from 'react-native-fast-image' import { useAccessibility } from '@utils/accessibility/AccessibilityManager' import { ComposeState } from './utils/types' import { useSelector } from 'react-redux' -import { getInstanceConfigurationStatusCharsURL } from '@utils/slices/instancesSlice' +import { + getInstanceConfigurationStatusCharsURL, + getInstanceFrequentEmojis +} from '@utils/slices/instancesSlice' +import { useTranslation } from 'react-i18next' const prefetchEmojis = ( sortedEmojis: NonNullable, @@ -99,15 +103,29 @@ const ComposeRoot = React.memo( } }, [composeState.tag]) + const { t } = useTranslation() const { data: emojisData } = useEmojisQuery({}) + const frequentEmojis = useSelector(getInstanceFrequentEmojis, () => true) useEffect(() => { if (emojisData && emojisData.length) { - let sortedEmojis: { title: string; data: Mastodon.Emoji[][] }[] = [] + const sortedEmojis: { + title: string + data: Pick[][] + }[] = [] forEach( groupBy(sortBy(emojisData, ['category', 'shortcode']), 'category'), (value, key) => sortedEmojis.push({ title: key, data: chunk(value, 5) }) ) + if (frequentEmojis.length) { + sortedEmojis.unshift({ + title: t('componentEmojis:frequentUsed'), + data: chunk( + frequentEmojis.map(e => e.emoji), + 5 + ) + }) + } composeDispatch({ type: 'emoji', payload: { ...composeState.emoji, emojis: sortedEmojis } diff --git a/src/screens/Compose/Root/Footer/Emojis.tsx b/src/screens/Compose/Root/Footer/Emojis.tsx index 1491ab88..71841734 100644 --- a/src/screens/Compose/Root/Footer/Emojis.tsx +++ b/src/screens/Compose/Root/Footer/Emojis.tsx @@ -1,5 +1,6 @@ import haptics from '@components/haptics' import { useAccessibility } from '@utils/accessibility/AccessibilityManager' +import { countInstanceEmoji } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { RefObject, useCallback, useContext, useEffect } from 'react' @@ -14,6 +15,7 @@ import { View } from 'react-native' import FastImage from 'react-native-fast-image' +import { useDispatch } from 'react-redux' import validUrl from 'valid-url' import updateText from '../../updateText' import ComposeContext from '../../utils/createContext' @@ -27,6 +29,7 @@ const ComposeEmojis: React.FC = ({ accessibleRefEmojis }) => { const { reduceMotionEnabled } = useAccessibility() const { colors } = useTheme() const { t } = useTranslation() + const dispatch = useDispatch() useEffect(() => { const tagEmojis = findNodeHandle(accessibleRefEmojis.current) @@ -53,13 +56,14 @@ const ComposeEmojis: React.FC = ({ accessibleRefEmojis }) => { { + haptics('Light') updateText({ composeState, composeDispatch, newText: `:${emoji.shortcode}:`, type: 'emoji' }) - haptics('Light') + dispatch(countInstanceEmoji(emoji)) }} > [][] + }[] + | undefined } poll: { active: boolean diff --git a/src/screens/Tabs/Shared/Account/Attachments.tsx b/src/screens/Tabs/Shared/Account/Attachments.tsx index 5c09e859..a47fb474 100644 --- a/src/screens/Tabs/Shared/Account/Attachments.tsx +++ b/src/screens/Tabs/Shared/Account/Attachments.tsx @@ -91,8 +91,10 @@ const AccountAttachments = React.memo( return ( > = ({ scrolled.current = true const pointer = flattenData.findIndex(({ id }) => id === toot.id) if (pointer === -1) return + Sentry.Native.setContext('Scroll to Index', { + type: 'original', + index: pointer, + itemsLength: flattenData.length, + id: toot.id, + flattenData: flattenData.map(({ id }) => id) + }) try { setTimeout(() => { flRef.current?.scrollToIndex({ @@ -53,14 +60,7 @@ const TabSharedToot: React.FC> = ({ }) }, 500) } catch (err) { - if (Math.random() < 0.1) { - Sentry.Native.setContext('Scroll to Index', { - type: 'original', - index: pointer, - itemsLength: flattenData.length, - id: toot.id, - flattenData: flattenData.map(({ id }) => id) - }) + if (Math.random() < 0.05) { Sentry.Native.captureException(err) } } @@ -74,6 +74,12 @@ const TabSharedToot: React.FC> = ({ error => { const offset = error.averageItemLength * error.index flRef.current?.scrollToOffset({ offset }) + Sentry.Native.setContext('Scroll to Index', { + type: 'onScrollToIndexFailed', + index: error.index, + itemsLength, + id: toot.id + }) try { error.index < itemsLength && setTimeout( @@ -85,13 +91,7 @@ const TabSharedToot: React.FC> = ({ 500 ) } catch (err) { - if (Math.random() < 0.1) { - Sentry.Native.setContext('Scroll to Index', { - type: 'onScrollToIndexFailed', - index: error.index, - itemsLength, - id: toot.id - }) + if (Math.random() < 0.05) { Sentry.Native.captureException(err) } } diff --git a/src/store.ts b/src/store.ts index df8ccc21..4df61af2 100644 --- a/src/store.ts +++ b/src/store.ts @@ -27,7 +27,7 @@ const instancesPersistConfig = { key: 'instances', prefix, storage: secureStorage, - version: 7, + version: 8, // @ts-ignore migrate: createMigrate(instancesMigration) } diff --git a/src/utils/migrations/instances/migration.ts b/src/utils/migrations/instances/migration.ts index 70d175d9..f45abb0a 100644 --- a/src/utils/migrations/instances/migration.ts +++ b/src/utils/migrations/instances/migration.ts @@ -3,6 +3,7 @@ import { InstanceV4 } from './v4' import { InstanceV5 } from './v5' import { InstanceV6 } from './v6' import { InstanceV7 } from './v7' +import { InstanceV8 } from './v8' const instancesMigration = { 4: (state: InstanceV3): InstanceV4 => { @@ -76,6 +77,16 @@ const instancesMigration = { } }) } + }, + 8: (state: InstanceV7): InstanceV8 => { + return { + instances: state.instances.map(instance => { + return { + ...instance, + frequentEmojis: [] + } + }) + } } } diff --git a/src/utils/migrations/instances/v8.ts b/src/utils/migrations/instances/v8.ts new file mode 100644 index 00000000..675c6628 --- /dev/null +++ b/src/utils/migrations/instances/v8.ts @@ -0,0 +1,83 @@ +import { ComposeStateDraft } from '@screens/Compose/utils/types' +import { QueryKeyTimeline } from '@utils/queryHooks/timeline' + +type Instance = { + active: boolean + appData: { + clientId: string + clientSecret: string + } + url: string + token: string + uri: Mastodon.Instance['uri'] + urls: Mastodon.Instance['urls'] + account: { + id: Mastodon.Account['id'] + acct: Mastodon.Account['acct'] + avatarStatic: Mastodon.Account['avatar_static'] + preferences: Mastodon.Preferences + } + max_toot_chars?: number // To be deprecated in v4 + configuration?: Mastodon.Instance['configuration'] + filters: Mastodon.Filter[] + notifications_filter: { + follow: boolean + favourite: boolean + reblog: boolean + mention: boolean + poll: boolean + follow_request: boolean + } + push: { + global: { loading: boolean; value: boolean } + decode: { loading: boolean; value: boolean } + alerts: { + follow: { + loading: boolean + value: Mastodon.PushSubscription['alerts']['follow'] + } + favourite: { + loading: boolean + value: Mastodon.PushSubscription['alerts']['favourite'] + } + reblog: { + loading: boolean + value: Mastodon.PushSubscription['alerts']['reblog'] + } + mention: { + loading: boolean + value: Mastodon.PushSubscription['alerts']['mention'] + } + poll: { + loading: boolean + value: Mastodon.PushSubscription['alerts']['poll'] + } + } + keys: { + auth?: string + public?: string // legacy + private?: string // legacy + } + } + timelinesLookback?: { + [key: string]: { + queryKey: QueryKeyTimeline + ids: Mastodon.Status['id'][] + } + } + mePage: { + lists: { shown: boolean } + announcements: { shown: boolean; unread: number } + } + drafts: ComposeStateDraft[] + frequentEmojis: { + emoji: Pick + score: number + count: number + lastUsed: Date + }[] +} + +export type InstanceV8 = { + instances: Instance[] +} diff --git a/src/utils/slices/instances/add.ts b/src/utils/slices/instances/add.ts index ecbc0c83..470cfc13 100644 --- a/src/utils/slices/instances/add.ts +++ b/src/utils/slices/instances/add.ts @@ -106,7 +106,8 @@ const addInstance = createAsyncThunk( lists: { shown: false }, announcements: { shown: false, unread: 0 } }, - drafts: [] + drafts: [], + frequentEmojis: [] } }) } diff --git a/src/utils/slices/instances/checkEmojis.ts b/src/utils/slices/instances/checkEmojis.ts new file mode 100644 index 00000000..d1e0c445 --- /dev/null +++ b/src/utils/slices/instances/checkEmojis.ts @@ -0,0 +1,15 @@ +import apiInstance from '@api/instance' +import queryClient from '@helpers/queryClient' +import { createAsyncThunk } from '@reduxjs/toolkit' + +export const checkEmojis = createAsyncThunk( + 'instances/checkEmojis', + async (): Promise => { + const res = await apiInstance({ + method: 'get', + url: 'custom_emojis' + }).then(res => res.body) + queryClient.setQueryData(['Emojis'], res) + return res + } +) diff --git a/src/utils/slices/instancesSlice.ts b/src/utils/slices/instancesSlice.ts index 3bc3b4d9..b6b553cb 100644 --- a/src/utils/slices/instancesSlice.ts +++ b/src/utils/slices/instancesSlice.ts @@ -4,6 +4,7 @@ import { RootState } from '@root/store' import { ComposeStateDraft } from '@screens/Compose/utils/types' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import addInstance from './instances/add' +import { checkEmojis } from './instances/checkEmojis' import removeInstance from './instances/remove' import { updateAccountPreferences } from './instances/updateAccountPreferences' import { updateConfiguration } from './instances/updateConfiguration' @@ -81,6 +82,12 @@ export type Instance = { announcements: { shown: boolean; unread: number } } drafts: ComposeStateDraft[] + frequentEmojis: { + emoji: Pick + score: number + count: number + lastUsed: number + }[] } export type InstancesState = { @@ -184,6 +191,56 @@ const instancesSlice = createSlice({ ...instances[activeIndex].mePage, ...action.payload } + }, + countInstanceEmoji: ( + { instances }, + action: PayloadAction + ) => { + const HALF_LIFE = 60 * 60 * 24 * 7 // 1 week + const calculateScore = (emoji: Instance['frequentEmojis'][0]): number => { + var seconds = (new Date().getTime() - emoji.lastUsed) / 1000 + var score = emoji.count + 1 + var order = Math.log(Math.max(score, 1)) / Math.LN10 + var sign = score > 0 ? 1 : score === 0 ? 0 : -1 + return (sign * order + seconds / HALF_LIFE) * 10 + } + const activeIndex = findInstanceActive(instances) + const foundEmojiIndex = instances[activeIndex].frequentEmojis?.findIndex( + e => + e.emoji.shortcode === action.payload.shortcode && + e.emoji.url === action.payload.url + ) + let newEmojisSort: Instance['frequentEmojis'] + if (foundEmojiIndex > -1) { + newEmojisSort = instances[activeIndex].frequentEmojis + .map((e, i) => + i === foundEmojiIndex + ? { + ...e, + score: calculateScore(e), + count: e.count + 1, + lastUsed: new Date().getTime() + } + : e + ) + .sort((a, b) => b.score - a.score) + } else { + newEmojisSort = instances[activeIndex].frequentEmojis || [] + const temp = { + emoji: action.payload, + score: 0, + count: 0, + lastUsed: new Date().getTime() + } + newEmojisSort.push({ + ...temp, + score: calculateScore(temp), + count: temp.count + 1 + }) + } + instances[activeIndex].frequentEmojis = newEmojisSort + .sort((a, b) => b.score - a.score) + .slice(0, 20) } }, extraReducers: builder => { @@ -321,6 +378,22 @@ const instancesSlice = createSlice({ action.meta.arg.changed ].loading = true }) + + // Check if frequently used emojis still exist + .addCase(checkEmojis.fulfilled, (state, action) => { + const activeIndex = findInstanceActive(state.instances) + state.instances[activeIndex].frequentEmojis = state.instances[ + activeIndex + ].frequentEmojis?.filter(emoji => { + return action.payload.find( + e => + e.shortcode === emoji.emoji.shortcode && e.url === emoji.emoji.url + ) + }) + }) + .addCase(checkEmojis.rejected, (_, action) => { + console.error(action.error) + }) } }) @@ -394,6 +467,10 @@ export const getInstanceMePage = ({ instances: { instances } }: RootState) => export const getInstanceDrafts = ({ instances: { instances } }: RootState) => instances[findInstanceActive(instances)]?.drafts +export const getInstanceFrequentEmojis = ({ + instances: { instances } +}: RootState) => instances[findInstanceActive(instances)]?.frequentEmojis + export const { updateInstanceActive, updateInstanceAccount, @@ -403,7 +480,8 @@ export const { clearPushLoading, disableAllPushes, updateInstanceTimelineLookback, - updateInstanceMePage + updateInstanceMePage, + countInstanceEmoji } = instancesSlice.actions export default instancesSlice.reducer diff --git a/yarn.lock b/yarn.lock index ca667ec9..cda8a540 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36,17 +36,17 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== -"@babel/core@7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.0.tgz#16b8772b0a567f215839f689c5ded6bb20e864d5" - integrity sha512-x/5Ea+RO5MvF9ize5DeVICJoVrNv0Mi2RnIABrZEKYvPEpldXwauPkgvYA17cKa6WpU3LoYvYbuEMFtSNFsarA== +"@babel/core@7.17.2": + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.2.tgz#2c77fc430e95139d816d39b113b31bf40fb22337" + integrity sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw== dependencies: "@ampproject/remapping" "^2.0.0" "@babel/code-frame" "^7.16.7" "@babel/generator" "^7.17.0" "@babel/helper-compilation-targets" "^7.16.7" "@babel/helper-module-transforms" "^7.16.7" - "@babel/helpers" "^7.17.0" + "@babel/helpers" "^7.17.2" "@babel/parser" "^7.17.0" "@babel/template" "^7.16.7" "@babel/traverse" "^7.17.0" @@ -447,10 +447,10 @@ "@babel/traverse" "^7.16.5" "@babel/types" "^7.16.0" -"@babel/helpers@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.0.tgz#79cdf6c66a579f3a7b5e739371bc63ca0306886b" - integrity sha512-Xe/9NFxjPwELUvW2dsukcMZIp6XwPSbI4ojFBJuX5ramHuVE22SVcZIwqzdWo5uCgeTXW8qV97lMvSOjq+1+nQ== +"@babel/helpers@^7.17.2": + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" + integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== dependencies: "@babel/template" "^7.16.7" "@babel/traverse" "^7.17.0" @@ -1410,12 +1410,12 @@ xcode "^3.0.1" xml2js "0.4.23" -"@expo/config-plugins@4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-4.0.16.tgz#832fb533245180c6dfd82fd359474f68a8e857b2" - integrity sha512-UTyE5JTBatMSwoOZCtX0u2H3zkp/ybMftc1228ARMop35Dbq+sYO11Ym3MrY5RPh8PYPofokDSOvoUq1jjORXw== +"@expo/config-plugins@4.0.18": + version "4.0.18" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-4.0.18.tgz#80cf6a3aed83e78f714ad097d80e5a7cca48591a" + integrity sha512-tW4bnrnKhn+PPHF8wf1KAoubICAVUHW8CcagvyFqaRIzeh6yavMIOsQShxOVTbgx7LzSyymZ1nEs45yCGAiMfA== dependencies: - "@expo/config-types" "^43.0.1" + "@expo/config-types" "^44.0.0" "@expo/json-file" "8.2.34" "@expo/plist" "0.0.17" "@expo/sdk-runtime-versions" "^1.0.0" @@ -1481,6 +1481,11 @@ resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-43.0.1.tgz#3e047dccb371741a540980eaff26fb0c95039c30" integrity sha512-EtllpCGDdB/UdwAIs5YXJwBLpbFQNdlLLrxIvoILA9cXrpQMWkeDCT9lQPJzFRMFcLUaMuGvkzX2tR4tx5EQFQ== +"@expo/config-types@^44.0.0": + version "44.0.0" + resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-44.0.0.tgz#d3480fe2c99f9e895dae4ebba58b74ed72d03e26" + integrity sha512-d+gpdKOAhqaD5RmcMzGgKzNtvE1w+GCqpFQNSXLliYlXjj+Tv0eL8EPeAdPtvke0vowpPFwd5McXLA90dgY6Jg== + "@expo/config@6.0.15": version "6.0.15" resolved "https://registry.yarnpkg.com/@expo/config/-/config-6.0.15.tgz#aa610f8b714e0b1103e13c8210059519479d11d6" @@ -1498,14 +1503,14 @@ slugify "^1.3.4" sucrase "^3.20.0" -"@expo/config@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@expo/config/-/config-6.0.16.tgz#3e603700b8d270d90718f8ca16a82e761a7f71fc" - integrity sha512-4UgBiEtj03zI9X0iUNClnY3Y7uUGSvmMNAfPTSpKOsYsb0S+mSN+B66fuB+H+12SUldqxDH695g5lpeIrkCiJA== +"@expo/config@6.0.18": + version "6.0.18" + resolved "https://registry.yarnpkg.com/@expo/config/-/config-6.0.18.tgz#fb3dd2dcf319a7ced77765c43e6825de4e14dff8" + integrity sha512-60z0YICI9eJWlvajl/Wgq8+lKm0jfkZOMdATDQtWz6GvWshUxd+A8EYhtgjJ9eWMGqOYkwrCpRNLHz5TZtCKWA== dependencies: "@babel/code-frame" "~7.10.4" - "@expo/config-plugins" "4.0.16" - "@expo/config-types" "^43.0.1" + "@expo/config-plugins" "4.0.18" + "@expo/config-types" "^44.0.0" "@expo/json-file" "8.2.34" getenv "^1.0.0" glob "7.1.6" @@ -1791,10 +1796,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@react-native-async-storage/async-storage@1.15.17": - version "1.15.17" - resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.15.17.tgz#0dae263a52e476ffce871086f1fef5b8e44708eb" - integrity sha512-NQCFs47aFEch9kya/bqwdpvSdZaVRtzU7YB02L8VrmLSLpKgQH/1VwzFUBPcc1/JI1s3GU4yOLVrEbwxq+Fqcw== +"@react-native-async-storage/async-storage@1.16.1": + version "1.16.1" + resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.16.1.tgz#1dbaa9e0f9736e4ab8fc04c628bbb608fd80b068" + integrity sha512-aQ7ka+Ii1e/q+7AVFIZPt4kDeSH8b784wMDtz19Kf4A7hf+OgCHBlUQpOXsrv8XxhlBxu0hv4tfrDO15ChnV0Q== dependencies: merge-options "^3.0.4" @@ -1947,10 +1952,10 @@ sudo-prompt "^9.0.0" wcwidth "^1.0.1" -"@react-native-community/netinfo@7.1.9": - version "7.1.9" - resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-7.1.9.tgz#d3042106ce720d5cda332d4afb1292c9b6156bb5" - integrity sha512-xxbxFherpOjQeJm3rIx6gmZNEEBqVDIcmBII5QVSN8zf3xAwmaT/RxT74ISYDkeSlZwv4eegpkD4QA1ky52CNg== +"@react-native-community/netinfo@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-8.0.0.tgz#7be288c8226ce14ba61e10c6dbd63ef4a56b6ee9" + integrity sha512-8cjkbOWe55vzzc64hfjDv6GWSY8+kfEnxRbwTf9l3hFYDIUMRmMoW+SwxE+QoAfMY32nbEERDy68iev3busRFQ== "@react-native-community/segmented-control@2.2.2": version "2.2.2" @@ -2002,10 +2007,10 @@ resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.1.tgz#aacb08fe0ca4db50f0ed16b72906869ce770edf4" integrity sha512-jIDRJaG8YPIinl4hZXJu/W3TnhDe8hLYmGSEdL1mxZ1aoNMiApCBYkgTy11oq0EfK/koZd3DPSkJNbzBAQmPJw== -"@react-navigation/native-stack@6.4.1": - version "6.4.1" - resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-6.4.1.tgz#089449ed9e1c8194f74373765637948289666d43" - integrity sha512-HBx2Ar9gvTvR+8BEgPJKn+NKT6qTfljEkhsRhNPaSBWzMxNBXACP29t9PoaIlOelkV4V9TvTnXe0NCHjDsObwg== +"@react-navigation/native-stack@6.5.0": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-6.5.0.tgz#332ac7c736e2c2a2d961b324085957c3db79e1d2" + integrity sha512-X2sV+AKkqEl7k0ltjN4lMBfx+FsynrnUWkCTGiROyMRo4yWElK1jY3XSTsj5Cpso2/MUHdf9v/AOw0EgU58FsA== dependencies: "@react-navigation/elements" "^1.3.1" warn-once "^0.1.0" @@ -4171,9 +4176,9 @@ flow-parser@^0.121.0: integrity sha512-1gIBiWJNR0tKUNv8gZuk7l9rVX06OuLzY9AoGio7y/JT4V1IZErEMEq2TJS+PFcw/y0RshZ1J/27VfK1UQzYVg== follow-redirects@^1.14.4: - version "1.14.7" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" - integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== fontfaceobserver@^2.1.0: version "2.1.0" @@ -6429,10 +6434,10 @@ react-freeze@^1.0.0: resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.0.tgz#b21c65fe1783743007c8c9a2952b1c8879a77354" integrity sha512-yQaiOqDmoKqks56LN9MTgY06O0qQHgV4FUrikH357DydArSZHQhl0BJFqGKIZoTqi8JizF9Dxhuk1FIZD6qCaw== -react-i18next@11.15.3: - version "11.15.3" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.15.3.tgz#7608fb3cacc02ac75a62fc2d68b579f140b198dd" - integrity sha512-RSUEM4So3Tu2JHV0JsZ5Yje+4nz66YViMfPZoywxOy0xyn3L7tE2CHvJ7Y9LUsrTU7vGmZ5bwb8PpjnkatdIxg== +react-i18next@11.15.4: + version "11.15.4" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.15.4.tgz#fe0c792ea93f038548e838cecae3ed4173822937" + integrity sha512-jKJNAcVcbPGK+yrTcXhLblgPY16n6NbpZZL3Mk8nswj1v3ayIiUBVDU09SgqnT+DluyQBS97hwSvPU5yVFG0yg== dependencies: "@babel/runtime" "^7.14.5" html-escaper "^2.0.2" @@ -6519,15 +6524,15 @@ react-native-iphone-x-helper@^1.3.1: resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010" integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg== -react-native-pager-view@5.4.9: - version "5.4.9" - resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-5.4.9.tgz#c0d40847cfeda5a4e729b53271b0ee0fedff3eb5" - integrity sha512-D6tzxpwMGdl6CXgtskGWhKRc5cJakCazESRGt7PkqnpyiH3N35ft1KmR82pCSQetAFlytFiToeu3a/dG5CELvA== +react-native-pager-view@5.4.11: + version "5.4.11" + resolved "https://registry.yarnpkg.com/react-native-pager-view/-/react-native-pager-view-5.4.11.tgz#677540293c7b4e0e022efb45727ef9b4efa35409" + integrity sha512-4QlBL5W8yVjeYwrw89oCdABI7sDxIGapFQvIbukfB5mAj1Zn1IQPkBqROLblNFtQ8PbAeexXRgDT1ENWygiJ7A== -react-native-reanimated@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.4.0.tgz#660860f3c9d36dc09e80e784306591c67060f940" - integrity sha512-KogpQaRxFu2jgIEWRwoHaTQ2TQbHva49rJFnXtEdMZ5LJefVMFITiEwRuyjlD4ZWpstFiaP0mWkR6uuS2APutA== +react-native-reanimated@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.4.1.tgz#4e33876fba525ce60ac13ab3c81fc3a9f8b132fe" + integrity sha512-kvf7ylGlwa5hxMQ+wpPFjQrI2c6eexf53/xRo+dvXBNefGmSYaYR5sFtD0XMMzIPQlkCB9tJ0Pu9+2WCQUY7Cg== dependencies: "@babel/plugin-transform-object-assign" "^7.10.4" "@types/invariant" "^2.2.35" @@ -6542,10 +6547,10 @@ react-native-safe-area-context@3.3.2: resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz#9549a2ce580f2374edb05e49d661258d1b8bcaed" integrity sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q== -react-native-screens@3.10.2: - version "3.10.2" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-3.10.2.tgz#cbf505d61c09e29ad5b335309951a3bd81f0df19" - integrity sha512-bMKSpwMeqAoXBqTJiDEG1ogM1cMk66sEmpp/4dGqdX59v+OwMqPeTuBk37qaSuS7gPOFFKsNW2X3ymGvBT4iEw== +react-native-screens@3.11.1: + version "3.11.1" + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-3.11.1.tgz#9bca9968986ca9195cb1e7e6fca37543bde64ecb" + integrity sha512-ziQqVm97tNtovacyHwNmDwJPb8n9CqwsfttXx2p5Hk7wUWemDcPAX0ZJ/nNnGMSq2p2QPhPjjUpr3qKXuES0sQ== dependencies: react-freeze "^1.0.0" warn-once "^0.1.0" @@ -6613,10 +6618,10 @@ react-native@0.66.4: whatwg-fetch "^3.0.0" ws "^6.1.4" -react-query@3.34.14: - version "3.34.14" - resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.34.14.tgz#ba944c1c36bbeab3e7037c42e8e862acd73111ff" - integrity sha512-KVMnM8omt+81oO9fPZfM65pGhQilpWzGsNwAqeeLMB2sG3xwY3bpIEYbhDf7FFgsqhAQfSzmCL4gRSiJaWIDwA== +react-query@3.34.15: + version "3.34.15" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.34.15.tgz#9e143b7d0aa39c515102a35120d5518ad91b10f6" + integrity sha512-dOhGLB5RT3p+wWj0rVdAompSg+R9t6oMRk+JhU8DP0tpJM2UyIv3r4Kk0zUkHSxT+QG34hFdrgdqxVWxgeNq4g== dependencies: "@babel/runtime" "^7.5.5" broadcast-channel "^3.4.1"