1
0
mirror of https://github.com/tooot-app/app synced 2025-02-20 13:50:49 +01:00

Merge pull request #229 from tooot-app/main

Test v3.5
This commit is contained in:
xmflsct 2022-02-13 22:21:12 +01:00 committed by GitHub
commit f873e12bcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
137 changed files with 1274 additions and 679 deletions

View File

@ -27,11 +27,9 @@ jobs:
token: ${{ secrets.EXPO_TOKEN }}
- name: -- Step 4 -- Install node dependencies
run: yarn install
- name: -- Step 5 -- Install native dependencies
run: npx pod-install
- name: -- Step 6 -- Install ruby dependencies
- name: -- Step 5 -- Install ruby dependencies
run: bundle install
- name: -- Step 7 -- Run fastlane
- name: -- Step 6 -- Run fastlane
env:
DEVELOPER_DIR: /Applications/Xcode_13.2.1.app/Contents/Developer
ENVIRONMENT: ${{ steps.branch.outputs.branch }}

View File

@ -17,17 +17,17 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.551.0)
aws-sdk-core (3.125.5)
aws-partitions (1.554.0)
aws-sdk-core (3.126.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.53.0)
aws-sdk-core (~> 3, >= 3.125.0)
aws-sdk-kms (1.54.0)
aws-sdk-core (~> 3, >= 3.126.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.111.3)
aws-sdk-core (~> 3, >= 3.125.0)
aws-sdk-s3 (1.112.0)
aws-sdk-core (~> 3, >= 3.126.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
@ -86,7 +86,7 @@ GEM
escape (0.0.4)
ethon (0.15.0)
ffi (>= 1.15.0)
excon (0.90.0)
excon (0.91.0)
faraday (1.9.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
@ -116,7 +116,7 @@ GEM
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.203.0)
fastlane (2.204.2)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@ -155,8 +155,8 @@ GEM
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-json (1.0.0)
fastlane-plugin-sentry (1.11.0)
fastlane-plugin-json (1.1.0)
fastlane-plugin-sentry (1.11.1)
fastlane-plugin-versioning_android (0.1.0)
fastlane-plugin-yarn (1.2)
ffi (1.15.4)

View File

@ -224,6 +224,7 @@ lane :build do
puts("Release #{GITHUB_RELEASE} exists. Continue with building React Native only.")
else
puts("Release #{GITHUB_RELEASE} does not exist. Create new release as well as new native build.")
cocoapods(clean_install: true, podfile: "./ios/Podfile", deployment: true)
build_ios
build_android
case ENVIRONMENT

View File

@ -14,6 +14,7 @@ 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'
@ -26,7 +27,6 @@ import { addScreenshotListener } from 'expo-screen-capture'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Alert, Platform, StatusBar } from 'react-native'
import { useQueryClient } from 'react-query'
import { useDispatch, useSelector } from 'react-redux'
import * as Sentry from 'sentry-expo'
@ -40,7 +40,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
const { t } = useTranslation('screens')
const dispatch = useDispatch()
const instanceActive = useSelector(getInstanceActive)
const { mode, theme } = useTheme()
const { colors, mode, theme } = useTheme()
enum barStyle {
light = 'dark-content',
dark = 'light-content'
@ -53,7 +53,6 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
getInstances,
(prev, next) => prev.length === next.length
)
const queryClient = useQueryClient()
pushUseConnect({ t, instances })
pushUseReceive({ instances })
pushUseRespond({ instances })
@ -77,8 +76,9 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
message: t('localCorrupt.message'),
description: localCorrupt.length ? localCorrupt : undefined,
type: 'error',
mode
theme
})
// @ts-ignore
navigationRef.navigate('Screen-Tabs', {
screen: 'Tab-Me'
})
@ -93,6 +93,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
dispatch(updateConfiguration())
dispatch(updateFilters())
dispatch(updateAccountPreferences())
dispatch(checkEmojis())
}
}, [instanceActive])
@ -164,11 +165,11 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
<>
<StatusBar
barStyle={barStyle[mode]}
backgroundColor={theme.backgroundDefault}
backgroundColor={colors.backgroundDefault}
/>
<NavigationContainer
ref={navigationRef}
theme={themes[mode]}
theme={themes[theme]}
onReady={navigationContainerOnReady}
onStateChange={navigationContainerOnStateChange}
>

View File

@ -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'
})

View File

@ -20,10 +20,9 @@ const ComponentAccount: React.FC<Props> = ({
onPress: customOnPress,
origin
}) => {
const { theme } = useTheme()
const navigation = useNavigation<
StackNavigationProp<TabLocalStackParamList>
>()
const { colors } = useTheme()
const navigation =
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
const onPress = useCallback(() => {
analytics('search_account_press', { page: origin })
@ -37,7 +36,7 @@ const ComponentAccount: React.FC<Props> = ({
onPress={customOnPress || onPress}
>
<GracefullyImage
uri={{ original: account.avatar_static }}
uri={{ original: account.avatar, static: account.avatar_static }}
style={styles.itemAccountAvatar}
/>
<View>
@ -51,7 +50,7 @@ const ComponentAccount: React.FC<Props> = ({
</Text>
<Text
numberOfLines={1}
style={[styles.itemAccountAcct, { color: theme.secondary }]}
style={[styles.itemAccountAcct, { color: colors.secondary }]}
>
@{account.acct}
</Text>

View File

@ -54,7 +54,7 @@ const Button: React.FC<Props> = ({
overlay = false,
onPress
}) => {
const { mode, theme } = useTheme()
const { colors, theme } = useTheme()
const mounted = useRef(false)
useEffect(() => {
@ -68,35 +68,35 @@ const Button: React.FC<Props> = ({
const loadingSpinkit = useMemo(
() => (
<View style={{ position: 'absolute' }}>
<Flow size={StyleConstants.Font.Size[size]} color={theme.secondary} />
<Flow size={StyleConstants.Font.Size[size]} color={colors.secondary} />
</View>
),
[mode]
[theme]
)
const mainColor = useMemo(() => {
if (selected) {
return theme.blue
return colors.blue
} else if (overlay) {
return theme.primaryOverlay
return colors.primaryOverlay
} else if (disabled || loading) {
return theme.disabled
return colors.disabled
} else {
if (destructive) {
return theme.red
return colors.red
} else {
return theme.primaryDefault
return colors.primaryDefault
}
}
}, [mode, disabled, loading, selected])
}, [theme, disabled, loading, selected])
const colorBackground = useMemo(() => {
if (overlay) {
return theme.backgroundOverlayInvert
return colors.backgroundOverlayInvert
} else {
return theme.backgroundDefault
return colors.backgroundDefault
}
}, [mode])
}, [theme])
const children = useMemo(() => {
switch (type) {
@ -130,7 +130,7 @@ const Button: React.FC<Props> = ({
</>
)
}
}, [mode, content, loading, disabled])
}, [theme, content, loading, disabled])
const [layoutHeight, setLayoutHeight] = useState<number | undefined>()

View File

@ -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<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>[][]
}[],
reduceMotionEnabled: boolean
) => {
const prefetches: { uri: string }[] = []
@ -101,14 +107,28 @@ const ComponentEmojis: React.FC<Props> = ({
[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<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>[][]
}[] = []
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

View File

@ -7,7 +7,7 @@ import EmojisContext from './helpers/EmojisContext'
const EmojisButton = React.memo(
() => {
const { theme } = useTheme()
const { colors } = useTheme()
const { emojisState, emojisDispatch } = useContext(EmojisContext)
return emojisState.enabled ? (
@ -30,8 +30,8 @@ const EmojisButton = React.memo(
size={StyleConstants.Font.Size.L}
color={
emojisState.emojis && emojisState.emojis.length
? theme.primaryDefault
: theme.disabled
? colors.primaryDefault
: colors.disabled
}
/>
}

View File

@ -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,20 +15,22 @@ 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()
const { emojisState, emojisDispatch } = useContext(EmojisContext)
const { theme } = useTheme()
const { colors } = useTheme()
const listHeader = useCallback(
({ section: { title } }) => (
<Text style={[styles.group, { color: theme.secondary }]}>{title}</Text>
<Text style={[styles.group, { color: colors.secondary }]}>{title}</Text>
),
[]
)
@ -42,12 +45,13 @@ const EmojisList = React.memo(
return (
<Pressable
key={emoji.shortcode}
onPress={() =>
onPress={() => {
emojisDispatch({
type: 'shortcode',
payload: `:${emoji.shortcode}:`
})
}
dispatch(countInstanceEmoji(emoji))
}}
>
<FastImage
accessibilityLabel={t(

View File

@ -3,7 +3,10 @@ import { createContext, Dispatch } from 'react'
export type EmojisState = {
enabled: boolean
active: boolean
emojis: { title: string; data: Mastodon.Emoji[][] }[]
emojis: {
title: string
data: Pick<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>[][]
}[]
shortcode: Mastodon.Emoji['shortcode'] | null
}

View File

@ -1,3 +1,4 @@
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useMemo, useState } from 'react'
import {
@ -23,7 +24,7 @@ export interface Props {
accessibilityHint?: AccessibilityProps['accessibilityHint']
hidden?: boolean
uri: { preview?: string; original?: string; remote?: string }
uri: { preview?: string; original?: string; remote?: string; static?: string }
blurhash?: string
dimension?: { width: number; height: number }
onPress?: () => void
@ -51,7 +52,8 @@ const GracefullyImage = React.memo(
imageStyle,
setImageDimensions
}: Props) => {
const { theme } = useTheme()
const { reduceMotionEnabled } = useAccessibility()
const { colors } = useTheme()
const [originalFailed, setOriginalFailed] = useState(false)
const [imageLoaded, setImageLoaded] = useState(false)
@ -59,7 +61,9 @@ const GracefullyImage = React.memo(
if (originalFailed) {
return { uri: uri.remote || undefined }
} else {
return { uri: uri.original }
return {
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
}
}
}, [originalFailed])
@ -85,7 +89,7 @@ const GracefullyImage = React.memo(
source={{ uri: uri.preview }}
style={[
styles.placeholder,
{ backgroundColor: theme.shimmerDefault }
{ backgroundColor: colors.shimmerDefault }
]}
/>
) : null,
@ -118,7 +122,7 @@ const GracefullyImage = React.memo(
<View
style={[
styles.placeholder,
{ backgroundColor: theme.shimmerDefault }
{ backgroundColor: colors.shimmerDefault }
]}
/>
)
@ -135,7 +139,7 @@ const GracefullyImage = React.memo(
: { accessibilityRole: 'image' })}
accessibilityLabel={accessibilityLabel}
accessibilityHint={accessibilityHint}
style={[style, dimension, { backgroundColor: theme.shimmerDefault }]}
style={[style, dimension, { backgroundColor: colors.shimmerDefault }]}
{...(onPress
? hidden
? { disabled: true }

View File

@ -17,10 +17,9 @@ const ComponentHashtag: React.FC<Props> = ({
onPress: customOnPress,
origin
}) => {
const { theme } = useTheme()
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const { colors } = useTheme()
const navigation =
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
const onPress = useCallback(() => {
analytics('search_account_press', { page: origin })
@ -33,7 +32,7 @@ const ComponentHashtag: React.FC<Props> = ({
style={styles.itemDefault}
onPress={customOnPress || onPress}
>
<Text style={[styles.itemHashtag, { color: theme.primaryDefault }]}>
<Text style={[styles.itemHashtag, { color: colors.primaryDefault }]}>
#{hashtag.name}
</Text>
</Pressable>

View File

@ -11,13 +11,13 @@ export interface Props {
// Used for Android mostly
const HeaderCenter = React.memo(
({ content, inverted = false }: Props) => {
const { theme } = useTheme()
const { colors } = useTheme()
return (
<Text
style={[
styles.text,
{ color: inverted ? theme.primaryOverlay : theme.primaryDefault }
{ color: inverted ? colors.primaryOverlay : colors.primaryDefault }
]}
children={content}
/>

View File

@ -20,14 +20,14 @@ const HeaderLeft: React.FC<Props> = ({
background = false,
onPress
}) => {
const { theme } = useTheme()
const { colors, theme } = useTheme()
const children = useMemo(() => {
switch (type) {
case 'icon':
return (
<Icon
color={theme.primaryDefault}
color={colors.primaryDefault}
name={content || 'ChevronLeft'}
size={StyleConstants.Spacing.M * 1.25}
/>
@ -35,7 +35,7 @@ const HeaderLeft: React.FC<Props> = ({
case 'text':
return (
<Text
style={[styles.text, { color: theme.primaryDefault }]}
style={[styles.text, { color: colors.primaryDefault }]}
children={content}
/>
)
@ -50,7 +50,7 @@ const HeaderLeft: React.FC<Props> = ({
styles.base,
{
backgroundColor: background
? theme.backgroundOverlayDefault
? colors.backgroundOverlayDefault
: undefined,
minHeight: 44,
minWidth: 44,

View File

@ -41,14 +41,14 @@ const HeaderRight: React.FC<Props> = ({
disabled,
onPress
}) => {
const { theme } = useTheme()
const { colors, theme } = useTheme()
const loadingSpinkit = useMemo(
() => (
<View style={{ position: 'absolute' }}>
<Flow
size={StyleConstants.Font.Size.M * 1.25}
color={theme.secondary}
color={colors.secondary}
/>
</View>
),
@ -64,7 +64,7 @@ const HeaderRight: React.FC<Props> = ({
name={content}
style={{ opacity: loading ? 0 : 1 }}
size={StyleConstants.Spacing.M * 1.25}
color={disabled ? theme.secondary : theme.primaryDefault}
color={disabled ? colors.secondary : colors.primaryDefault}
/>
{loading && loadingSpinkit}
</>
@ -76,7 +76,7 @@ const HeaderRight: React.FC<Props> = ({
style={[
styles.text,
{
color: disabled ? theme.secondary : theme.primaryDefault,
color: disabled ? colors.secondary : colors.primaryDefault,
opacity: loading ? 0 : 1
}
]}
@ -101,7 +101,7 @@ const HeaderRight: React.FC<Props> = ({
styles.base,
{
backgroundColor: background
? theme.backgroundOverlayDefault
? colors.backgroundOverlayDefault
: undefined,
minHeight: 44,
minWidth: 44,

View File

@ -57,7 +57,7 @@ const Input: React.FC<Props> = ({
setValue,
options
}) => {
const { mode, theme } = useTheme()
const { colors, mode } = useTheme()
const animateTitle = useAnimatedStyle(() => {
if (value) {
@ -66,7 +66,7 @@ const Input: React.FC<Props> = ({
paddingHorizontal: withTiming(StyleConstants.Spacing.XS),
left: withTiming(StyleConstants.Spacing.S),
top: withTiming(-(StyleConstants.Font.Size.S / 2) - 2),
backgroundColor: withTiming(theme.backgroundDefault)
backgroundColor: withTiming(colors.backgroundDefault)
}
} else {
return {
@ -74,7 +74,7 @@ const Input: React.FC<Props> = ({
paddingHorizontal: withTiming(0),
left: withTiming(StyleConstants.Spacing.S),
top: withTiming(StyleConstants.Spacing.S + 1),
backgroundColor: withTiming(theme.backgroundDefaultTransparent)
backgroundColor: withTiming(colors.backgroundDefaultTransparent)
}
}
}, [mode, value])
@ -109,7 +109,7 @@ const Input: React.FC<Props> = ({
style={[
styles.base,
{
borderColor: theme.border,
borderColor: colors.border,
flexDirection: multiline ? 'column' : 'row',
alignItems: 'stretch'
}
@ -127,7 +127,7 @@ const Input: React.FC<Props> = ({
style={[
styles.textInput,
{
color: theme.primaryDefault,
color: colors.primaryDefault,
minHeight:
Platform.OS === 'ios' && multiline
? StyleConstants.Font.LineHeight.M * 5
@ -149,14 +149,14 @@ const Input: React.FC<Props> = ({
</EmojisContext.Consumer>
{title ? (
<Animated.Text
style={[styles.title, animateTitle, { color: theme.secondary }]}
style={[styles.title, animateTitle, { color: colors.secondary }]}
>
{title}
</Animated.Text>
) : null}
<View style={{ flexDirection: 'row', alignSelf: 'flex-end' }}>
{options?.maxLength && value?.length ? (
<Text style={[styles.maxLength, { color: theme.secondary }]}>
<Text style={[styles.maxLength, { color: colors.secondary }]}>
{value?.length} / {options.maxLength}
</Text>
) : null}

View File

@ -39,7 +39,7 @@ const ComponentInstance: React.FC<Props> = ({
goBack = false
}) => {
const { t } = useTranslation('componentInstance')
const { mode, theme } = useTheme()
const { colors, mode } = useTheme()
const { screenReaderEnabled } = useAccessibility()
const instances = useSelector(getInstances, () => true)
@ -149,10 +149,10 @@ const ComponentInstance: React.FC<Props> = ({
style={[
styles.prefix,
{
color: theme.primaryDefault,
color: colors.primaryDefault,
borderBottomColor: instanceQuery.isError
? theme.red
: theme.border
? colors.red
: colors.border
}
]}
editable={false}
@ -162,10 +162,10 @@ const ComponentInstance: React.FC<Props> = ({
style={[
styles.textInput,
{
color: theme.primaryDefault,
color: colors.primaryDefault,
borderBottomColor: instanceQuery.isError
? theme.red
: theme.border
? colors.red
: colors.border
}
]}
onChangeText={onChangeText}
@ -175,7 +175,7 @@ const ComponentInstance: React.FC<Props> = ({
textContentType='URL'
onSubmitEditing={onSubmitEditing}
placeholder={' ' + t('server.textInput.placeholder')}
placeholderTextColor={theme.secondary}
placeholderTextColor={colors.secondary}
returnKeyType='go'
keyboardAppearance={mode}
{...(scrollViewRef && {
@ -234,11 +234,11 @@ const ComponentInstance: React.FC<Props> = ({
<Icon
name='Lock'
size={StyleConstants.Font.Size.S}
color={theme.secondary}
color={colors.secondary}
style={styles.disclaimerIcon}
/>
<Text
style={[styles.disclaimerText, { color: theme.secondary }]}
style={[styles.disclaimerText, { color: colors.secondary }]}
accessibilityRole='link'
onPress={() => {
if (screenReaderEnabled) {
@ -252,7 +252,7 @@ const ComponentInstance: React.FC<Props> = ({
{t('server.disclaimer.base')}
<Text
accessible
style={{ color: theme.blue }}
style={{ color: colors.blue }}
onPress={() => {
analytics('view_privacy')
WebBrowser.openBrowserAsync(

View File

@ -13,15 +13,15 @@ export interface Props {
const InstanceInfo = React.memo(
({ style, header, content, potentialWidth }: Props) => {
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View style={[styles.base, style]} accessible>
<Text style={[styles.header, { color: theme.primaryDefault }]}>
<Text style={[styles.header, { color: colors.primaryDefault }]}>
{header}
</Text>
{content ? (
<Text style={[styles.content, { color: theme.primaryDefault }]}>
<Text style={[styles.content, { color: colors.primaryDefault }]}>
{content}
</Text>
) : (
@ -32,7 +32,7 @@ const InstanceInfo = React.memo(
: undefined
}
height={StyleConstants.Font.LineHeight.M}
color={theme.shimmerDefault}
color={colors.shimmerDefault}
noMargin
style={{ borderRadius: 0 }}
/>

View File

@ -8,11 +8,11 @@ export interface Props {
}
const MenuHeader: React.FC<Props> = ({ heading }) => {
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View style={styles.base}>
<Text style={[styles.text, { color: theme.secondary }]}>{heading}</Text>
<Text style={[styles.text, { color: colors.secondary }]}>{heading}</Text>
</View>
)
}

View File

@ -43,7 +43,7 @@ const MenuRow: React.FC<Props> = ({
loading = false,
onPress
}) => {
const { theme } = useTheme()
const { colors, theme } = useTheme()
const { screenReaderEnabled } = useAccessibility()
const loadingSpinkit = useMemo(
@ -51,7 +51,7 @@ const MenuRow: React.FC<Props> = ({
<View style={{ position: 'absolute' }}>
<Flow
size={StyleConstants.Font.Size.M * 1.25}
color={theme.secondary}
color={colors.secondary}
/>
</View>
),
@ -83,7 +83,7 @@ const MenuRow: React.FC<Props> = ({
<Icon
name={iconFront}
size={StyleConstants.Font.Size.L}
color={theme[iconFrontColor]}
color={colors[iconFrontColor]}
style={styles.iconFront}
/>
)}
@ -92,7 +92,7 @@ const MenuRow: React.FC<Props> = ({
style={{
width: 8,
height: 8,
backgroundColor: theme.red,
backgroundColor: colors.red,
borderRadius: 8,
marginRight: StyleConstants.Spacing.S
}}
@ -100,7 +100,7 @@ const MenuRow: React.FC<Props> = ({
) : null}
<View style={styles.main}>
<Text
style={[styles.title, { color: theme.primaryDefault }]}
style={[styles.title, { color: colors.primaryDefault }]}
numberOfLines={1}
>
{title}
@ -116,7 +116,7 @@ const MenuRow: React.FC<Props> = ({
style={[
styles.content,
{
color: theme.secondary,
color: colors.secondary,
opacity: !iconBack && loading ? 0 : 1
}
]}
@ -133,7 +133,7 @@ const MenuRow: React.FC<Props> = ({
value={switchValue}
onValueChange={switchOnValueChange}
disabled={switchDisabled}
trackColor={{ true: theme.blue, false: theme.disabled }}
trackColor={{ true: colors.blue, false: colors.disabled }}
style={{ opacity: loading ? 0 : 1 }}
/>
) : null}
@ -141,7 +141,7 @@ const MenuRow: React.FC<Props> = ({
<Icon
name={iconBack}
size={StyleConstants.Font.Size.L}
color={theme[iconBackColor]}
color={colors[iconBackColor]}
style={[styles.iconBack, { opacity: loading ? 0 : 1 }]}
/>
) : null}
@ -150,7 +150,7 @@ const MenuRow: React.FC<Props> = ({
) : null}
</View>
{description ? (
<Text style={[styles.description, { color: theme.secondary }]}>
<Text style={[styles.description, { color: colors.secondary }]}>
{description}
</Text>
) : null}

View File

@ -1,7 +1,7 @@
import Icon from '@components/Icon'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import { getTheme } from '@utils/styles/themes'
import { getColors, Theme } from '@utils/styles/themes'
import React, { RefObject } from 'react'
import { AccessibilityInfo } from 'react-native'
import FlashMessage, {
@ -17,7 +17,7 @@ const displayMessage = ({
message,
description,
onPress,
mode,
theme,
type
}:
| {
@ -27,7 +27,7 @@ const displayMessage = ({
message: string
description?: string
onPress?: () => void
mode?: undefined
theme?: undefined
type?: undefined
}
| {
@ -37,7 +37,7 @@ const displayMessage = ({
message: string
description?: string
onPress?: () => void
mode: 'light' | 'dark'
theme: Theme
type: 'success' | 'error' | 'warning'
}) => {
AccessibilityInfo.announceForAccessibility(message + '.' + description)
@ -64,14 +64,14 @@ const displayMessage = ({
message,
description,
onPress,
...(mode &&
...(theme &&
type && {
renderFlashMessageIcon: () => {
return (
<Icon
name={iconMapping[type]}
size={StyleConstants.Font.LineHeight.M}
color={getTheme(mode)[colorMapping[type]]}
color={getColors(theme)[colorMapping[type]]}
style={{ marginRight: StyleConstants.Spacing.S }}
/>
)
@ -85,14 +85,14 @@ const displayMessage = ({
message,
description,
onPress,
...(mode &&
...(theme &&
type && {
renderFlashMessageIcon: () => {
return (
<Icon
name={iconMapping[type]}
size={StyleConstants.Font.LineHeight.M}
color={getTheme(mode)[colorMapping[type]]}
color={getColors(theme)[colorMapping[type]]}
style={{ marginRight: StyleConstants.Spacing.S }}
/>
)
@ -111,7 +111,7 @@ const removeMessage = () => {
}
const Message = React.forwardRef<FlashMessage>((_, ref) => {
const { mode, theme } = useTheme()
const { colors, theme } = useTheme()
return (
<FlashMessage
@ -120,19 +120,19 @@ const Message = React.forwardRef<FlashMessage>((_, ref) => {
position='top'
floating
style={{
backgroundColor: theme.backgroundDefault,
shadowColor: theme.primaryDefault,
backgroundColor: colors.backgroundDefault,
shadowColor: colors.primaryDefault,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: mode === 'light' ? 0.16 : 0.24,
shadowOpacity: theme === 'light' ? 0.16 : 0.24,
shadowRadius: 4
}}
titleStyle={{
color: theme.primaryDefault,
color: colors.primaryDefault,
...StyleConstants.FontStyle.M,
fontWeight: StyleConstants.Font.Weight.Bold
}}
textStyle={{
color: theme.primaryDefault,
color: colors.primaryDefault,
...StyleConstants.FontStyle.S
}}
// @ts-ignore

View File

@ -39,11 +39,11 @@ const ParseEmojis = React.memo(
adaptiveSize ? adaptiveFontsize : 0
)
const { mode, theme } = useTheme()
const { colors, theme } = useTheme()
const styles = useMemo(() => {
return StyleSheet.create({
text: {
color: theme.primaryDefault,
color: colors.primaryDefault,
fontSize: adaptedFontsize,
lineHeight: adaptedLineheight,
...(fontBold && { fontWeight: StyleConstants.Font.Weight.Bold })
@ -54,7 +54,7 @@ const ParseEmojis = React.memo(
transform: [{ translateY: -2 }]
}
})
}, [mode, adaptiveFontsize])
}, [theme, adaptiveFontsize])
return (
<Text style={styles.text}>

View File

@ -18,7 +18,7 @@ import { useSelector } from 'react-redux'
// Prevent going to the same hashtag multiple times
const renderNode = ({
routeParams,
theme,
colors,
node,
index,
adaptedFontsize,
@ -30,7 +30,7 @@ const renderNode = ({
disableDetails
}: {
routeParams?: any
theme: any
colors: any
node: any
index: number
adaptedFontsize: number
@ -56,7 +56,7 @@ const renderNode = ({
accessible
key={index}
style={{
color: theme.blue,
color: colors.blue,
fontSize: adaptedFontsize,
lineHeight: adaptedLineheight
}}
@ -84,7 +84,8 @@ const renderNode = ({
<Text
key={index}
style={{
color: accountIndex !== -1 ? theme.blue : theme.primaryDefault,
color:
accountIndex !== -1 ? colors.blue : colors.primaryDefault,
fontSize: adaptedFontsize,
lineHeight: adaptedLineheight
}}
@ -114,7 +115,7 @@ const renderNode = ({
<Text
key={index}
style={{
color: theme.blue,
color: colors.blue,
alignItems: 'center',
fontSize: adaptedFontsize,
lineHeight: adaptedLineheight
@ -132,7 +133,7 @@ const renderNode = ({
(showFullLink ? href : domain[1])}
{!shouldBeTag ? (
<Icon
color={theme.blue}
color={colors.blue}
name='ExternalLink'
size={adaptedFontsize}
style={{
@ -192,11 +193,10 @@ const ParseHTML = React.memo(
adaptiveSize ? adaptiveFontsize : 0
)
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const navigation =
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
const route = useRoute()
const { mode, theme } = useTheme()
const { colors, theme } = useTheme()
const { t, i18n } = useTranslation('componentParse')
if (!expandHint) {
expandHint = t('HTML.defaultHint')
@ -206,7 +206,7 @@ const ParseHTML = React.memo(
(node, index) =>
renderNode({
routeParams: route.params,
theme,
colors,
node,
index,
adaptedFontsize,
@ -271,14 +271,14 @@ const ParseHTML = React.memo(
justifyContent: 'center',
marginTop: expanded ? 0 : -adaptedLineheight,
minHeight: 44,
backgroundColor: theme.backgroundDefault
backgroundColor: colors.backgroundDefault
}}
>
<Text
style={{
textAlign: 'center',
...StyleConstants.FontStyle.S,
color: theme.primaryDefault
color: colors.primaryDefault
}}
children={t(`HTML.expanded.${expanded.toString()}`, {
hint: expandHint
@ -289,7 +289,7 @@ const ParseHTML = React.memo(
</View>
)
},
[mode, i18n.language]
[theme, i18n.language]
)
return (

View File

@ -19,7 +19,7 @@ export interface Props {
}
const RelationshipIncoming: React.FC<Props> = ({ id }) => {
const { mode } = useTheme()
const { theme } = useTheme()
const { t } = useTranslation()
const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }]
@ -40,7 +40,7 @@ const RelationshipIncoming: React.FC<Props> = ({ id }) => {
haptics('Error')
displayMessage({
type: 'error',
mode,
theme,
message: t('common:message.error.message', {
function: t(`relationship:${type}.function`)
}),

View File

@ -19,7 +19,7 @@ export interface Props {
const RelationshipOutgoing = React.memo(
({ id }: Props) => {
const { mode } = useTheme()
const { theme } = useTheme()
const { t } = useTranslation('componentRelationship')
const query = useRelationshipQuery({ id })
@ -40,7 +40,7 @@ const RelationshipOutgoing = React.memo(
},
onError: (err: any, { payload: { action } }) => {
displayMessage({
mode,
theme,
type: 'error',
message: t('common:message.error.message', {
function: t(`${action}.function`)

View File

@ -10,13 +10,13 @@ export interface Props {
const ComponentSeparator = React.memo(
({ extraMarginLeft = 0, extraMarginRight = 0 }: Props) => {
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View
style={{
backgroundColor: theme.backgroundDefault,
borderTopColor: theme.border,
backgroundColor: colors.backgroundDefault,
borderTopColor: colors.border,
borderTopWidth: StyleSheet.hairlineWidth,
marginLeft:
StyleConstants.Spacing.Global.PagePadding + extraMarginLeft,

View File

@ -48,7 +48,7 @@ const Timeline: React.FC<Props> = ({
lookback,
customProps
}) => {
const { theme } = useTheme()
const { colors } = useTheme()
const {
data,
@ -118,8 +118,8 @@ const Timeline: React.FC<Props> = ({
refreshControl: (
<RefreshControl
enabled
colors={[theme.primaryDefault]}
progressBackgroundColor={theme.backgroundDefault}
colors={[colors.primaryDefault]}
progressBackgroundColor={colors.backgroundDefault}
refreshing={isFetching || isLoading}
onRefresh={() => refetch()}
/>

View File

@ -32,7 +32,7 @@ const Avatars: React.FC<{ accounts: Mastodon.Account[] }> = ({ accounts }) => {
{accounts.slice(0, 4).map(account => (
<GracefullyImage
key={account.id}
uri={{ original: account.avatar_static }}
uri={{ original: account.avatar, static: account.avatar_static }}
dimension={{
width: StyleConstants.Avatar.M,
height:
@ -62,7 +62,7 @@ const TimelineConversation: React.FC<Props> = ({
getInstanceAccount,
(prev, next) => prev?.id === next?.id
)
const { theme } = useTheme()
const { colors } = useTheme()
const queryClient = useQueryClient()
const fireMutation = useCallback(() => {
@ -77,9 +77,8 @@ const TimelineConversation: React.FC<Props> = ({
}
})
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const navigation =
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
const onPress = useCallback(() => {
analytics('timeline_conversation_press')
if (conversation.last_status) {
@ -95,10 +94,10 @@ const TimelineConversation: React.FC<Props> = ({
<Pressable
style={[
styles.base,
{ backgroundColor: theme.backgroundDefault },
{ backgroundColor: colors.backgroundDefault },
conversation.unread && {
borderLeftWidth: StyleConstants.Spacing.XS,
borderLeftColor: theme.blue,
borderLeftColor: colors.blue,
paddingLeft:
StyleConstants.Spacing.Global.PagePadding -
StyleConstants.Spacing.XS

View File

@ -42,11 +42,10 @@ const TimelineDefault: React.FC<Props> = ({
disableDetails = false,
disableOnPress = false
}) => {
const { theme } = useTheme()
const { colors } = useTheme()
const instanceAccount = useSelector(getInstanceAccount, () => true)
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const navigation =
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
const actualStatus = item.reblog ? item.reblog : item
@ -78,7 +77,7 @@ const TimelineDefault: React.FC<Props> = ({
style={[
styles.statusView,
{
backgroundColor: theme.backgroundDefault,
backgroundColor: colors.backgroundDefault,
paddingBottom:
disableDetails && disableOnPress
? StyleConstants.Spacing.Global.PagePadding

View File

@ -20,14 +20,17 @@ const TimelineEmpty = React.memo(
options: { notifyOnChangeProps: ['status'] }
})
const { mode, theme } = useTheme()
const { colors, theme } = useTheme()
const { t, i18n } = useTranslation('componentTimeline')
const children = useMemo(() => {
switch (status) {
case 'loading':
return (
<Circle size={StyleConstants.Font.Size.L} color={theme.secondary} />
<Circle
size={StyleConstants.Font.Size.L}
color={colors.secondary}
/>
)
case 'error':
return (
@ -35,9 +38,9 @@ const TimelineEmpty = React.memo(
<Icon
name='Frown'
size={StyleConstants.Font.Size.L}
color={theme.primaryDefault}
color={colors.primaryDefault}
/>
<Text style={[styles.error, { color: theme.primaryDefault }]}>
<Text style={[styles.error, { color: colors.primaryDefault }]}>
{t('empty.error.message')}
</Text>
<Button
@ -56,18 +59,18 @@ const TimelineEmpty = React.memo(
<Icon
name='Smartphone'
size={StyleConstants.Font.Size.L}
color={theme.primaryDefault}
color={colors.primaryDefault}
/>
<Text style={[styles.error, { color: theme.primaryDefault }]}>
<Text style={[styles.error, { color: colors.primaryDefault }]}>
{t('empty.success.message')}
</Text>
</>
)
}
}, [mode, i18n.language, status])
}, [theme, i18n.language, status])
return (
<View
style={[styles.base, { backgroundColor: theme.backgroundDefault }]}
style={[styles.base, { backgroundColor: colors.backgroundDefault }]}
children={children}
/>
)

View File

@ -24,21 +24,21 @@ const TimelineFooter = React.memo(
}
})
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View style={styles.base}>
{!disableInfinity && hasNextPage ? (
<Circle size={StyleConstants.Font.Size.L} color={theme.secondary} />
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
) : (
<Text style={[styles.text, { color: theme.secondary }]}>
<Text style={[styles.text, { color: colors.secondary }]}>
<Trans
i18nKey='componentTimeline:end.message'
components={[
<Icon
name='Coffee'
size={StyleConstants.Font.Size.S}
color={theme.secondary}
color={colors.secondary}
/>
]}
/>

View File

@ -7,12 +7,14 @@ import { StyleSheet, Text, View } from 'react-native'
const TimelineLookback = React.memo(
() => {
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View style={[styles.base, { backgroundColor: theme.backgroundDefault }]}>
<View
style={[styles.base, { backgroundColor: colors.backgroundDefault }]}
>
<Text
style={[StyleConstants.FontStyle.S, { color: theme.primaryDefault }]}
style={[StyleConstants.FontStyle.S, { color: colors.primaryDefault }]}
>
{t('lookback.message')}
</Text>

View File

@ -38,14 +38,13 @@ const TimelineNotifications: React.FC<Props> = ({
return <TimelineFiltered />
}
const { theme } = useTheme()
const { colors } = useTheme()
const instanceAccount = useSelector(
getInstanceAccount,
(prev, next) => prev?.id === next?.id
)
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const navigation =
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
const actualAccount = notification.status
? notification.status.account
@ -65,7 +64,7 @@ const TimelineNotifications: React.FC<Props> = ({
style={[
styles.notificationView,
{
backgroundColor: theme.backgroundDefault,
backgroundColor: colors.backgroundDefault,
paddingBottom: notification.status
? 0
: StyleConstants.Spacing.Global.PagePadding
@ -148,8 +147,10 @@ const TimelineNotifications: React.FC<Props> = ({
status={notification.status}
highlighted={highlighted}
accts={uniqBy(
([notification.status.account] as Mastodon.Account[] &
Mastodon.Mention[])
(
[notification.status.account] as Mastodon.Account[] &
Mastodon.Mention[]
)
.concat(notification.status.mentions)
.filter(d => d?.id !== instanceAccount?.id),
d => d?.id

View File

@ -101,7 +101,7 @@ const TimelineRefresh: React.FC<Props> = ({
})
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
const queryClient = useQueryClient()
const clearFirstPage = () => {
@ -254,13 +254,16 @@ const TimelineRefresh: React.FC<Props> = ({
<View style={styles.base}>
{isFetching ? (
<View style={styles.container2}>
<Circle size={StyleConstants.Font.Size.L} color={theme.secondary} />
<Circle
size={StyleConstants.Font.Size.L}
color={colors.secondary}
/>
</View>
) : (
<>
<View style={styles.container1}>
<Text
style={[styles.explanation, { color: theme.primaryDefault }]}
style={[styles.explanation, { color: colors.primaryDefault }]}
onLayout={onLayout}
children={t('refresh.fetchPreviousPage')}
/>
@ -277,14 +280,14 @@ const TimelineRefresh: React.FC<Props> = ({
<Icon
name='ArrowLeft'
size={StyleConstants.Font.Size.M}
color={theme.primaryDefault}
color={colors.primaryDefault}
/>
}
/>
</View>
<View style={styles.container2}>
<Text
style={[styles.explanation, { color: theme.primaryDefault }]}
style={[styles.explanation, { color: colors.primaryDefault }]}
onLayout={onLayout}
children={t('refresh.refetch')}
/>

View File

@ -18,12 +18,11 @@ export interface Props {
const TimelineActioned = React.memo(
({ account, action, notification = false }: Props) => {
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const { colors } = useTheme()
const navigation =
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
const name = account.display_name || account.username
const iconColor = theme.primaryDefault
const iconColor = colors.primaryDefault
const content = (content: string) => (
<ParseEmojis content={content} emojis={account.emojis} size='S' />

View File

@ -33,8 +33,8 @@ const TimelineActions: React.FC<Props> = ({
}) => {
const navigation = useNavigation()
const { t } = useTranslation('componentTimeline')
const { mode, theme } = useTheme()
const iconColor = theme.secondary
const { colors, theme } = useTheme()
const iconColor = colors.secondary
const queryClient = useQueryClient()
const mutation = useTimelineMutation({
@ -83,7 +83,7 @@ const TimelineActions: React.FC<Props> = ({
onError: (err: any, params, oldData) => {
const correctParam = params as MutationVarsTimelineUpdateStatusProperty
displayMessage({
mode,
theme,
type: 'error',
message: t('common:message.error.message', {
function: t(
@ -185,7 +185,7 @@ const TimelineActions: React.FC<Props> = ({
{status.replies_count > 0 ? (
<Text
style={{
color: theme.secondary,
color: colors.secondary,
fontSize: StyleConstants.Font.Size.M,
marginLeft: StyleConstants.Spacing.XS
}}
@ -198,14 +198,14 @@ const TimelineActions: React.FC<Props> = ({
[status.replies_count]
)
const childrenReblog = useMemo(() => {
const color = (state: boolean) => (state ? theme.green : theme.secondary)
const color = (state: boolean) => (state ? colors.green : colors.secondary)
return (
<>
<Icon
name='Repeat'
color={
status.visibility === 'private' || status.visibility === 'direct'
? theme.disabled
? colors.disabled
: color(status.reblogged)
}
size={StyleConstants.Font.Size.L}
@ -225,7 +225,7 @@ const TimelineActions: React.FC<Props> = ({
)
}, [status.reblogged, status.reblogs_count])
const childrenFavourite = useMemo(() => {
const color = (state: boolean) => (state ? theme.red : theme.secondary)
const color = (state: boolean) => (state ? colors.red : colors.secondary)
return (
<>
<Icon
@ -249,7 +249,7 @@ const TimelineActions: React.FC<Props> = ({
)
}, [status.favourited, status.favourites_count])
const childrenBookmark = useMemo(() => {
const color = (state: boolean) => (state ? theme.yellow : theme.secondary)
const color = (state: boolean) => (state ? colors.yellow : colors.secondary)
return (
<Icon
name='Bookmark'

View File

@ -19,10 +19,9 @@ const TimelineActionsUsers = React.memo(
}
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const { colors } = useTheme()
const navigation =
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
return (
<View style={styles.base}>
@ -38,7 +37,7 @@ const TimelineActionsUsers = React.memo(
'shared.actionsUsers.reblogged_by.accessibilityHint'
)}
accessibilityRole='button'
style={[styles.text, { color: theme.blue }]}
style={[styles.text, { color: colors.blue }]}
onPress={() => {
analytics('timeline_shared_actionsusers_press_boosted', {
count: status.reblogs_count
@ -68,7 +67,7 @@ const TimelineActionsUsers = React.memo(
'shared.actionsUsers.favourited_by.accessibilityHint'
)}
accessibilityRole='button'
style={[styles.text, { color: theme.blue }]}
style={[styles.text, { color: colors.blue }]}
onPress={() => {
analytics('timeline_shared_actionsusers_press_boosted', {
count: status.favourites_count

View File

@ -23,7 +23,7 @@ const AttachmentAudio: React.FC<Props> = ({
sensitiveShown,
audio
}) => {
const { theme } = useTheme()
const { colors } = useTheme()
const [audioPlayer, setAudioPlayer] = useState<Audio.Sound>()
const [audioPlaying, setAudioPlaying] = useState(false)
@ -56,7 +56,7 @@ const AttachmentAudio: React.FC<Props> = ({
style={[
styles.base,
{
backgroundColor: theme.disabled,
backgroundColor: colors.disabled,
aspectRatio: attachmentAspectRatio({ total, index })
}
]}
@ -102,7 +102,7 @@ const AttachmentAudio: React.FC<Props> = ({
alignSelf: 'flex-end',
width: '100%',
height: StyleConstants.Spacing.M + StyleConstants.Spacing.S * 2,
backgroundColor: theme.backgroundOverlayInvert,
backgroundColor: colors.backgroundOverlayInvert,
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
borderRadius: 100,
opacity: sensitiveShown ? 0.35 : undefined
@ -112,8 +112,8 @@ const AttachmentAudio: React.FC<Props> = ({
minimumValue={0}
maximumValue={audio.meta.original.duration * 1000}
value={audioPosition}
minimumTrackTintColor={theme.secondary}
maximumTrackTintColor={theme.disabled}
minimumTrackTintColor={colors.secondary}
maximumTrackTintColor={colors.disabled}
// onSlidingStart={() => {
// audioPlayer?.pauseAsync()
// setAudioPlaying(false)
@ -123,7 +123,7 @@ const AttachmentAudio: React.FC<Props> = ({
// }}
enabled={false} // Bug in above sliding actions
thumbSize={StyleConstants.Spacing.M}
thumbTintColor={theme.primaryOverlay}
thumbTintColor={colors.primaryOverlay}
/>
</View>
) : null}

View File

@ -28,7 +28,16 @@ const AttachmentImage = React.memo(
uri={{ original: image.preview_url, remote: image.remote_url }}
blurhash={image.blurhash}
onPress={onPress}
style={[{ aspectRatio: attachmentAspectRatio({ total, index }) }]}
style={{
aspectRatio:
total > 1 ||
!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.width / image.meta.original.height
}}
/>
</View>
)

View File

@ -23,7 +23,7 @@ const AttachmentUnsupported: React.FC<Props> = ({
attachment
}) => {
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View
@ -49,8 +49,8 @@ const AttachmentUnsupported: React.FC<Props> = ({
styles.text,
{
color: attachment.blurhash
? theme.backgroundDefault
: theme.primaryDefault
? colors.backgroundDefault
: colors.primaryDefault
}
]}
>

View File

@ -16,9 +16,8 @@ export interface Props {
const TimelineAvatar = React.memo(
({ queryKey, account, highlighted }: Props) => {
const { t } = useTranslation('componentTimeline')
const navigation = useNavigation<
StackNavigationProp<Nav.TabLocalStackParamList>
>()
const navigation =
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
// Need to fix go back root
const onPress = useCallback(() => {
analytics('timeline_shared_avatar_press', {
@ -38,7 +37,7 @@ const TimelineAvatar = React.memo(
})
})}
onPress={onPress}
uri={{ original: account.avatar_static }}
uri={{ original: account.avatar, static: account.avatar_static }}
dimension={{
width: StyleConstants.Avatar.M,
height: StyleConstants.Avatar.M

View File

@ -13,14 +13,14 @@ export interface Props {
const TimelineCard = React.memo(
({ card }: Props) => {
const { theme } = useTheme()
const { colors } = useTheme()
const navigation = useNavigation()
return (
<Pressable
accessible
accessibilityRole='link'
style={[styles.card, { borderColor: theme.border }]}
style={[styles.card, { borderColor: colors.border }]}
onPress={async () => {
analytics('timeline_shared_card_press')
await openLink(card.url, navigation)
@ -38,7 +38,7 @@ const TimelineCard = React.memo(
<View style={styles.right}>
<Text
numberOfLines={2}
style={[styles.rightTitle, { color: theme.primaryDefault }]}
style={[styles.rightTitle, { color: colors.primaryDefault }]}
testID='title'
>
{card.title}
@ -46,7 +46,10 @@ const TimelineCard = React.memo(
{card.description ? (
<Text
numberOfLines={1}
style={[styles.rightDescription, { color: theme.primaryDefault }]}
style={[
styles.rightDescription,
{ color: colors.primaryDefault }
]}
testID='description'
>
{card.description}
@ -54,7 +57,7 @@ const TimelineCard = React.memo(
) : null}
<Text
numberOfLines={1}
style={[styles.rightLink, { color: theme.secondary }]}
style={[styles.rightLink, { color: colors.secondary }]}
>
{card.url}
</Text>

View File

@ -10,15 +10,15 @@ import { Text, View } from 'react-native'
const TimelineFiltered = React.memo(
() => {
const { theme } = useTheme()
const { colors } = useTheme()
const { t } = useTranslation('componentTimeline')
return (
<View style={{ backgroundColor: theme.backgroundDefault }}>
<View style={{ backgroundColor: colors.backgroundDefault }}>
<Text
style={{
...StyleConstants.FontStyle.S,
color: theme.secondary,
color: colors.secondary,
textAlign: 'center',
paddingVertical: StyleConstants.Spacing.S,
paddingLeft: StyleConstants.Avatar.M + StyleConstants.Spacing.S
@ -46,7 +46,7 @@ export const shouldFilter = ({
let shouldFilter = false
if (!ownAccount) {
const parser = new htmlparser2.Parser({
ontext (text: string) {
ontext: (text: string) => {
const checkFilter = (filter: Mastodon.Filter) => {
const escapedPhrase = filter.phrase.replace(
/[.*+?^${}()|[\]\\]/g,
@ -54,7 +54,7 @@ export const shouldFilter = ({
) // $& means the whole matched string
switch (filter.whole_word) {
case true:
if (new RegExp('\\b' + escapedPhrase + '\\b').test(text)) {
if (new RegExp('\\B' + escapedPhrase + '\\B').test(text)) {
shouldFilter = true
}
break

View File

@ -13,7 +13,7 @@ export interface Props {
const TimelineFullConversation = React.memo(
({ queryKey, status }: Props) => {
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
return queryKey &&
queryKey[1].page !== 'Toot' &&
@ -25,7 +25,7 @@ const TimelineFullConversation = React.memo(
<Text
style={{
...StyleConstants.FontStyle.S,
color: theme.blue,
color: colors.blue,
marginTop: StyleConstants.Spacing.S
}}
>

View File

@ -18,12 +18,12 @@ import HeaderSharedMuted from './HeaderShared/Muted'
const Names = React.memo(
({ accounts }: { accounts: Mastodon.Account[] }) => {
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
return (
<Text
numberOfLines={1}
style={[styles.namesLeading, { color: theme.secondary }]}
style={[styles.namesLeading, { color: colors.secondary }]}
>
<Text>{t('shared.header.conversation.withAccounts')}</Text>
{accounts.map((account, index) => (
@ -49,7 +49,7 @@ export interface Props {
const HeaderConversation = React.memo(
({ queryKey, conversation }: Props) => {
const { mode } = useTheme()
const { colors, theme } = useTheme()
const { t } = useTranslation('componentTimeline')
const queryClient = useQueryClient()
@ -57,7 +57,7 @@ const HeaderConversation = React.memo(
onMutate: true,
onError: (err: any, _, oldData) => {
displayMessage({
mode,
theme,
type: 'error',
message: t('common:message.error.message', {
function: t(`shared.header.conversation.delete.function`)
@ -74,8 +74,6 @@ const HeaderConversation = React.memo(
}
})
const { theme } = useTheme()
const actionOnPress = useCallback(() => {
analytics('timeline_conversation_delete_press')
mutation.mutate({
@ -90,7 +88,7 @@ const HeaderConversation = React.memo(
() => (
<Icon
name='Trash'
color={theme.secondary}
color={colors.secondary}
size={StyleConstants.Font.Size.L}
/>
),

View File

@ -22,7 +22,7 @@ const TimelineHeaderDefault = React.memo(
({ queryKey, rootQueryKey, status }: Props) => {
const { t } = useTranslation('componentTimeline')
const navigation = useNavigation()
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View style={styles.base}>
@ -52,7 +52,7 @@ const TimelineHeaderDefault = React.memo(
children={
<Icon
name='MoreHorizontal'
color={theme.secondary}
color={colors.secondary}
size={StyleConstants.Font.Size.L}
/>
}

View File

@ -23,7 +23,7 @@ export interface Props {
const TimelineHeaderNotification = React.memo(
({ queryKey, notification }: Props) => {
const navigation = useNavigation()
const { theme } = useTheme()
const { colors } = useTheme()
const actions = useMemo(() => {
switch (notification.type) {
@ -52,7 +52,7 @@ const TimelineHeaderNotification = React.memo(
children={
<Icon
name='MoreHorizontal'
color={theme.secondary}
color={colors.secondary}
size={StyleConstants.Font.Size.L}
/>
}

View File

@ -13,7 +13,7 @@ export interface Props {
const HeaderSharedAccount = React.memo(
({ account, withoutName = false }: Props) => {
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View style={styles.base}>
@ -36,7 +36,7 @@ const HeaderSharedAccount = React.memo(
accessibilityHint={t(
'shared.header.shared.account.account.accessibilityHint'
)}
style={[styles.acct, { color: theme.secondary }]}
style={[styles.acct, { color: colors.secondary }]}
numberOfLines={1}
>
@{account.acct}

View File

@ -12,7 +12,7 @@ export interface Props {
const HeaderSharedApplication = React.memo(
({ application }: Props) => {
const { theme } = useTheme()
const { colors } = useTheme()
const { t } = useTranslation('componentTimeline')
return application && application.name !== 'Web' ? (
@ -24,7 +24,7 @@ const HeaderSharedApplication = React.memo(
})
application.website && (await openLink(application.website))
}}
style={[styles.application, { color: theme.secondary }]}
style={[styles.application, { color: colors.secondary }]}
numberOfLines={1}
>
{t('shared.header.shared.application', {

View File

@ -10,10 +10,10 @@ export interface Props {
const HeaderSharedCreated = React.memo(
({ created_at }: Props) => {
const { theme } = useTheme()
const { colors } = useTheme()
return (
<Text style={[styles.created_at, { color: theme.secondary }]}>
<Text style={[styles.created_at, { color: colors.secondary }]}>
<RelativeTime date={created_at} />
</Text>
)

View File

@ -12,14 +12,14 @@ export interface Props {
const HeaderSharedMuted = React.memo(
({ muted }: Props) => {
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
return muted ? (
<Icon
accessibilityLabel={t('shared.header.shared.muted.accessibilityLabel')}
name='VolumeX'
size={StyleConstants.Font.Size.S}
color={theme.secondary}
color={colors.secondary}
style={styles.visibility}
/>
) : null

View File

@ -12,7 +12,7 @@ export interface Props {
const HeaderSharedVisibility = React.memo(
({ visibility }: Props) => {
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
switch (visibility) {
case 'private':
@ -23,7 +23,7 @@ const HeaderSharedVisibility = React.memo(
)}
name='Lock'
size={StyleConstants.Font.Size.S}
color={theme.secondary}
color={colors.secondary}
style={styles.visibility}
/>
)
@ -35,7 +35,7 @@ const HeaderSharedVisibility = React.memo(
)}
name='Mail'
size={StyleConstants.Font.Size.S}
color={theme.secondary}
color={colors.secondary}
style={styles.visibility}
/>
)

View File

@ -36,7 +36,7 @@ const TimelinePoll: React.FC<Props> = ({
reblog,
sameAccount
}) => {
const { mode, theme } = useTheme()
const { colors, theme } = useTheme()
const { t, i18n } = useTranslation('componentTimeline')
const [allOptions, setAllOptions] = useState(
@ -53,7 +53,7 @@ const TimelinePoll: React.FC<Props> = ({
haptics('Success')
switch (theParams.payload.property) {
case 'poll':
theParams.payload.data = (body as unknown) as Mastodon.Poll
theParams.payload.data = body as unknown as Mastodon.Poll
updateStatusProperty(theParams)
break
}
@ -61,7 +61,7 @@ const TimelinePoll: React.FC<Props> = ({
onError: (err: any, params) => {
const theParams = params as MutationVarsTimelineUpdateStatusProperty
displayMessage({
mode,
theme,
type: 'error',
message: t('common:message.error.message', {
// @ts-ignore
@ -136,7 +136,7 @@ const TimelinePoll: React.FC<Props> = ({
}
}
}, [
mode,
theme,
i18n.language,
poll.expired,
poll.voted,
@ -147,14 +147,14 @@ const TimelinePoll: React.FC<Props> = ({
const pollExpiration = useMemo(() => {
if (poll.expired) {
return (
<Text style={[styles.expiration, { color: theme.secondary }]}>
<Text style={[styles.expiration, { color: colors.secondary }]}>
{t('shared.poll.meta.expiration.expired')}
</Text>
)
} else {
if (poll.expires_at) {
return (
<Text style={[styles.expiration, { color: theme.secondary }]}>
<Text style={[styles.expiration, { color: colors.secondary }]}>
<Trans
i18nKey='componentTimeline:shared.poll.meta.expiration.until'
components={[<RelativeTime date={poll.expires_at} />]}
@ -163,7 +163,7 @@ const TimelinePoll: React.FC<Props> = ({
)
}
}
}, [mode, i18n.language, poll.expired, poll.expires_at])
}, [theme, i18n.language, poll.expired, poll.expires_at])
const isSelected = useCallback(
(index: number): string =>
@ -174,8 +174,10 @@ const TimelinePoll: React.FC<Props> = ({
)
const pollBodyDisallow = useMemo(() => {
const maxValue = maxBy(poll.options, option => option.votes_count)
?.votes_count
const maxValue = maxBy(
poll.options,
option => option.votes_count
)?.votes_count
return poll.options.map((option, index) => (
<View key={index} style={styles.optionContainer}>
<View style={styles.optionContent}>
@ -188,14 +190,14 @@ const TimelinePoll: React.FC<Props> = ({
}
size={StyleConstants.Font.Size.M}
color={
poll.own_votes?.includes(index) ? theme.blue : theme.disabled
poll.own_votes?.includes(index) ? colors.blue : colors.disabled
}
/>
<Text style={styles.optionText}>
<ParseEmojis content={option.title} emojis={poll.emojis} />
</Text>
<Text
style={[styles.optionPercentage, { color: theme.primaryDefault }]}
style={[styles.optionPercentage, { color: colors.primaryDefault }]}
>
{poll.votes_count
? Math.round(
@ -217,13 +219,13 @@ const TimelinePoll: React.FC<Props> = ({
100
)}%`,
backgroundColor:
option.votes_count === maxValue ? theme.blue : theme.disabled
option.votes_count === maxValue ? colors.blue : colors.disabled
}
]}
/>
</View>
))
}, [mode, poll.options])
}, [theme, poll.options])
const pollBodyAllow = useMemo(() => {
return poll.options.map((option, index) => (
<Pressable
@ -256,7 +258,7 @@ const TimelinePoll: React.FC<Props> = ({
style={styles.optionSelection}
name={isSelected(index)}
size={StyleConstants.Font.Size.M}
color={theme.primaryDefault}
color={colors.primaryDefault}
/>
<Text style={styles.optionText}>
<ParseEmojis content={option.title} emojis={poll.emojis} />
@ -264,19 +266,19 @@ const TimelinePoll: React.FC<Props> = ({
</View>
</Pressable>
))
}, [mode, allOptions])
}, [theme, allOptions])
const pollVoteCounts = useMemo(() => {
if (poll.voters_count !== null) {
return (
<Text style={[styles.votes, { color: theme.secondary }]}>
<Text style={[styles.votes, { color: colors.secondary }]}>
{t('shared.poll.meta.count.voters', { count: poll.voters_count })}
{' • '}
</Text>
)
} else if (poll.votes_count !== null) {
return (
<Text style={[styles.votes, { color: theme.secondary }]}>
<Text style={[styles.votes, { color: colors.secondary }]}>
{t('shared.poll.meta.count.votes', { count: poll.votes_count })}
{' • '}
</Text>

View File

@ -26,7 +26,7 @@ const TimelineTranslate = React.memo(
}
const { t } = useTranslation('componentTimeline')
const { theme } = useTheme()
const { colors } = useTheme()
const tootLanguage = status.language.slice(0, 2)
@ -82,10 +82,10 @@ const TimelineTranslate = React.memo(
...StyleConstants.FontStyle.M,
color:
isLoading || isSuccess
? theme.secondary
? colors.secondary
: isError
? theme.red
: theme.blue
? colors.red
: colors.blue
}}
>
{isError
@ -109,7 +109,7 @@ const TimelineTranslate = React.memo(
{isLoading ? (
<Circle
size={StyleConstants.Font.Size.M}
color={theme.disabled}
color={colors.disabled}
style={{ marginLeft: StyleConstants.Spacing.S }}
/>
) : null}

View File

@ -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'),

View File

@ -0,0 +1,3 @@
{
"frequentUsed": "Frequent used"
}

View File

@ -232,6 +232,14 @@
"cancel": "$t(common:buttons.cancel)"
}
},
"darkTheme": {
"heading": "Dark theme",
"options": {
"lighter": "Lighter",
"darker": "Darker",
"cancel": "$t(common:buttons.cancel)"
}
},
"browser": {
"heading": "Opening Link",
"options": {
@ -240,6 +248,9 @@
"cancel": "$t(common:buttons.cancel)"
}
},
"feedback": {
"heading": "Feature Requests"
},
"support": {
"heading": "Support tooot"
},

View File

@ -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'),

View File

@ -0,0 +1 @@
{}

View File

@ -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'),

View File

@ -0,0 +1 @@
{}

View File

@ -1,8 +1,8 @@
{
"HTML": {
"expanded": {
"true": "Thu gọn {{hint}}",
"false": "Mở rộng {{hint}}"
"true": "Cuộn {{hint}}",
"false": "Mở {{hint}}"
},
"defaultHint": "Tút"
}

View File

@ -2,7 +2,7 @@
"strings": {
"prefixAgo": "",
"prefixFromNow": "",
"suffixAgo": "trước",
"suffixAgo": " trước",
"suffixFromNow": "",
"seconds": "%d giây",
"minute": "một phút",

View File

@ -27,7 +27,7 @@
"follow_request": "{{name}} yêu cầu theo dõi bạn",
"poll": "Cuộc bình chọn đã kết thúc",
"reblog": {
"default": "{{name}} đã chia sẻ",
"default": "{{name}} chia sẻ",
"notification": "{{name}} chia sẻ tút của bạn"
}
},
@ -62,7 +62,7 @@
},
"attachment": {
"sensitive": {
"button": "Hiện nội dung nhạy cảm"
"button": "NSFW"
},
"unsupported": {
"text": "Không hỗ trợ định dạng",
@ -74,14 +74,16 @@
"accessibilityHint": "Đến trang cá nhân {{name}}"
},
"content": {
"expandHint": "nội dung bị ẩn"
"expandHint": "nội dung ẩn"
},
"filtered": "Đã lọc",
"fullConversation": "Xem thêm",
"translate": {
"default": "Dịch",
"succeed": "Dịch bằng {{provider}} từ {{source}}",
"failed": "Không thể dịch!"
"failed": "Không thể dịch!",
"source_not_supported": "không hỗ trợ ngôn ngữ tút",
"target_not_supported": "Ngôn ngữ đích không hỗ trợ"
},
"header": {
"shared": {
@ -93,7 +95,7 @@
"accessibilityHint": "Tài khoản"
}
},
"application": "Đăng bằng {{application}}",
"application": "via {{application}}",
"muted": {
"accessibilityLabel": "Đã ẩn tút"
},
@ -107,7 +109,7 @@
}
},
"conversation": {
"withAccounts": "Với",
"withAccounts": "Với ",
"delete": {
"function": "Xóa nhắn riêng"
}
@ -146,16 +148,16 @@
},
"share": {
"status": {
"heading": "Tút",
"heading": "Chia sẻ",
"button": "Chia sẻ URL tút"
},
"account": {
"heading": "Chia sẻ người dùng",
"heading": "Chia sẻ",
"button": "Chia sẻ URL người dùng này"
}
},
"status": {
"heading": "Về tút",
"heading": "Đối với tút",
"delete": {
"function": "Xóa tút",
"button": "Xóa tút này"

View File

@ -5,7 +5,7 @@
"cancel": "$t(common:buttons.cancel)"
},
"notificationsFilter": {
"heading": "Hiện những dạng thông báo",
"heading": "Những kiểu thông báo cho phép",
"content": {
"follow": "$t(screenTabs:me.push.follow.heading)",
"favourite": "$t(screenTabs:me.push.favourite.heading)",

View File

@ -13,10 +13,10 @@
},
"right": {
"button": {
"default": "Tút",
"default": "Đăng",
"conversation": "Tin nhắn",
"reply": "Lượt trả lời",
"edit": "Tút"
"reply": "Trả lời",
"edit": "Sửa"
},
"alert": {
"default": {
@ -63,7 +63,7 @@
"option": {
"placeholder": {
"accessibilityLabel": "Tùy chọn bình chọn {{index}}",
"single": "Chỉ được chọn một",
"single": "Lựa chọn",
"multiple": "Nhiều lựa chọn"
}
},
@ -117,7 +117,7 @@
},
"visibility": {
"accessibilityLabel": "Tút thuộc dạng {{visibility}}",
"title": "Dạng tút",
"title": "Kiểu tút",
"options": {
"public": "Công khai",
"unlisted": "Hạn chế",

View File

@ -53,7 +53,7 @@
"name": "Thông báo đẩy"
},
"profile": {
"name": "Trang cá nhân"
"name": "Cài đặt cá nhân"
},
"profileName": {
"name": "Tên hiển thị mới"
@ -65,7 +65,7 @@
"name": "Metadata"
},
"settings": {
"name": "Cài đặt App"
"name": "Thiết lập"
},
"switch": {
"name": "Chuyển đổi tài khoản"
@ -102,22 +102,22 @@
},
"avatar": {
"title": "Ảnh đại diện",
"description": "Sẽ tự động chuyển còn 400x400px"
"description": "Tự động nén xuống 400x400px"
},
"header": {
"title": "Ảnh bìa",
"description": "Sẽ tự động chuyển còn 1500x1500px"
"description": "Tự động nén xuống 1500x500px"
},
"note": {
"title": "Mô tả"
},
"fields": {
"title": "Metadata",
"total": "{{count}} trường",
"total_plural": "{{count}} trường"
"total": "{{count}} mục",
"total_plural": "{{count}} mục"
},
"visibility": {
"title": "Dạng tút",
"title": "Kiểu tút mặc định",
"options": {
"public": "Công khai",
"unlisted": "Hạn chế",
@ -126,19 +126,19 @@
}
},
"sensitive": {
"title": ăng nội dung nhạy cảm"
"title": ây là tài khoản NSFW"
},
"lock": {
"title": "Đây là tài khoản riêng tư",
"description": "Tự bạn sẽ phê duyệt người theo dõi"
},
"bot": {
"title": "Tài khoản bot",
"title": "Đây là tài khoản Bot",
"description": "Tài khoản này tự động thực hiện các hành động và không được quản lý bởi người thật"
}
},
"fields": {
"group": "Nhóm {{index}}",
"group": "Mục {{index}}",
"label": "Nhãn",
"content": "Nội dung"
}
@ -150,11 +150,11 @@
"settings": "Bật trong cài đặt"
},
"global": {
"heading": "Bật khi {{acct}}",
"heading": "Bật cho {{acct}}",
"description": "Thông báo được truyền qua máy chủ tooot"
},
"decode": {
"heading": "Chi tiết thông báo",
"heading": "Hiện chi tiết thông báo",
"description": "Theo mặc định, thông báo truyền qua máy chủ tooot sẽ được mã hóa, nhưng bạn cũng có thể chọn không mã hóa. Máy chủ của chúng tôi luôn công khai mã nguồn và không lưu lại bất cứ gì."
},
"default": {
@ -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": {
@ -240,6 +248,9 @@
"cancel": "$t(common:buttons.cancel)"
}
},
"feedback": {
"heading": "Yêu cầu tính năng"
},
"support": {
"heading": "Ủng hộ tooot"
},
@ -250,13 +261,13 @@
"heading": "Liên hệ tooot"
},
"analytics": {
"heading": "Đóng góp ý kiến",
"description": "Chỉ thu thập thông tin không liên quan người dùng"
"heading": "Thu thập dữ liệu",
"description": "Giúp cải thiện chất lượng app"
},
"version": "Phiên bản {{version}}"
},
"switch": {
"existing": "Chọn từ màn hình đăng nhập",
"existing": "Đã đăng nhập trước đó",
"new": "Đăng nhập máy chủ"
}
},
@ -279,15 +290,15 @@
}
},
"attachments": {
"name": "<0 /><1>'s media</1>"
"name": "Bộ sưu tập của <1></1>"
},
"search": {
"header": {
"prefix": "Đang tìm...",
"placeholder": "về..."
"prefix": "Tìm kiếm",
"placeholder": "tất cả mọi thứ"
},
"empty": {
"general": "Nhập từ khóa <bold>$t(screenTabs:shared.search.sections.accounts)</bold>、<bold>$t(screenTabs:shared.search.sections.hashtags)</bold> hoặc<bold>$t(screenTabs:shared.search.sections.statuses)</bold>",
"general": "Tìm <bold>$t(screenTabs:shared.search.sections.accounts)</bold>, <bold>$t(screenTabs:shared.search.sections.hashtags)</bold> hoặc <bold>$t(screenTabs:shared.search.sections.statuses)</bold><bold>",
"advanced": {
"header": "Tìm kiếm nâng cao",
"example": {
@ -310,12 +321,12 @@
},
"users": {
"accounts": {
"following": "Đang theo dõi {{count}}",
"following": "{{count}} theo dõi",
"followers": "{{count}} người theo dõi"
},
"statuses": {
"reblogged_by": "{{count}} người chia sẻ",
"favourited_by": "{{count}} người thích"
"reblogged_by": "{{count}} chia sẻ",
"favourited_by": "{{count}} thích"
}
}
}

View File

@ -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'),

View File

@ -0,0 +1,3 @@
{
"frequentUsed": "常用"
}

View File

@ -232,6 +232,14 @@
"cancel": "$t(common:buttons.cancel)"
}
},
"darkTheme": {
"heading": "深色主题",
"options": {
"lighter": "暗淡",
"darker": "暗黑",
"cancel": "$t(common:buttons.cancel)"
}
},
"browser": {
"heading": "外部链接",
"options": {
@ -240,6 +248,9 @@
"cancel": "$t(common:buttons.cancel)"
}
},
"feedback": {
"heading": "功能建议"
},
"support": {
"heading": "赞助 tooot 开发"
},

View File

@ -68,7 +68,7 @@ const ScreenActions = React.memo(
break
}
const { theme } = useTheme()
const { colors } = useTheme()
const insets = useSafeAreaInsets()
const DEFAULT_VALUE = 350
@ -189,7 +189,7 @@ const ScreenActions = React.memo(
<Animated.View
style={[
styles.overlay,
{ backgroundColor: theme.backgroundOverlayInvert }
{ backgroundColor: colors.backgroundOverlayInvert }
]}
>
<PanGestureHandler onGestureEvent={onGestureEvent}>
@ -198,7 +198,7 @@ const ScreenActions = React.memo(
styles.container,
styleTop,
{
backgroundColor: theme.backgroundDefault,
backgroundColor: colors.backgroundDefault,
paddingBottom: insets.bottom || StyleConstants.Spacing.L
}
]}
@ -206,7 +206,7 @@ const ScreenActions = React.memo(
<View
style={[
styles.handle,
{ backgroundColor: theme.primaryOverlay }
{ backgroundColor: colors.primaryOverlay }
]}
/>
{actions}

View File

@ -24,7 +24,7 @@ const ActionsAccount: React.FC<Props> = ({
account,
dismiss
}) => {
const { mode } = useTheme()
const { theme } = useTheme()
const { t } = useTranslation('componentTimeline')
const queryClient = useQueryClient()
@ -32,7 +32,7 @@ const ActionsAccount: React.FC<Props> = ({
onSuccess: (_, params) => {
const theParams = params as MutationVarsTimelineUpdateAccountProperty
displayMessage({
mode,
theme,
type: 'success',
message: t('common:message.success.message', {
function: t(
@ -47,7 +47,7 @@ const ActionsAccount: React.FC<Props> = ({
onError: (err: any, params) => {
const theParams = params as MutationVarsTimelineUpdateAccountProperty
displayMessage({
mode,
theme,
type: 'error',
message: t('common:message.error.message', {
function: t(

View File

@ -26,13 +26,13 @@ const ActionsDomain: React.FC<Props> = ({
domain,
dismiss
}) => {
const { mode } = useTheme()
const { theme } = useTheme()
const { t } = useTranslation('componentTimeline')
const queryClient = useQueryClient()
const mutation = useTimelineMutation({
onSettled: () => {
displayMessage({
mode,
theme,
type: 'success',
message: t('common:message.success.message', {
function: t(`shared.header.actions.domain.block.function`)

View File

@ -30,7 +30,7 @@ const ActionsStatus: React.FC<Props> = ({
status,
dismiss
}) => {
const { mode } = useTheme()
const { theme } = useTheme()
const { t } = useTranslation('componentTimeline')
const queryClient = useQueryClient()
@ -42,7 +42,7 @@ const ActionsStatus: React.FC<Props> = ({
? (params as MutationVarsTimelineUpdateStatusProperty).payload.property
: 'delete'
displayMessage({
mode,
theme,
type: 'error',
message: t('common:message.error.message', {
function: t(`shared.header.actions.status.${theFunction}.function`)

View File

@ -36,7 +36,7 @@ const ScreenAnnouncements: React.FC<
navigation
}) => {
const { reduceMotionEnabled } = useAccessibility()
const { mode, theme } = useTheme()
const { colors, mode } = useTheme()
const [index, setIndex] = useState(0)
const { t } = useTranslation('screenAnnouncements')
@ -73,12 +73,12 @@ const ScreenAnnouncements: React.FC<
style={[
styles.announcement,
{
borderColor: theme.primaryDefault,
backgroundColor: theme.backgroundDefault
borderColor: colors.primaryDefault,
backgroundColor: colors.backgroundDefault
}
]}
>
<Text style={[styles.published, { color: theme.secondary }]}>
<Text style={[styles.published, { color: colors.secondary }]}>
<Trans
i18nKey='screenAnnouncements:content.published'
components={[<RelativeTime date={item.published_at} />]}
@ -103,11 +103,11 @@ const ScreenAnnouncements: React.FC<
styles.reaction,
{
borderColor: reaction.me
? theme.disabled
: theme.primaryDefault,
? colors.disabled
: colors.primaryDefault,
backgroundColor: reaction.me
? theme.disabled
: theme.backgroundDefault
? colors.disabled
: colors.backgroundDefault
}
]}
onPress={() => {
@ -138,7 +138,7 @@ const ScreenAnnouncements: React.FC<
<Text
style={[
styles.reactionCount,
{ color: theme.primaryDefault }
{ color: colors.primaryDefault }
]}
>
{reaction.count}
@ -147,13 +147,13 @@ const ScreenAnnouncements: React.FC<
</Pressable>
))}
{/* <Pressable
style={[styles.reaction, { borderColor: theme.primaryDefault }]}
style={[styles.reaction, { borderColor: colors.primaryDefault }]}
onPress={() => invisibleTextInputRef.current?.focus()}
>
<Icon
name='Plus'
size={StyleConstants.Font.Size.M}
color={theme.primaryDefault}
color={colors.primaryDefault}
/>
</Pressable> */}
</View>
@ -177,7 +177,7 @@ const ScreenAnnouncements: React.FC<
</View>
</View>
),
[theme]
[mode]
)
const onMomentumScrollEnd = useCallback(
@ -201,7 +201,7 @@ const ScreenAnnouncements: React.FC<
alignItems: 'center'
}}
>
<Circle size={StyleConstants.Font.Size.L} color={theme.secondary} />
<Circle size={StyleConstants.Font.Size.L} color={colors.secondary} />
</View>
)
}, [])
@ -211,7 +211,7 @@ const ScreenAnnouncements: React.FC<
blurType={mode}
blurAmount={20}
style={styles.base}
reducedTransparencyFallbackColor={theme.backgroundDefault}
reducedTransparencyFallbackColor={colors.backgroundDefault}
>
<SafeAreaView style={styles.base}>
<FlatList
@ -232,9 +232,9 @@ const ScreenAnnouncements: React.FC<
style={[
styles.indicator,
{
borderColor: theme.primaryDefault,
borderColor: colors.primaryDefault,
backgroundColor:
i === index ? theme.primaryDefault : undefined,
i === index ? colors.primaryDefault : undefined,
marginLeft:
i === query.data.length ? 0 : StyleConstants.Spacing.S
}
@ -248,7 +248,7 @@ const ScreenAnnouncements: React.FC<
</BlurView>
) : (
<SafeAreaView
style={[styles.base, { backgroundColor: theme.backgroundDefault }]}
style={[styles.base, { backgroundColor: colors.backgroundDefault }]}
>
<FlatList
horizontal
@ -268,9 +268,9 @@ const ScreenAnnouncements: React.FC<
style={[
styles.indicator,
{
borderColor: theme.primaryDefault,
borderColor: colors.primaryDefault,
backgroundColor:
i === index ? theme.primaryDefault : undefined,
i === index ? colors.primaryDefault : undefined,
marginLeft:
i === query.data.length ? 0 : StyleConstants.Spacing.S
}

View File

@ -50,7 +50,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
navigation
}) => {
const { t } = useTranslation('screenCompose')
const { theme } = useTheme()
const { colors } = useTheme()
const queryClient = useQueryClient()
const [hasKeyboard, setHasKeyboard] = useState(false)
@ -373,7 +373,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
component={ComposeRoot}
options={{
title: headerContent,
titleStyle: {
headerTitleStyle: {
fontWeight:
totalTextCount > maxTootChars
? StyleConstants.Font.Weight.Bold
@ -381,7 +381,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
fontSize: StyleConstants.Font.Size.M
},
headerTintColor:
totalTextCount > maxTootChars ? theme.red : theme.secondary,
totalTextCount > maxTootChars ? colors.red : colors.secondary,
headerLeft,
headerRight
}}

View File

@ -37,7 +37,7 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
const { t } = useTranslation('screenCompose')
const navigation = useNavigation()
const dispatch = useDispatch()
const { mode, theme } = useTheme()
const { colors, theme } = useTheme()
const instanceDrafts = useSelector(getInstanceDrafts)?.filter(
draft => draft.timestamp !== timestamp
)
@ -56,7 +56,7 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
return (
<Pressable
accessibilityHint={t('content.draftsList.content.accessibilityHint')}
style={[styles.draft, { backgroundColor: theme.backgroundDefault }]}
style={[styles.draft, { backgroundColor: colors.backgroundDefault }]}
onPress={async () => {
setCheckingAttachments(true)
let tempDraft = item
@ -104,7 +104,7 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
<HeaderSharedCreated created_at={item.timestamp} />
<Text
numberOfLines={2}
style={[styles.text, { color: theme.primaryDefault }]}
style={[styles.text, { color: colors.primaryDefault }]}
>
{item.text ||
item.spoiler ||
@ -132,12 +132,12 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
</Pressable>
)
},
[mode]
[theme]
)
const renderHiddenItem = useCallback(
({ item }) => (
<View
style={[styles.hiddenBase, { backgroundColor: theme.red }]}
style={[styles.hiddenBase, { backgroundColor: colors.red }]}
children={
<Pressable
style={styles.action}
@ -146,14 +146,14 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
<Icon
name='Trash'
size={StyleConstants.Font.Size.L}
color={theme.primaryOverlay}
color={colors.primaryOverlay}
/>
}
/>
}
/>
),
[mode]
[theme]
)
return (
@ -184,14 +184,14 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
<View
style={[
styles.modal,
{ backgroundColor: theme.backgroundOverlayInvert }
{ backgroundColor: colors.backgroundOverlayInvert }
]}
children={
<Text
children='检查附件在服务器的状态…'
style={{
...StyleConstants.FontStyle.M,
color: theme.primaryOverlay
color: colors.primaryOverlay
}}
/>
}

View File

@ -22,7 +22,7 @@ export interface Props {
const ComposeEditAttachmentImage: React.FC<Props> = ({ index }) => {
const { t } = useTranslation('screenCompose')
const { theme } = useTheme()
const { colors } = useTheme()
const { screenReaderEnabled } = useAccessibility()
const { composeState, composeDispatch } = useContext(ComposeContext)
@ -146,16 +146,21 @@ const ComposeEditAttachmentImage: React.FC<Props> = ({ index }) => {
<G>
<Path
d='M1000,0 L1000,1000 L0,1000 L0,0 L1000,0 Z M500,475 C486.192881,475 475,486.192881 475,500 C475,513.807119 486.192881,525 500,525 C513.807119,525 525,513.807119 525,500 C525,486.192881 513.807119,475 500,475 Z'
fill={theme.backgroundOverlayInvert}
fill={colors.backgroundOverlayInvert}
/>
<Circle
stroke={theme.primaryOverlay}
stroke={colors.primaryOverlay}
stroke-width='2'
cx='500'
cy='500'
r='24'
/>
<Circle fill={theme.primaryOverlay} cx='500' cy='500' r='2' />
<Circle
fill={colors.primaryOverlay}
cx='500'
cy='500'
r='2'
/>
</G>
</G>
</Svg>
@ -163,7 +168,7 @@ const ComposeEditAttachmentImage: React.FC<Props> = ({ index }) => {
</PanGestureHandler>
</View>
{screenReaderEnabled ? null : (
<Text style={[styles.imageFocusText, { color: theme.primaryDefault }]}>
<Text style={[styles.imageFocusText, { color: colors.primaryDefault }]}>
{t('content.editAttachment.content.imageFocus')}
</Text>
)}

View File

@ -13,7 +13,7 @@ export interface Props {
const ComposeEditAttachmentRoot: React.FC<Props> = ({ index }) => {
const { t } = useTranslation('screenCompose')
const { mode, theme } = useTheme()
const { colors, mode } = useTheme()
const { composeState, composeDispatch } = useContext(ComposeContext)
const theAttachment = composeState.attachments.uploads[index].remote!
@ -61,13 +61,15 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({ index }) => {
<ScrollView ref={scrollViewRef}>
{mediaDisplay}
<View style={styles.altTextContainer}>
<Text style={[styles.altTextInputHeading, { color: theme.primaryDefault }]}>
<Text
style={[styles.altTextInputHeading, { color: colors.primaryDefault }]}
>
{t('content.editAttachment.content.altText.heading')}
</Text>
<TextInput
style={[
styles.altTextInput,
{ borderColor: theme.border, color: theme.primaryDefault }
{ borderColor: colors.border, color: colors.primaryDefault }
]}
onFocus={() => scrollViewRef.current?.scrollToEnd()}
autoCapitalize='none'
@ -76,12 +78,12 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({ index }) => {
multiline
onChangeText={onChangeText}
placeholder={t('content.editAttachment.content.altText.placeholder')}
placeholderTextColor={theme.secondary}
placeholderTextColor={colors.secondary}
scrollEnabled
value={theAttachment.description}
keyboardAppearance={mode}
/>
<Text style={[styles.altTextLength, { color: theme.secondary }]}>
<Text style={[styles.altTextLength, { color: colors.secondary }]}>
{theAttachment.description?.length || 0} / 1500
</Text>
</View>

View File

@ -6,7 +6,7 @@ import ComposeContext from './utils/createContext'
const ComposePosting = React.memo(
() => {
const { composeState } = useContext(ComposeContext)
const { theme } = useTheme()
const { colors } = useTheme()
return (
<Modal
@ -14,7 +14,9 @@ const ComposePosting = React.memo(
animationType='fade'
visible={composeState.posting}
children={
<View style={{ flex: 1, backgroundColor: theme.backgroundOverlayInvert }} />
<View
style={{ flex: 1, backgroundColor: colors.backgroundOverlayInvert }}
/>
}
/>
)

View File

@ -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<ComposeState['emoji']['emojis']>,
@ -61,7 +65,7 @@ export let instanceConfigurationStatusCharsURL = 23
const ComposeRoot = React.memo(
() => {
const { reduceMotionEnabled } = useAccessibility()
const { theme } = useTheme()
const { colors } = useTheme()
instanceConfigurationStatusCharsURL = useSelector(
getInstanceConfigurationStatusCharsURL,
@ -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<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>[][]
}[] = []
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 }
@ -122,7 +140,7 @@ const ComposeRoot = React.memo(
<View key='listEmpty' style={styles.loading}>
<Circle
size={StyleConstants.Font.Size.M * 1.25}
color={theme.secondary}
color={colors.secondary}
/>
</View>
)

View File

@ -16,19 +16,19 @@ const ComposeActions: React.FC = () => {
const { showActionSheetWithOptions } = useActionSheet()
const { composeState, composeDispatch } = useContext(ComposeContext)
const { t } = useTranslation('screenCompose')
const { theme } = useTheme()
const { colors, mode } = useTheme()
const instanceConfigurationStatusMaxAttachments = useSelector(
getInstanceConfigurationStatusMaxAttachments,
() => true
)
const attachmentColor = useMemo(() => {
if (composeState.poll.active) return theme.disabled
if (composeState.poll.active) return colors.disabled
if (composeState.attachments.uploads.length) {
return theme.primaryDefault
return colors.primaryDefault
} else {
return theme.secondary
return colors.secondary
}
}, [composeState.poll.active, composeState.attachments.uploads])
const attachmentOnPress = useCallback(async () => {
@ -49,12 +49,12 @@ const ComposeActions: React.FC = () => {
}, [composeState.poll.active, composeState.attachments.uploads])
const pollColor = useMemo(() => {
if (composeState.attachments.uploads.length) return theme.disabled
if (composeState.attachments.uploads.length) return colors.disabled
if (composeState.poll.active) {
return theme.primaryDefault
return colors.primaryDefault
} else {
return theme.secondary
return colors.secondary
}
}, [composeState.poll.active, composeState.attachments.uploads])
const pollOnPress = useCallback(() => {
@ -97,7 +97,8 @@ const ComposeActions: React.FC = () => {
t('content.root.actions.visibility.options.direct'),
t('content.root.actions.visibility.options.cancel')
],
cancelButtonIndex: 4
cancelButtonIndex: 4,
userInterfaceStyle: mode
},
buttonIndex => {
switch (buttonIndex) {
@ -150,12 +151,12 @@ const ComposeActions: React.FC = () => {
}, [composeState.spoiler.active, composeState.textInputFocus])
const emojiColor = useMemo(() => {
if (!composeState.emoji.emojis) return theme.disabled
if (!composeState.emoji.emojis) return colors.disabled
if (composeState.emoji.active) {
return theme.primaryDefault
return colors.primaryDefault
} else {
return theme.secondary
return colors.secondary
}
}, [composeState.emoji.active, composeState.emoji.emojis])
const emojiOnPress = useCallback(() => {
@ -177,8 +178,8 @@ const ComposeActions: React.FC = () => {
style={[
styles.additions,
{
backgroundColor: theme.backgroundDefault,
borderTopColor: theme.border
backgroundColor: colors.backgroundDefault,
borderTopColor: colors.border
}
]}
>
@ -223,7 +224,7 @@ const ComposeActions: React.FC = () => {
name={visibilityIcon}
size={24}
color={
composeState.visibilityLock ? theme.disabled : theme.secondary
composeState.visibilityLock ? colors.disabled : colors.secondary
}
/>
}
@ -242,8 +243,8 @@ const ComposeActions: React.FC = () => {
size={24}
color={
composeState.spoiler.active
? theme.primaryDefault
: theme.secondary
? colors.primaryDefault
: colors.secondary
}
/>
}

View File

@ -39,7 +39,7 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
const { showActionSheetWithOptions } = useActionSheet()
const { composeState, composeDispatch } = useContext(ComposeContext)
const { t } = useTranslation('screenCompose')
const { theme } = useTheme()
const { colors, mode } = useTheme()
const navigation = useNavigation<any>()
const flatListRef = useRef<FlatList>(null)
@ -135,8 +135,8 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
style={[
styles.duration,
{
color: theme.backgroundDefault,
backgroundColor: theme.backgroundOverlayInvert
color: colors.backgroundDefault,
backgroundColor: colors.backgroundOverlayInvert
}
]}
>
@ -147,12 +147,12 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
<View
style={[
styles.uploading,
{ backgroundColor: theme.backgroundOverlayInvert }
{ backgroundColor: colors.backgroundOverlayInvert }
]}
>
<Circle
size={StyleConstants.Font.Size.L}
color={theme.primaryOverlay}
color={colors.primaryOverlay}
/>
</View>
) : (
@ -213,7 +213,7 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
styles.container,
{
width: DEFAULT_HEIGHT,
backgroundColor: theme.backgroundOverlayInvert
backgroundColor: colors.backgroundOverlayInvert
}
]}
onPress={async () => {
@ -255,9 +255,9 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
<Icon
name={composeState.attachments.sensitive ? 'CheckCircle' : 'Circle'}
size={StyleConstants.Font.Size.L}
color={theme.primaryDefault}
color={colors.primaryDefault}
/>
<Text style={[styles.sensitiveText, { color: theme.primaryDefault }]}>
<Text style={[styles.sensitiveText, { color: colors.primaryDefault }]}>
{t('content.root.footer.attachments.sensitive')}
</Text>
</Pressable>

View File

@ -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'
@ -25,8 +27,9 @@ export interface Props {
const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
const { composeState, composeDispatch } = useContext(ComposeContext)
const { reduceMotionEnabled } = useAccessibility()
const { theme } = useTheme()
const { colors } = useTheme()
const { t } = useTranslation()
const dispatch = useDispatch()
useEffect(() => {
const tagEmojis = findNodeHandle(accessibleRefEmojis.current)
@ -37,7 +40,7 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
const listHeader = useCallback(
({ section: { title } }) => (
<Text style={[styles.group, { color: theme.secondary }]}>{title}</Text>
<Text style={[styles.group, { color: colors.secondary }]}>{title}</Text>
),
[]
)
@ -53,13 +56,14 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
<Pressable
key={emoji.shortcode}
onPress={() => {
haptics('Light')
updateText({
composeState,
composeDispatch,
newText: `:${emoji.shortcode}:`,
type: 'emoji'
})
haptics('Light')
dispatch(countInstanceEmoji(emoji))
}}
>
<FastImage

View File

@ -21,7 +21,7 @@ const ComposePoll: React.FC = () => {
composeDispatch
} = useContext(ComposeContext)
const { t } = useTranslation('screenCompose')
const { mode, theme } = useTheme()
const { colors, mode } = useTheme()
const instanceConfigurationPoll = useSelector(
getInstanceConfigurationPoll,
@ -39,7 +39,7 @@ const ComposePoll: React.FC = () => {
}, [])
return (
<View style={[styles.base, { borderColor: theme.border }]}>
<View style={[styles.base, { borderColor: colors.border }]}>
<View style={styles.options}>
{[...Array(total)].map((e, i) => {
const restOptions = Object.keys(options).filter(
@ -57,7 +57,7 @@ const ComposePoll: React.FC = () => {
<Icon
name={multiple ? 'Square' : 'Circle'}
size={StyleConstants.Font.Size.L}
color={theme.secondary}
color={colors.secondary}
/>
<TextInput
accessibilityLabel={t(
@ -69,8 +69,8 @@ const ComposePoll: React.FC = () => {
style={[
styles.textInput,
{
borderColor: theme.border,
color: hasConflict ? theme.red : theme.primaryDefault
borderColor: colors.border,
color: hasConflict ? colors.red : colors.primaryDefault
}
]}
placeholder={
@ -78,7 +78,7 @@ const ComposePoll: React.FC = () => {
? t('content.root.footer.poll.option.placeholder.multiple')
: t('content.root.footer.poll.option.placeholder.single')
}
placeholderTextColor={theme.disabled}
placeholderTextColor={colors.disabled}
maxLength={MAX_CHARS_PER_OPTION}
// @ts-ignore
value={options[i]}
@ -168,7 +168,8 @@ const ComposePoll: React.FC = () => {
t('content.root.footer.poll.multiple.options.multiple'),
t('content.root.footer.poll.multiple.options.cancel')
],
cancelButtonIndex: 2
cancelButtonIndex: 2,
userInterfaceStyle: mode
},
index => {
if (index && index < 2) {
@ -211,7 +212,8 @@ const ComposePoll: React.FC = () => {
),
t('content.root.footer.poll.expiration.options.cancel')
],
cancelButtonIndex: expirations.length
cancelButtonIndex: expirations.length,
userInterfaceStyle: mode
},
index => {
if (index && index < expirations.length) {

View File

@ -8,10 +8,10 @@ const ComposeReply: React.FC = () => {
const {
composeState: { replyToStatus }
} = useContext(ComposeContext)
const { theme } = useTheme()
const { colors } = useTheme()
return (
<View style={[styles.base, { borderTopColor: theme.border }]}>
<View style={[styles.base, { borderTopColor: colors.border }]}>
<TimelineDefault item={replyToStatus!} disableDetails disableOnPress />
</View>
)

View File

@ -12,7 +12,7 @@ import { useSelector } from 'react-redux'
const ComposePostingAs = React.memo(
() => {
const { t } = useTranslation('screenCompose')
const { theme } = useTheme()
const { colors } = useTheme()
const instanceAccount = useSelector(
getInstanceAccount,
@ -21,7 +21,7 @@ const ComposePostingAs = React.memo(
const instanceUri = useSelector(getInstanceUri)
return (
<Text style={[styles.text, { color: theme.secondary }]}>
<Text style={[styles.text, { color: colors.secondary }]}>
{t('content.root.header.postingAs', {
acct: instanceAccount?.acct,
domain: instanceUri

View File

@ -9,7 +9,7 @@ import ComposeContext from '../../utils/createContext'
const ComposeSpoilerInput: React.FC = () => {
const { composeState, composeDispatch } = useContext(ComposeContext)
const { t } = useTranslation('screenCompose')
const { mode, theme } = useTheme()
const { colors, mode } = useTheme()
return (
<TextInput
@ -17,8 +17,8 @@ const ComposeSpoilerInput: React.FC = () => {
style={[
styles.spoilerInput,
{
color: theme.primaryDefault,
borderBottomColor: theme.border
color: colors.primaryDefault,
borderBottomColor: colors.border
}
]}
autoCapitalize='none'
@ -27,7 +27,7 @@ const ComposeSpoilerInput: React.FC = () => {
enablesReturnKeyAutomatically
multiline
placeholder={t('content.root.header.spoilerInput.placeholder')}
placeholderTextColor={theme.secondary}
placeholderTextColor={colors.secondary}
onChangeText={content =>
formatText({
textInput: 'spoiler',

View File

@ -9,7 +9,7 @@ import ComposeContext from '../../utils/createContext'
const ComposeTextInput: React.FC = () => {
const { composeState, composeDispatch } = useContext(ComposeContext)
const { t } = useTranslation('screenCompose')
const { mode, theme } = useTheme()
const { colors, mode } = useTheme()
return (
<TextInput
@ -17,15 +17,15 @@ const ComposeTextInput: React.FC = () => {
style={[
styles.textInput,
{
color: theme.primaryDefault,
borderBottomColor: theme.border
color: colors.primaryDefault,
borderBottomColor: colors.border
}
]}
autoFocus
enablesReturnKeyAutomatically
multiline
placeholder={t('content.root.header.textInput.placeholder')}
placeholderTextColor={theme.secondary}
placeholderTextColor={colors.secondary}
onChangeText={content =>
formatText({
textInput: 'text',

View File

@ -16,9 +16,9 @@ export interface Params {
}
const TagText = ({ text }: { text: string }) => {
const { theme } = useTheme()
const { colors } = useTheme()
return <Text style={{ color: theme.blue }}>{text}</Text>
return <Text style={{ color: colors.blue }}>{text}</Text>
}
const debouncedSuggestions = debounce(

View File

@ -40,7 +40,12 @@ export type ComposeState = {
}
emoji: {
active: boolean
emojis: { title: string; data: Mastodon.Emoji[][] }[] | undefined
emojis:
| {
title: string
data: Pick<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>[][]
}[]
| undefined
}
poll: {
active: boolean

View File

@ -2,6 +2,7 @@ import haptics from '@components/haptics'
import { displayMessage } from '@components/Message'
import CameraRoll from '@react-native-community/cameraroll'
import { RootStackParamList } from '@utils/navigation/navigators'
import { Theme } from '@utils/styles/themes'
import * as FileSystem from 'expo-file-system'
import i18next from 'i18next'
import { RefObject } from 'react'
@ -10,17 +11,17 @@ import FlashMessage from 'react-native-flash-message'
type CommonProps = {
messageRef: RefObject<FlashMessage>
mode: 'light' | 'dark'
theme: Theme
image: RootStackParamList['Screen-ImagesViewer']['imageUrls'][0]
}
const saveIos = async ({ messageRef, mode, image }: CommonProps) => {
const saveIos = async ({ messageRef, theme, image }: CommonProps) => {
CameraRoll.save(image.url)
.then(() => {
haptics('Success')
displayMessage({
ref: messageRef,
mode,
theme,
type: 'success',
message: i18next.t('screenImageViewer:content.save.succeed')
})
@ -32,7 +33,7 @@ const saveIos = async ({ messageRef, mode, image }: CommonProps) => {
haptics('Success')
displayMessage({
ref: messageRef,
mode,
theme,
type: 'success',
message: i18next.t('screenImageViewer:content.save.succeed')
})
@ -41,7 +42,7 @@ const saveIos = async ({ messageRef, mode, image }: CommonProps) => {
haptics('Error')
displayMessage({
ref: messageRef,
mode,
theme,
type: 'error',
message: i18next.t('screenImageViewer:content.save.failed')
})
@ -50,7 +51,7 @@ const saveIos = async ({ messageRef, mode, image }: CommonProps) => {
haptics('Error')
displayMessage({
ref: messageRef,
mode,
theme,
type: 'error',
message: i18next.t('screenImageViewer:content.save.failed')
})
@ -58,7 +59,7 @@ const saveIos = async ({ messageRef, mode, image }: CommonProps) => {
})
}
const saveAndroid = async ({ messageRef, mode, image }: CommonProps) => {
const saveAndroid = async ({ messageRef, theme, image }: CommonProps) => {
const fileUri: string = `${FileSystem.documentDirectory}${image.id}.jpg`
const downloadedFile: FileSystem.FileSystemDownloadResult =
await FileSystem.downloadAsync(image.url, fileUri)
@ -67,7 +68,7 @@ const saveAndroid = async ({ messageRef, mode, image }: CommonProps) => {
haptics('Error')
displayMessage({
ref: messageRef,
mode,
theme,
type: 'error',
message: i18next.t('screenImageViewer:content.save.failed')
})
@ -83,7 +84,7 @@ const saveAndroid = async ({ messageRef, mode, image }: CommonProps) => {
haptics('Error')
displayMessage({
ref: messageRef,
mode,
theme,
type: 'error',
message: i18next.t('screenImageViewer:content.save.failed')
})
@ -96,7 +97,7 @@ const saveAndroid = async ({ messageRef, mode, image }: CommonProps) => {
haptics('Success')
displayMessage({
ref: messageRef,
mode,
theme,
type: 'success',
message: i18next.t('screenImageViewer:content.save.succeed')
})
@ -105,7 +106,7 @@ const saveAndroid = async ({ messageRef, mode, image }: CommonProps) => {
haptics('Error')
displayMessage({
ref: messageRef,
mode,
theme,
type: 'error',
message: i18next.t('screenImageViewer:content.save.failed')
})

View File

@ -35,7 +35,7 @@ const HeaderComponent = React.memo(
imageUrls: RootStackParamList['Screen-ImagesViewer']['imageUrls']
}) => {
const insets = useSafeAreaInsets()
const { mode } = useTheme()
const { mode, theme } = useTheme()
const { t } = useTranslation('screenImageViewer')
const { showActionSheetWithOptions } = useActionSheet()
@ -48,13 +48,14 @@ const HeaderComponent = React.memo(
t('content.options.share'),
t('content.options.cancel')
],
cancelButtonIndex: 2
cancelButtonIndex: 2,
userInterfaceStyle: mode
},
async buttonIndex => {
switch (buttonIndex) {
case 0:
analytics('imageviewer_more_save_press')
saveImage({ messageRef, mode, image: imageUrls[currentIndex] })
saveImage({ messageRef, theme, image: imageUrls[currentIndex] })
break
case 1:
analytics('imageviewer_more_share_press')
@ -117,7 +118,7 @@ const ScreenImagesViewer = ({
return null
}
const { mode } = useTheme()
const { theme } = useTheme()
const initialIndex = imageUrls.findIndex(image => image.id === id)
const [currentIndex, setCurrentIndex] = useState(initialIndex)
@ -132,7 +133,7 @@ const ScreenImagesViewer = ({
imageIndex={initialIndex}
onImageIndexChange={index => setCurrentIndex(index)}
onRequestClose={() => navigation.goBack()}
onLongPress={image => saveImage({ messageRef, mode, image })}
onLongPress={image => saveImage({ messageRef, theme, image })}
HeaderComponent={() => (
<HeaderComponent
messageRef={messageRef}

View File

@ -31,7 +31,7 @@ const Tab = createBottomTabNavigator<ScreenTabsStackParamList>()
const ScreenTabs = React.memo(
({ navigation }: RootStackScreenProps<'Screen-Tabs'>) => {
const { theme } = useTheme()
const { colors } = useTheme()
const instanceActive = useSelector(getInstanceActive)
const instanceAccount = useSelector(
@ -42,8 +42,8 @@ const ScreenTabs = React.memo(
const screenOptions = useCallback(
({ route }): BottomTabNavigationOptions => ({
headerShown: false,
tabBarActiveTintColor: theme.primaryDefault,
tabBarInactiveTintColor: theme.secondary,
tabBarActiveTintColor: colors.primaryDefault,
tabBarInactiveTintColor: colors.secondary,
tabBarShowLabel: false,
...(Platform.OS === 'android' && { tabBarHideOnKeyboard: true }),
tabBarStyle: { display: instanceActive !== -1 ? 'flex' : 'none' },
@ -78,7 +78,7 @@ const ScreenTabs = React.memo(
borderRadius: size,
overflow: 'hidden',
borderWidth: focused ? 2 : 0,
borderColor: focused ? theme.secondary : color
borderColor: focused ? colors.secondary : color
}}
/>
)

View File

@ -23,16 +23,18 @@ const prepareFields = (
})
}
const TabMeProfileFields: React.FC<TabMeProfileStackScreenProps<
'Tab-Me-Profile-Fields'
> & { messageRef: RefObject<FlashMessage> }> = ({
const TabMeProfileFields: React.FC<
TabMeProfileStackScreenProps<'Tab-Me-Profile-Fields'> & {
messageRef: RefObject<FlashMessage>
}
> = ({
messageRef,
route: {
params: { fields }
},
navigation
}) => {
const { mode, theme } = useTheme()
const { colors, theme } = useTheme()
const { t, i18n } = useTranslation('screenTabs')
const { mutateAsync, status } = useProfileMutation()
@ -77,7 +79,7 @@ const TabMeProfileFields: React.FC<TabMeProfileStackScreenProps<
content='Save'
onPress={async () => {
mutateAsync({
mode,
theme,
messageRef,
message: {
text: 'me.profile.root.note.title',
@ -95,14 +97,14 @@ const TabMeProfileFields: React.FC<TabMeProfileStackScreenProps<
/>
)
})
}, [mode, i18n.language, dirty, status, newFields])
}, [theme, i18n.language, dirty, status, newFields])
return (
<ScrollView style={styles.base} keyboardShouldPersistTaps='always'>
<View style={{ marginBottom: StyleConstants.Spacing.L * 2 }}>
{Array.from(Array(4).keys()).map(index => (
<View key={index} style={styles.group}>
<Text style={[styles.headline, { color: theme.primaryDefault }]}>
<Text style={[styles.headline, { color: colors.primaryDefault }]}>
{t('me.profile.fields.group', { index: index + 1 })}
</Text>
<Input

View File

@ -21,7 +21,7 @@ const TabMeProfileName: React.FC<
},
navigation
}) => {
const { mode } = useTheme()
const { theme } = useTheme()
const { t, i18n } = useTranslation('screenTabs')
const { mutateAsync, status } = useProfileMutation()
@ -66,7 +66,7 @@ const TabMeProfileName: React.FC<
content='Save'
onPress={async () => {
mutateAsync({
mode,
theme,
messageRef,
message: {
text: 'me.profile.root.name.title',
@ -82,7 +82,7 @@ const TabMeProfileName: React.FC<
/>
)
})
}, [mode, i18n.language, dirty, status, displayName])
}, [theme, i18n.language, dirty, status, displayName])
return (
<ScrollView style={styles.base} keyboardShouldPersistTaps='always'>

View File

@ -10,16 +10,18 @@ import { Alert, StyleSheet, View } from 'react-native'
import FlashMessage from 'react-native-flash-message'
import { ScrollView } from 'react-native-gesture-handler'
const TabMeProfileNote: React.FC<TabMeProfileStackScreenProps<
'Tab-Me-Profile-Note'
> & { messageRef: RefObject<FlashMessage> }> = ({
const TabMeProfileNote: React.FC<
TabMeProfileStackScreenProps<'Tab-Me-Profile-Note'> & {
messageRef: RefObject<FlashMessage>
}
> = ({
messageRef,
route: {
params: { note }
},
navigation
}) => {
const { mode } = useTheme()
const { theme } = useTheme()
const { t, i18n } = useTranslation('screenTabs')
const { mutateAsync, status } = useProfileMutation()
@ -64,7 +66,7 @@ const TabMeProfileNote: React.FC<TabMeProfileStackScreenProps<
content='Save'
onPress={async () => {
mutateAsync({
mode,
theme,
messageRef,
message: {
text: 'me.profile.root.note.title',
@ -80,7 +82,7 @@ const TabMeProfileNote: React.FC<TabMeProfileStackScreenProps<
/>
)
})
}, [mode, i18n.language, dirty, status, newNote])
}, [theme, i18n.language, dirty, status, newNote])
return (
<ScrollView style={styles.base} keyboardShouldPersistTaps='always'>

View File

@ -12,10 +12,12 @@ import { ScrollView } from 'react-native-gesture-handler'
import { useDispatch } from 'react-redux'
import ProfileAvatarHeader from './Root/AvatarHeader'
const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
'Tab-Me-Profile-Root'
> & { messageRef: RefObject<FlashMessage> }> = ({ messageRef, navigation }) => {
const { mode } = useTheme()
const TabMeProfileRoot: React.FC<
TabMeProfileStackScreenProps<'Tab-Me-Profile-Root'> & {
messageRef: RefObject<FlashMessage>
}
> = ({ messageRef, navigation }) => {
const { mode, theme } = useTheme()
const { t } = useTranslation('screenTabs')
const { showActionSheetWithOptions } = useActionSheet()
@ -34,7 +36,8 @@ const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
t('me.profile.root.visibility.options.private'),
t('me.profile.root.visibility.options.cancel')
],
cancelButtonIndex: 3
cancelButtonIndex: 3,
userInterfaceStyle: mode
},
async buttonIndex => {
switch (buttonIndex) {
@ -54,7 +57,7 @@ const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
new: indexVisibilityMapping[buttonIndex]
})
mutateAsync({
mode,
theme,
messageRef,
message: {
text: 'me.profile.root.visibility.title',
@ -69,7 +72,7 @@ const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
}
}
)
}, [data?.source.privacy])
}, [theme, data?.source.privacy])
const onPressSensitive = useCallback(() => {
analytics('me_profile_sensitive', {
@ -77,7 +80,7 @@ const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
new: data?.source.sensitive === undefined ? true : !data.source.sensitive
})
mutateAsync({
mode,
theme,
messageRef,
message: {
text: 'me.profile.root.sensitive.title',
@ -95,7 +98,7 @@ const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
new: data?.locked === undefined ? true : !data.locked
})
mutateAsync({
mode,
theme,
messageRef,
message: {
text: 'me.profile.root.lock.title',
@ -105,7 +108,7 @@ const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
type: 'locked',
data: data?.locked === undefined ? true : !data.locked
})
}, [data?.locked])
}, [theme, data?.locked])
const onPressBot = useCallback(() => {
analytics('me_profile_bot', {
@ -113,7 +116,7 @@ const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
new: data?.bot === undefined ? true : !data.bot
})
mutateAsync({
mode,
theme,
messageRef,
message: {
text: 'me.profile.root.bot.title',
@ -123,7 +126,7 @@ const TabMeProfileRoot: React.FC<TabMeProfileStackScreenProps<
type: 'bot',
data: data?.bot === undefined ? true : !data.bot
})
}, [data?.bot])
}, [theme, data?.bot])
return (
<ScrollView>

View File

@ -14,7 +14,7 @@ export interface Props {
}
const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => {
const { mode } = useTheme()
const { theme } = useTheme()
const { t } = useTranslation('screenTabs')
const { showActionSheetWithOptions } = useActionSheet()
@ -35,7 +35,7 @@ const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => {
resize: { width: 400, height: 400 }
})
mutation.mutate({
mode,
theme,
messageRef,
message: {
text: `me.profile.root.${type}.title`,

Some files were not shown because too many files have changed in this diff Show More