diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt index 46181811..5e983aa2 100644 --- a/fastlane/metadata/en-US/release_notes.txt +++ b/fastlane/metadata/en-US/release_notes.txt @@ -1,9 +1,3 @@ Enjoy toooting! This version includes following improvements and fixes: -- Auto fetch remote content in conversations! -- Remember last read position in timeline! -- Follow a user with other logged in accounts -- Allowing adding more context of reports -- Option to disable autoplay gif -- Hide boosts from users -- Followed hashtags are underlined -- Support GoToSocial \ No newline at end of file +- Added following remote instance +- Added set note of followed users \ No newline at end of file diff --git a/fastlane/metadata/zh-Hans/release_notes.txt b/fastlane/metadata/zh-Hans/release_notes.txt index df4f1cc5..8d2d6a1b 100644 --- a/fastlane/metadata/zh-Hans/release_notes.txt +++ b/fastlane/metadata/zh-Hans/release_notes.txt @@ -1,9 +1,3 @@ toooting愉快!此版本包括以下改进和修复: -- 主动获取对话的远程内容 -- 自动加载上次我的关注的阅读位置 -- 用其它已登陆的账户关注用户 -- 可添加举报细节 -- 新增暂停自动播放gif动画选项 -- 隐藏用户的转嘟 -- 下划线高亮正在关注的话题标签 -- 支持GoToSocial \ No newline at end of file +- 新增关注远程实例功能 +- 新增关注用户备注功能 \ No newline at end of file diff --git a/ios/tooot/Info.plist b/ios/tooot/Info.plist index 1cc5b7b4..a70dccd6 100644 --- a/ios/tooot/Info.plist +++ b/ios/tooot/Info.plist @@ -68,7 +68,7 @@ NSPhotoLibraryUsageDescription Allow $(PRODUCT_NAME) to access your camera roll to attach photos or videos to your toot UILaunchStoryboardName - SplashScreen.storyboard + SplashScreen UIRequiredDeviceCapabilities armv7 diff --git a/package.json b/package.json index ed39a02e..96cfbca4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tooot", - "version": "4.8.9", + "version": "4.9.0", "description": "tooot for Mastodon", "author": "xmflsct ", "license": "GPL-3.0-or-later", diff --git a/src/@types/app.d.ts b/src/@types/app.d.ts index 3b2b9525..36b6ff82 100644 --- a/src/@types/app.d.ts +++ b/src/@types/app.d.ts @@ -3,7 +3,7 @@ declare namespace App { | 'Following' | 'Local' | 'LocalPublic' - | 'Trending' + | 'Explore' | 'Notifications' | 'Hashtag' | 'List' diff --git a/src/components/Account.tsx b/src/components/Account.tsx index b0b9fdba..75ddfac4 100644 --- a/src/components/Account.tsx +++ b/src/components/Account.tsx @@ -42,7 +42,7 @@ const ComponentAccount: React.FC = ({ account, props, style={{ width: StyleConstants.Avatar.S, height: StyleConstants.Avatar.S, - borderRadius: 8, + borderRadius: StyleConstants.BorderRadius, marginRight: StyleConstants.Spacing.S }} dim diff --git a/src/components/AccountButton.tsx b/src/components/AccountButton.tsx index 9e481745..f6ea0a49 100644 --- a/src/components/AccountButton.tsx +++ b/src/components/AccountButton.tsx @@ -45,7 +45,7 @@ const AccountButton: React.FC = ({ account, additionalActions }) => { width: StyleConstants.Font.Size.L, height: StyleConstants.Font.Size.L }} - style={{ borderRadius: StyleConstants.Font.Size.L / 2, overflow: 'hidden' }} + style={{ borderRadius: 99, overflow: 'hidden' }} /> = ({ }} style={[ { - borderRadius: 100, + borderRadius: 99, justifyContent: 'center', alignItems: 'center', borderWidth: overlay ? 0 : selected ? 1.5 : 1, diff --git a/src/components/Emojis/Button.tsx b/src/components/Emojis/Button.tsx index 98ca98d6..f9776ee5 100644 --- a/src/components/Emojis/Button.tsx +++ b/src/components/Emojis/Button.tsx @@ -35,7 +35,7 @@ const EmojisButton: React.FC = () => { borderWidth: 2, borderColor: colors.primaryDefault, padding: StyleConstants.Spacing.Global.PagePadding / 2, - borderRadius: 100 + borderRadius: 99 }} > = ({ minWidth: 44, marginLeft: native ? -StyleConstants.Spacing.S : StyleConstants.Spacing.S, ...(type === undefined && { - borderRadius: 100 + borderRadius: 99 }), ...(type === 'text' && { paddingHorizontal: StyleConstants.Spacing.S diff --git a/src/components/Header/Right.tsx b/src/components/Header/Right.tsx index 91ab09f2..a918cbf9 100644 --- a/src/components/Header/Right.tsx +++ b/src/components/Header/Right.tsx @@ -98,9 +98,7 @@ const HeaderRight: React.FC = ({ minHeight: 44, minWidth: 44, marginRight: native ? -StyleConstants.Spacing.S : StyleConstants.Spacing.S, - ...(type === undefined && { - borderRadius: 100 - }), + ...(type === undefined && { borderRadius: 99 }), ...(type === 'text' && { paddingHorizontal: StyleConstants.Spacing.S }) diff --git a/src/components/Menu/Row.tsx b/src/components/Menu/Row.tsx index ffad2741..c381ccb7 100644 --- a/src/components/Menu/Row.tsx +++ b/src/components/Menu/Row.tsx @@ -22,7 +22,7 @@ export interface Props { switchDisabled?: boolean switchOnValueChange?: () => void - iconBack?: 'chevron-right' | 'external-link' | 'check' + iconBack?: 'chevron-right' | 'chevron-down' | 'external-link' | 'check' iconBackColor?: ColorDefinitions loading?: boolean @@ -66,14 +66,7 @@ const MenuRow: React.FC = ({ }} > - + = ({ width: 8, height: 8, backgroundColor: colors.red, - borderRadius: 8, + borderRadius: StyleConstants.BorderRadius, marginRight: StyleConstants.Spacing.S }} /> diff --git a/src/components/Relationship/Outgoing.tsx b/src/components/Relationship/Outgoing.tsx index e4fabbf2..685a0518 100644 --- a/src/components/Relationship/Outgoing.tsx +++ b/src/components/Relationship/Outgoing.tsx @@ -29,20 +29,20 @@ const RelationshipOutgoing: React.FC = ({ id }: Props) => { const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id }] const queryClient = useQueryClient() const mutation = useRelationshipMutation({ - onSuccess: (res, { payload: { action } }) => { + onSuccess: (res, vars) => { haptics('Success') queryClient.setQueryData(queryKeyRelationship, [res]) - if (action === 'block') { + if (vars.type === 'outgoing' && vars.payload.action === 'block') { const queryKey = ['Timeline', { page: 'Following' }] queryClient.invalidateQueries({ queryKey, exact: false }) } }, - onError: (err: any, { payload: { action } }) => { + onError: (err: any, vars) => { displayMessage({ theme, type: 'error', message: t('common:message.error.message', { - function: t(`componentRelationship:${action}.function` as any) + function: t(`componentRelationship:${(vars.payload as any).action}.function` as any) }), ...(err.status && typeof err.status === 'number' && diff --git a/src/components/SwipeToActions.tsx b/src/components/SwipeToActions.tsx index 74f6e01f..4306f8d2 100644 --- a/src/components/SwipeToActions.tsx +++ b/src/components/SwipeToActions.tsx @@ -31,9 +31,11 @@ export const SwipeToActions = ({ haptics(action.haptic || 'Light') action.onPress({ item }) }} + style={{ backgroundColor: 'rgba(0, 255, 0, 0.2)' }} > = ({ conversation, queryKey, highlig = ({ total, index, sensitiveShown, audio width: '100%', height: StyleConstants.Spacing.M + StyleConstants.Spacing.S * 2, paddingHorizontal: StyleConstants.Spacing.Global.PagePadding, - borderRadius: 100, + borderRadius: 99, opacity: sensitiveShown ? 0.35 : undefined }} > diff --git a/src/components/Timeline/Shared/Attachment/index.tsx b/src/components/Timeline/Shared/Attachment/index.tsx index 087754a0..6bf95481 100644 --- a/src/components/Timeline/Shared/Attachment/index.tsx +++ b/src/components/Timeline/Shared/Attachment/index.tsx @@ -9,8 +9,9 @@ import { StackNavigationProp } from '@react-navigation/stack' import { RootStackParamList } from '@utils/navigation/navigators' import { usePreferencesQuery } from '@utils/queryHooks/preferences' import { StyleConstants } from '@utils/styles/constants' +import { isLargeDevice } from '@utils/styles/scaling' import { chunk } from 'lodash' -import React, { useContext, useState } from 'react' +import React, { Fragment, useContext, useState } from 'react' import { useTranslation } from 'react-i18next' import { Pressable, View } from 'react-native' import StatusContext from '../Context' @@ -71,14 +72,9 @@ const TimelineAttachment = () => { } default: if ( - attachment.preview_url?.endsWith('.jpg') || - attachment.preview_url?.endsWith('.jpeg') || - attachment.preview_url?.endsWith('.png') || - attachment.preview_url?.endsWith('.gif') || - attachment.remote_url?.endsWith('.jpg') || - attachment.remote_url?.endsWith('.jpeg') || - attachment.remote_url?.endsWith('.png') || - attachment.remote_url?.endsWith('.gif') + // https://docs.expo.dev/versions/unversioned/sdk/image/#supported-image-formats + attachment.preview_url?.match(/.(?:a?png|jpe?g|webp|avif|heic|gif|svg|ico|icns)$/i) || + attachment.remote_url?.match(/.(?:a?png|jpe?g|webp|avif|heic|gif|svg|ico|icns)$/i) ) { return { id: attachment.id, @@ -179,11 +175,13 @@ const TimelineAttachment = () => { style={{ marginTop: StyleConstants.Spacing.M, flex: 1, - gap: StyleConstants.Spacing.XS + gap: StyleConstants.Spacing.XS, + ...(isLargeDevice && { maxWidth: 375 }) }} > {chunk(status.media_attachments, 2).map((chunk, chunkIndex) => ( { gap: StyleConstants.Spacing.XS }} > - {chunk.map((a, aIndex) => mapAttachmentType(a, chunkIndex * 2 + aIndex))} + {chunk.map((a, aIndex) => ( + {mapAttachmentType(a, chunkIndex * 2 + aIndex)} + ))} ))} diff --git a/src/components/Timeline/Shared/Avatar.tsx b/src/components/Timeline/Shared/Avatar.tsx index ff80d0ab..55aa6ed0 100644 --- a/src/components/Timeline/Shared/Avatar.tsx +++ b/src/components/Timeline/Shared/Avatar.tsx @@ -49,7 +49,7 @@ const TimelineAvatar: React.FC = ({ account }) => { } } style={{ - borderRadius: StyleConstants.Avatar.M, + borderRadius: 99, overflow: 'hidden', marginRight: StyleConstants.Spacing.S }} diff --git a/src/components/Timeline/Shared/Card.tsx b/src/components/Timeline/Shared/Card.tsx index 2f7adac7..86e2ae4e 100644 --- a/src/components/Timeline/Shared/Card.tsx +++ b/src/components/Timeline/Shared/Card.tsx @@ -137,7 +137,7 @@ const TimelineCard: React.FC = () => { flexDirection: 'row', marginTop: StyleConstants.Spacing.M, borderWidth: 1, - borderRadius: StyleConstants.Spacing.S, + borderRadius: StyleConstants.BorderRadius, overflow: 'hidden', borderColor: colors.border }} diff --git a/src/components/Timeline/index.tsx b/src/components/Timeline/index.tsx index 13ff0573..84346e3b 100644 --- a/src/components/Timeline/index.tsx +++ b/src/components/Timeline/index.tsx @@ -42,6 +42,7 @@ export interface Props { 'notifyOnChangeProps' | 'getNextPageParam' | 'getPreviousPageParam' | 'select' | 'onSuccess' > disableRefresh?: boolean + refreshAutoRefetch?: boolean disableInfinity?: boolean readMarker?: 'read_marker_following' customProps?: Partial> @@ -52,6 +53,7 @@ const Timeline: React.FC = ({ queryKey, queryOptions, disableRefresh = false, + refreshAutoRefetch = true, disableInfinity = false, readMarker = undefined, customProps @@ -154,6 +156,7 @@ const Timeline: React.FC = ({ if ( curr === true && prev === false && + refreshAutoRefetch && !isFetchingPrev.value && fetchingType.value === 0 && shouldAutoFetch.value && diff --git a/src/components/contextMenu/account.ts b/src/components/contextMenu/account.ts index 413d5e94..4da0a947 100644 --- a/src/components/contextMenu/account.ts +++ b/src/components/contextMenu/account.ts @@ -105,21 +105,21 @@ const menuAccount = ({ }) const queryKeyRelationship: QueryKeyRelationship = ['Relationship', { id: actualAccount?.id }] const relationshipMutation = useRelationshipMutation({ - onSuccess: (res, { payload: { action } }) => { + onSuccess: (res, vars) => { haptics('Success') queryClient.setQueryData(queryKeyRelationship, [res]) - if (action === 'block') { + if (vars.type === 'outgoing' && vars.payload.action === 'block') { queryClient.invalidateQueries({ queryKey: ['Timeline', { page: 'Following' }], exact: false }) } }, - onError: (err: any, { payload: { action } }) => { + onError: (err: any, vars) => { displayMessage({ type: 'danger', message: t('common:message.error.message', { - function: t(`componentContextMenu:${action}.function` as any) + function: t(`componentContextMenu:${(vars.payload as any).action}.function` as any) }), ...(err.status && typeof err.status === 'number' && diff --git a/src/components/discardConfirmation.ts b/src/components/discardConfirmation.ts new file mode 100644 index 00000000..f3f4fbb4 --- /dev/null +++ b/src/components/discardConfirmation.ts @@ -0,0 +1,26 @@ +import i18n from '@i18n/index' +import { Alert } from 'react-native' + +export const discardConfirmation = ({ + condition, + action +}: { + condition: boolean + action: () => void +}) => { + if (condition) { + Alert.alert(i18n.t('common:discard.title'), i18n.t('common:discard.message'), [ + { + text: i18n.t('common:buttons.discard'), + style: 'destructive', + onPress: () => action() + }, + { + text: i18n.t('common:buttons.cancel'), + style: 'default' + } + ]) + } else { + action() + } +} diff --git a/src/components/haptics.ts b/src/components/haptics.ts index 57148017..ef77d42a 100644 --- a/src/components/haptics.ts +++ b/src/components/haptics.ts @@ -4,9 +4,6 @@ import { Platform } from 'react-native' const haptics = ( type: 'Success' | 'Warning' | 'Error' | 'Light' | 'Medium' | 'Heavy' ) => { - if (Platform.OS === 'ios' && parseInt(Platform.Version, 10) <= 12) { - return - } if (Platform.OS === 'android') { if (type === 'Error') { Haptics.impactAsync(Haptics.ImpactFeedbackStyle['Light']).catch(() => {}) diff --git a/src/components/mediaSelector.ts b/src/components/mediaSelector.ts index 4ef0ccfd..e05aedde 100644 --- a/src/components/mediaSelector.ts +++ b/src/components/mediaSelector.ts @@ -5,7 +5,7 @@ import i18next from 'i18next' import { Asset, launchImageLibrary } from 'react-native-image-picker' const queryKeyInstance: QueryKeyInstance = ['Instance'] -export const MAX_MEDIA_ATTACHMENTS: number = +export const MAX_MEDIA_ATTACHMENTS = (): number => queryClient.getQueryData>(queryKeyInstance)?.configuration?.statuses .max_media_attachments || 4 @@ -27,7 +27,7 @@ const mediaSelector = async ({ indicateMaximum = false, showActionSheetWithOptions }: Props): Promise => { - const _maximum = maximum || MAX_MEDIA_ATTACHMENTS + const _maximum = maximum || MAX_MEDIA_ATTACHMENTS() const options = () => { switch (mediaType) { diff --git a/src/i18n/en/common.json b/src/i18n/en/common.json index e8f0f972..368a8baa 100644 --- a/src/i18n/en/common.json +++ b/src/i18n/en/common.json @@ -8,7 +8,8 @@ "create": "Create", "delete": "Delete", "done": "Done", - "confirm": "Confirm" + "confirm": "Confirm", + "add": "Add" }, "customEmoji": { "accessibilityLabel": "Custom emoji {{emoji}}" diff --git a/src/i18n/en/components/instance.json b/src/i18n/en/components/instance.json index 05a71792..c9d1eb14 100644 --- a/src/i18n/en/components/instance.json +++ b/src/i18n/en/components/instance.json @@ -7,9 +7,7 @@ "button": "Login", "information": { "name": "Name", - "accounts": "Users", - "statuses": "Toots", - "domains": "Universes" + "description": "Description" }, "disclaimer": { "base": "Logging in process uses system browser that, your account information won't be visible to tooot app." diff --git a/src/i18n/en/screens/tabs.json b/src/i18n/en/screens/tabs.json index 3c27bd0b..615c4db5 100644 --- a/src/i18n/en/screens/tabs.json +++ b/src/i18n/en/screens/tabs.json @@ -11,7 +11,13 @@ "segments": { "federated": "Federated", "local": "Local", - "trending": "Trending" + "explore": "Explore" + }, + "exploring": { + "heading": "Exploring", + "trending": "Trending", + "followRemote": "Follow remote instance", + "noTitle": "No Title" } }, "notifications": { @@ -380,6 +386,7 @@ "accessibilityHint": "You can mute, block, report or share this user" }, "followed_by": " is following you", + "privateNote": "Set private note", "moved": "User moved", "created_at": "Joined: {{date}}", "summary": { diff --git a/src/screens/AccountSelection.tsx b/src/screens/AccountSelection.tsx index 7681ca8f..1e3ce4e1 100644 --- a/src/screens/AccountSelection.tsx +++ b/src/screens/AccountSelection.tsx @@ -52,7 +52,7 @@ const Share = ({ padding: StyleConstants.Spacing.M, borderWidth: 1, borderColor: colors.shimmerHighlight, - borderRadius: 8 + borderRadius: StyleConstants.BorderRadius }} children={text} /> @@ -65,7 +65,7 @@ const Share = ({ padding: StyleConstants.Spacing.M, borderWidth: 1, borderColor: colors.shimmerHighlight, - borderRadius: 8 + borderRadius: StyleConstants.BorderRadius }} > padding: StyleConstants.Spacing.Global.PagePadding, marginTop: StyleConstants.Spacing.Global.PagePadding, borderWidth: 1, - borderRadius: 6, + borderRadius: StyleConstants.BorderRadius, borderColor: colors.primaryDefault, backgroundColor: colors.backgroundDefault }} @@ -123,7 +123,7 @@ const ScreenAnnouncements: React.FC marginTop: StyleConstants.Spacing.Global.PagePadding / 2, marginBottom: StyleConstants.Spacing.Global.PagePadding / 2, marginRight: StyleConstants.Spacing.M, - borderRadius: 6, + borderRadius: StyleConstants.BorderRadius, flexDirection: 'row', borderColor: reaction.me ? colors.disabled : colors.primaryDefault, backgroundColor: reaction.me ? colors.disabled : colors.backgroundDefault @@ -231,7 +231,7 @@ const ScreenAnnouncements: React.FC style={{ width: StyleConstants.Spacing.S, height: StyleConstants.Spacing.S, - borderRadius: StyleConstants.Spacing.S, + borderRadius: StyleConstants.BorderRadius, borderWidth: 1, borderColor: colors.primaryDefault, backgroundColor: i === index ? colors.primaryDefault : undefined, @@ -271,7 +271,7 @@ const ScreenAnnouncements: React.FC style={{ width: StyleConstants.Spacing.S, height: StyleConstants.Spacing.S, - borderRadius: StyleConstants.Spacing.S, + borderRadius: StyleConstants.BorderRadius, borderWidth: 1, borderColor: colors.primaryDefault, backgroundColor: i === index ? colors.primaryDefault : undefined, diff --git a/src/screens/Compose/DraftsList.tsx b/src/screens/Compose/DraftsList.tsx index fba9e684..0d4ea949 100644 --- a/src/screens/Compose/DraftsList.tsx +++ b/src/screens/Compose/DraftsList.tsx @@ -56,7 +56,7 @@ const ComposeDraftsList: React.FC diff --git a/src/screens/Compose/EditAttachment.tsx b/src/screens/Compose/EditAttachment.tsx index c7a9b096..7c1a3e54 100644 --- a/src/screens/Compose/EditAttachment.tsx +++ b/src/screens/Compose/EditAttachment.tsx @@ -1,3 +1,4 @@ +import { discardConfirmation } from '@components/discardConfirmation' import haptics from '@components/haptics' import { HeaderLeft, HeaderRight } from '@components/Header' import { ModalScrollView } from '@components/ModalScrollView' @@ -6,9 +7,11 @@ import apiInstance from '@utils/api/instance' import { ScreenComposeStackScreenProps } from '@utils/navigation/navigators' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' +import { Image } from 'expo-image' import React, { useContext, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Alert, TextInput } from 'react-native' +import { Alert, TextInput, View } from 'react-native' +import { DEFAULT_WIDTH } from './Root/Footer/Attachments' import ComposeContext from './utils/createContext' const ComposeEditAttachment: React.FC< @@ -20,7 +23,7 @@ const ComposeEditAttachment: React.FC< } }) => { const { colors } = useTheme() - const { t } = useTranslation('screenCompose') + const { t } = useTranslation(['common', 'screenCompose']) const { composeState, composeDispatch } = useContext(ComposeContext) const [isSubmitting, setIsSubmitting] = useState(false) @@ -31,55 +34,89 @@ const ComposeEditAttachment: React.FC< return null } + const [altText, setAltText] = useState(theAttachment.description) + useEffect(() => { navigation.setOptions({ - title: t('content.editAttachment.header.title'), - headerLeft: () => navigation.goBack()} />, + title: t('screenCompose:content.editAttachment.header.title'), + headerLeft: () => ( + { + discardConfirmation({ + condition: theAttachment.description != altText, + action: () => navigation.goBack() + }) + }} + /> + ), headerRight: () => ( { if (composeState.type === 'edit') { - composeDispatch({ type: 'attachment/edit', payload: { ...theAttachment } }) + composeDispatch({ + type: 'attachment/edit', + payload: { ...theAttachment, description: altText } + }) navigation.goBack() return } - setIsSubmitting(true) - const body = { description: theAttachment.description } - theAttachment?.id && apiInstance({ method: 'put', url: `media/${theAttachment.id}`, - body + body: { description: altText } }) - .then(() => { + .then(res => { + setIsSubmitting(false) haptics('Success') + composeDispatch({ + type: 'attachment/edit', + payload: res.body + }) navigation.goBack() }) .catch(() => { setIsSubmitting(false) haptics('Error') - Alert.alert(t('content.editAttachment.header.right.failed.title'), undefined, [ - { - text: t('content.editAttachment.header.right.failed.button'), - style: 'cancel' - } - ]) + Alert.alert( + t('screenCompose:content.editAttachment.header.right.failed.title'), + undefined, + [ + { + text: t('screenCompose:content.editAttachment.header.right.failed.button'), + style: 'cancel' + } + ] + ) }) }} /> ) }) - }, [theAttachment]) + }, [theAttachment, altText]) return ( + + + - {t('content.editAttachment.content.altText.heading')} + {t('screenCompose:content.editAttachment.content.altText.heading')} - composeDispatch({ - type: 'attachment/edit', - payload: { - ...theAttachment, - description: e - } - }) - } - placeholder={t('content.editAttachment.content.altText.placeholder')} + value={altText} + onChangeText={e => setAltText(e)} + placeholder={t('screenCompose:content.editAttachment.content.altText.placeholder')} placeholderTextColor={colors.secondary} - value={theAttachment.description} /> { const attachmentOnPress = () => { if (composeState.poll.active) return - if (composeState.attachments.uploads.length < MAX_MEDIA_ATTACHMENTS) { + if (composeState.attachments.uploads.length < MAX_MEDIA_ATTACHMENTS()) { return chooseAndUploadAttachment({ composeDispatch, showActionSheetWithOptions }) } } diff --git a/src/screens/Compose/Root/Footer/Attachments.tsx b/src/screens/Compose/Root/Footer/Attachments.tsx index d1db2c3a..f84f6411 100644 --- a/src/screens/Compose/Root/Footer/Attachments.tsx +++ b/src/screens/Compose/Root/Footer/Attachments.tsx @@ -12,19 +12,19 @@ import { StyleConstants } from '@utils/styles/constants' import layoutAnimation from '@utils/styles/layoutAnimation' import { useTheme } from '@utils/styles/ThemeManager' import { Image } from 'expo-image' -import React, { RefObject, useContext, useEffect, useRef } from 'react' +import React, { RefObject, useContext, useRef } from 'react' import { useTranslation } from 'react-i18next' import { FlatList, Pressable, StyleSheet, View } from 'react-native' import ComposeContext from '../../utils/createContext' import { ExtendedAttachment } from '../../utils/types' import chooseAndUploadAttachment from './addAttachment' +export const DEFAULT_WIDTH = 150 + export interface Props { accessibleRefAttachments: RefObject } -const DEFAULT_HEIGHT = 200 - const ComposeAttachments: React.FC = ({ accessibleRefAttachments }) => { const { showActionSheetWithOptions } = useActionSheet() const { composeState, composeDispatch } = useContext(ComposeContext) @@ -40,72 +40,22 @@ const ComposeAttachments: React.FC = ({ accessibleRefAttachments }) => { payload: { sensitive: !composeState.attachments.sensitive } }) - const calculateWidth = (item: ExtendedAttachment) => { - if (item.local) { - return ((item.local.width || 100) / (item.local.height || 100)) * DEFAULT_HEIGHT - } else { - if (item.remote) { - if (item.remote.meta.original.aspect) { - return item.remote.meta.original.aspect * DEFAULT_HEIGHT - } else if (item.remote.meta.original.width && item.remote.meta.original.height) { - return ( - (item.remote.meta.original.width / item.remote.meta.original.height) * DEFAULT_HEIGHT - ) - } else { - return DEFAULT_HEIGHT - } - } else { - return DEFAULT_HEIGHT - } - } - } - - const snapToOffsets = () => { - const attachmentsOffsets = composeState.attachments.uploads.map((_, index) => { - let currentOffset = 0 - Array.from(Array(index).keys()).map( - i => - (currentOffset = - currentOffset + - calculateWidth(composeState.attachments.uploads[i]) + - StyleConstants.Spacing.Global.PagePadding) - ) - return currentOffset - }) - return attachmentsOffsets.length < 4 - ? [ - ...attachmentsOffsets, - attachmentsOffsets.reduce((a, b) => a + b, 0) + - DEFAULT_HEIGHT + - StyleConstants.Spacing.Global.PagePadding - ] - : attachmentsOffsets - } - let prevOffsets = useRef() - useEffect(() => { - const snap = snapToOffsets() - if (snap.length > (prevOffsets.current ? prevOffsets.current.length : 0)) { - flatListRef.current?.scrollToOffset({ - offset: snap[snapToOffsets.length - 2] + snap[snapToOffsets.length - 1] - }) - } - prevOffsets.current = snap - }, [snapToOffsets, prevOffsets.current]) - const renderAttachment = ({ item, index }: { item: ExtendedAttachment; index: number }) => { return ( = ({ accessibleRefAttachments }) => { paddingRight: StyleConstants.Spacing.S, paddingTop: StyleConstants.Spacing.XS, paddingBottom: StyleConstants.Spacing.XS, - color: colors.backgroundDefault, + color: colors.primaryOverlay, backgroundColor: colors.backgroundOverlayInvert }} > @@ -157,7 +107,7 @@ const ComposeAttachments: React.FC = ({ accessibleRefAttachments }) => { })} type='icon' content='x' - spacing='M' + size='L' round overlay onPress={() => { @@ -175,11 +125,11 @@ const ComposeAttachments: React.FC = ({ accessibleRefAttachments }) => { accessibilityLabel={t('content.root.footer.attachments.edit.accessibilityLabel', { attachment: index + 1 })} - type='icon' - content='edit' - spacing='M' - round overlay + size='S' + type='text' + content={!!item.remote?.description?.length ? 'ALT ✓' : '+ ALT'} + fontBold onPress={() => navigation.navigate('Screen-Compose-EditAttachment', { index })} /> ) : null} @@ -230,23 +180,23 @@ const ComposeAttachments: React.FC = ({ accessibleRefAttachments }) => { pagingEnabled={false} snapToAlignment='center' renderItem={renderAttachment} - snapToOffsets={snapToOffsets()} + snapToOffsets={new Array(composeState.attachments.uploads.length).fill(DEFAULT_WIDTH)} keyboardShouldPersistTaps='always' showsHorizontalScrollIndicator={false} data={composeState.attachments.uploads} keyExtractor={item => item.local?.uri || item.remote?.url || Math.random().toString()} ListFooterComponent={ - composeState.attachments.uploads.length < MAX_MEDIA_ATTACHMENTS ? ( + composeState.attachments.uploads.length < MAX_MEDIA_ATTACHMENTS() ? ( { await chooseAndUploadAttachment({ @@ -258,9 +208,7 @@ const ComposeAttachments: React.FC = ({ accessibleRefAttachments }) => {