Add static emoji option

This commit is contained in:
Zhiyuan Zheng 2022-05-13 00:04:15 +02:00
parent 9c3e8f58b0
commit dde33ad1ad
9 changed files with 131 additions and 59 deletions

View File

@ -166,7 +166,7 @@ const styles = StyleSheet.create({
core: { core: {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
paddingVertical: StyleConstants.Spacing.S paddingTop: StyleConstants.Spacing.S
}, },
front: { front: {
flex: 2, flex: 2,

View File

@ -130,7 +130,9 @@ const Timeline: React.FC<Props> = ({
} }
ListEmptyComponent={<TimelineEmpty queryKey={queryKey} />} ListEmptyComponent={<TimelineEmpty queryKey={queryKey} />}
ItemSeparatorComponent={ItemSeparatorComponent} ItemSeparatorComponent={ItemSeparatorComponent}
maintainVisibleContentPosition={{ minIndexForVisible: 0 }} {...(isFetchingPreviousPage && {
maintainVisibleContentPosition: { minIndexForVisible: 0 }
})}
refreshing={isFetchingPreviousPage} refreshing={isFetchingPreviousPage}
onRefresh={() => { onRefresh={() => {
if (!disableRefresh && !isFetchingPreviousPage) { if (!disableRefresh && !isFetchingPreviousPage) {

View File

@ -251,6 +251,10 @@
"cancel": "$t(common:buttons.cancel)" "cancel": "$t(common:buttons.cancel)"
} }
}, },
"staticEmoji": {
"heading": "Use static emoji",
"description": "If you encounter frequent app crash when viewing emoji list, you can try to use static emoji instead."
},
"feedback": { "feedback": {
"heading": "Feature Requests" "heading": "Feature Requests"
}, },

View File

@ -3,6 +3,7 @@ import CustomText from '@components/Text'
import { useAppDispatch } from '@root/store' import { useAppDispatch } from '@root/store'
import { useAccessibility } from '@utils/accessibility/AccessibilityManager' import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
import { countInstanceEmoji } from '@utils/slices/instancesSlice' import { countInstanceEmoji } from '@utils/slices/instancesSlice'
import { getSettingsStaticEmoji } from '@utils/slices/settingsSlice'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React, { RefObject, useCallback, useContext, useEffect } from 'react' import React, { RefObject, useCallback, useContext, useEffect } from 'react'
@ -10,11 +11,13 @@ import { useTranslation } from 'react-i18next'
import { import {
AccessibilityInfo, AccessibilityInfo,
findNodeHandle, findNodeHandle,
Image,
Pressable, Pressable,
SectionList, SectionList,
View View
} from 'react-native' } from 'react-native'
import FastImage from 'react-native-fast-image' import FastImage from 'react-native-fast-image'
import { useSelector } from 'react-redux'
import validUrl from 'valid-url' import validUrl from 'valid-url'
import updateText from '../../updateText' import updateText from '../../updateText'
import ComposeContext from '../../utils/createContext' import ComposeContext from '../../utils/createContext'
@ -30,6 +33,8 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
const { t } = useTranslation() const { t } = useTranslation()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const staticEmoji = useSelector(getSettingsStaticEmoji)
useEffect(() => { useEffect(() => {
const tagEmojis = findNodeHandle(accessibleRefEmojis.current) const tagEmojis = findNodeHandle(accessibleRefEmojis.current)
if (composeState.emoji.active) { if (composeState.emoji.active) {
@ -82,6 +87,26 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
dispatch(countInstanceEmoji(emoji)) dispatch(countInstanceEmoji(emoji))
}} }}
> >
{staticEmoji ? (
<Image
accessibilityLabel={t(
'common:customEmoji.accessibilityLabel',
{
emoji: emoji.shortcode
}
)}
accessibilityHint={t(
'screenCompose:content.root.footer.emojis.accessibilityHint'
)}
source={{ uri }}
style={{
width: 32,
height: 32,
padding: StyleConstants.Spacing.S,
margin: StyleConstants.Spacing.S
}}
/>
) : (
<FastImage <FastImage
accessibilityLabel={t( accessibilityLabel={t(
'common:customEmoji.accessibilityLabel', 'common:customEmoji.accessibilityLabel',
@ -100,6 +125,7 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
margin: StyleConstants.Spacing.S margin: StyleConstants.Spacing.S
}} }}
/> />
)}
</Pressable> </Pressable>
) )
} else { } else {

View File

@ -22,6 +22,7 @@ const SettingsAnalytics: React.FC = () => {
const instanceVersion = useSelector(getInstanceVersion, () => true) const instanceVersion = useSelector(getInstanceVersion, () => true)
return ( return (
<>
<MenuContainer> <MenuContainer>
<MenuRow <MenuRow
title={t('me.settings.analytics.heading')} title={t('me.settings.analytics.heading')}
@ -31,6 +32,8 @@ const SettingsAnalytics: React.FC = () => {
dispatch(changeAnalytics(!settingsAnalytics)) dispatch(changeAnalytics(!settingsAnalytics))
} }
/> />
</MenuContainer>
<MenuContainer>
<CustomText <CustomText
fontStyle='S' fontStyle='S'
style={{ style={{
@ -51,6 +54,7 @@ const SettingsAnalytics: React.FC = () => {
{t('me.settings.instanceVersion', { version: instanceVersion })} {t('me.settings.instanceVersion', { version: instanceVersion })}
</CustomText> </CustomText>
</MenuContainer> </MenuContainer>
</>
) )
} }

View File

@ -15,7 +15,9 @@ import {
getSettingsBrowser, getSettingsBrowser,
getSettingsFontsize, getSettingsFontsize,
getSettingsDarkTheme, getSettingsDarkTheme,
changeDarkTheme changeDarkTheme,
getSettingsStaticEmoji,
changeStaticEmoji
} from '@utils/slices/settingsSlice' } from '@utils/slices/settingsSlice'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import * as Notifications from 'expo-notifications' import * as Notifications from 'expo-notifications'
@ -37,6 +39,7 @@ const SettingsApp: React.FC = () => {
const settingsTheme = useSelector(getSettingsTheme) const settingsTheme = useSelector(getSettingsTheme)
const settingsDarkTheme = useSelector(getSettingsDarkTheme) const settingsDarkTheme = useSelector(getSettingsDarkTheme)
const settingsBrowser = useSelector(getSettingsBrowser) const settingsBrowser = useSelector(getSettingsBrowser)
const settingsStaticEmoji = useSelector(getSettingsStaticEmoji)
return ( return (
<MenuContainer> <MenuContainer>
@ -266,6 +269,18 @@ const SettingsApp: React.FC = () => {
) )
} }
/> />
<MenuRow
title={t('me.settings.staticEmoji.heading')}
description={t('me.settings.staticEmoji.description')}
switchValue={settingsStaticEmoji}
switchOnValueChange={() => {
analytics('settings_staticemoji_press', {
current: settingsStaticEmoji.toString(),
new: !settingsStaticEmoji.toString()
})
dispatch(changeStaticEmoji(!settingsStaticEmoji))
}}
/>
</MenuContainer> </MenuContainer>
) )
} }

View File

@ -1,5 +1,6 @@
import { SettingsV0 } from './v0' import { SettingsV0 } from './v0'
import { SettingsV1 } from './v1' import { SettingsV1 } from './v1'
import { SettingsV2 } from './v2'
const settingsMigration = { const settingsMigration = {
1: (state: SettingsV0): SettingsV1 => { 1: (state: SettingsV0): SettingsV1 => {
@ -7,6 +8,13 @@ const settingsMigration = {
...state, ...state,
darkTheme: 'lighter' darkTheme: 'lighter'
} }
},
2: (state: SettingsV1): SettingsV2 => {
return {
...state,
darkTheme: 'lighter',
staticEmoji: false
}
} }
} }

View File

@ -0,0 +1,9 @@
export type SettingsV2 = {
fontsize: -1 | 0 | 1 | 2 | 3
language: string
theme: 'light' | 'dark' | 'auto'
darkTheme: 'lighter' | 'darker'
browser: 'internal' | 'external'
staticEmoji: boolean
analytics: boolean
}

View File

@ -1,6 +1,7 @@
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit' import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
import { LOCALES } from '@root/i18n/locales' import { LOCALES } from '@root/i18n/locales'
import { RootState } from '@root/store' import { RootState } from '@root/store'
import { SettingsV2 } from '@utils/migrations/settings/v2'
import * as Analytics from 'expo-firebase-analytics' import * as Analytics from 'expo-firebase-analytics'
import * as Localization from 'expo-localization' import * as Localization from 'expo-localization'
import { pickBy } from 'lodash' import { pickBy } from 'lodash'
@ -13,14 +14,7 @@ export const changeAnalytics = createAsyncThunk(
} }
) )
export type SettingsState = { export type SettingsState = SettingsV2
fontsize: -1 | 0 | 1 | 2 | 3
language: string
theme: 'light' | 'dark' | 'auto'
darkTheme: 'lighter' | 'darker'
browser: 'internal' | 'external'
analytics: boolean
}
export const settingsInitialState = { export const settingsInitialState = {
fontsize: 0, fontsize: 0,
@ -37,6 +31,7 @@ export const settingsInitialState = {
theme: 'auto', theme: 'auto',
darkTheme: 'lighter', darkTheme: 'lighter',
browser: 'internal', browser: 'internal',
staticEmoji: false,
analytics: true analytics: true
} }
@ -73,6 +68,12 @@ const settingsSlice = createSlice({
action: PayloadAction<NonNullable<SettingsState['browser']>> action: PayloadAction<NonNullable<SettingsState['browser']>>
) => { ) => {
state.browser = action.payload state.browser = action.payload
},
changeStaticEmoji: (
state,
action: PayloadAction<NonNullable<SettingsState['staticEmoji']>>
) => {
state.staticEmoji = action.payload
} }
}, },
extraReducers: builder => { extraReducers: builder => {
@ -89,6 +90,8 @@ export const getSettingsTheme = (state: RootState) => state.settings.theme
export const getSettingsDarkTheme = (state: RootState) => export const getSettingsDarkTheme = (state: RootState) =>
state.settings.darkTheme state.settings.darkTheme
export const getSettingsBrowser = (state: RootState) => state.settings.browser export const getSettingsBrowser = (state: RootState) => state.settings.browser
export const getSettingsStaticEmoji = (state: RootState) =>
state.settings.staticEmoji
export const getSettingsAnalytics = (state: RootState) => export const getSettingsAnalytics = (state: RootState) =>
state.settings.analytics state.settings.analytics
@ -97,6 +100,7 @@ export const {
changeLanguage, changeLanguage,
changeTheme, changeTheme,
changeDarkTheme, changeDarkTheme,
changeBrowser changeBrowser,
changeStaticEmoji
} = settingsSlice.actions } = settingsSlice.actions
export default settingsSlice.reducer export default settingsSlice.reducer