mirror of https://github.com/tooot-app/app
Add static emoji option
This commit is contained in:
parent
9c3e8f58b0
commit
dde33ad1ad
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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,24 +87,45 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
|
||||||
dispatch(countInstanceEmoji(emoji))
|
dispatch(countInstanceEmoji(emoji))
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FastImage
|
{staticEmoji ? (
|
||||||
accessibilityLabel={t(
|
<Image
|
||||||
'common:customEmoji.accessibilityLabel',
|
accessibilityLabel={t(
|
||||||
{
|
'common:customEmoji.accessibilityLabel',
|
||||||
emoji: emoji.shortcode
|
{
|
||||||
}
|
emoji: emoji.shortcode
|
||||||
)}
|
}
|
||||||
accessibilityHint={t(
|
)}
|
||||||
'screenCompose:content.root.footer.emojis.accessibilityHint'
|
accessibilityHint={t(
|
||||||
)}
|
'screenCompose:content.root.footer.emojis.accessibilityHint'
|
||||||
source={{ uri }}
|
)}
|
||||||
style={{
|
source={{ uri }}
|
||||||
width: 32,
|
style={{
|
||||||
height: 32,
|
width: 32,
|
||||||
padding: StyleConstants.Spacing.S,
|
height: 32,
|
||||||
margin: StyleConstants.Spacing.S
|
padding: StyleConstants.Spacing.S,
|
||||||
}}
|
margin: StyleConstants.Spacing.S
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FastImage
|
||||||
|
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
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -22,35 +22,39 @@ const SettingsAnalytics: React.FC = () => {
|
||||||
const instanceVersion = useSelector(getInstanceVersion, () => true)
|
const instanceVersion = useSelector(getInstanceVersion, () => true)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuContainer>
|
<>
|
||||||
<MenuRow
|
<MenuContainer>
|
||||||
title={t('me.settings.analytics.heading')}
|
<MenuRow
|
||||||
description={t('me.settings.analytics.description')}
|
title={t('me.settings.analytics.heading')}
|
||||||
switchValue={settingsAnalytics}
|
description={t('me.settings.analytics.description')}
|
||||||
switchOnValueChange={() =>
|
switchValue={settingsAnalytics}
|
||||||
dispatch(changeAnalytics(!settingsAnalytics))
|
switchOnValueChange={() =>
|
||||||
}
|
dispatch(changeAnalytics(!settingsAnalytics))
|
||||||
/>
|
}
|
||||||
<CustomText
|
/>
|
||||||
fontStyle='S'
|
</MenuContainer>
|
||||||
style={{
|
<MenuContainer>
|
||||||
textAlign: 'center',
|
<CustomText
|
||||||
marginTop: StyleConstants.Spacing.S,
|
fontStyle='S'
|
||||||
color: colors.secondary
|
style={{
|
||||||
}}
|
textAlign: 'center',
|
||||||
>
|
marginTop: StyleConstants.Spacing.S,
|
||||||
{t('me.settings.version', { version: Constants.manifest?.version })}
|
color: colors.secondary
|
||||||
</CustomText>
|
}}
|
||||||
<CustomText
|
>
|
||||||
fontStyle='S'
|
{t('me.settings.version', { version: Constants.manifest?.version })}
|
||||||
style={{
|
</CustomText>
|
||||||
textAlign: 'center',
|
<CustomText
|
||||||
color: colors.secondary
|
fontStyle='S'
|
||||||
}}
|
style={{
|
||||||
>
|
textAlign: 'center',
|
||||||
{t('me.settings.instanceVersion', { version: instanceVersion })}
|
color: colors.secondary
|
||||||
</CustomText>
|
}}
|
||||||
</MenuContainer>
|
>
|
||||||
|
{t('me.settings.instanceVersion', { version: instanceVersion })}
|
||||||
|
</CustomText>
|
||||||
|
</MenuContainer>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue