From 738194d1082c17d41270413e5663f205a779e0f2 Mon Sep 17 00:00:00 2001 From: xmflsct Date: Fri, 27 Jan 2023 18:44:48 +0100 Subject: [PATCH] Consolidate swipe to delete views --- src/components/Filter.tsx | 15 +- src/components/Hr.tsx | 2 +- src/components/SwipeToActions.tsx | 57 +++++ src/screens/Compose/DraftsList.tsx | 232 +++++++++----------- src/screens/Compose/index.tsx | 4 +- src/screens/Tabs/Me/Preferences/Filters.tsx | 42 ++-- src/screens/Tabs/Me/Preferences/index.tsx | 12 +- 7 files changed, 189 insertions(+), 175 deletions(-) create mode 100644 src/components/SwipeToActions.tsx diff --git a/src/components/Filter.tsx b/src/components/Filter.tsx index 6ed73a2f..f9c57fef 100644 --- a/src/components/Filter.tsx +++ b/src/components/Filter.tsx @@ -2,7 +2,7 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import { Fragment } from 'react' import { Trans, useTranslation } from 'react-i18next' -import { View } from 'react-native' +import { View, ViewStyle } from 'react-native' import { TouchableNativeFeedback } from 'react-native-gesture-handler' import Icon from './Icon' import CustomText from './Text' @@ -11,9 +11,10 @@ export type Props = { onPress: () => void filter: Mastodon.Filter<'v2'> button?: React.ReactNode + style?: ViewStyle } -export const Filter: React.FC = ({ onPress, filter, button }) => { +export const Filter: React.FC = ({ onPress, filter, button, style }) => { const { t } = useTranslation(['common', 'screenTabs']) const { colors } = useTheme() @@ -24,7 +25,8 @@ export const Filter: React.FC = ({ onPress, filter, button }) => { paddingVertical: StyleConstants.Spacing.S, flexDirection: 'row', alignItems: 'center', - backgroundColor: colors.backgroundDefault + backgroundColor: colors.backgroundDefault, + ...style }} > @@ -83,12 +85,7 @@ export const Filter: React.FC = ({ onPress, filter, button }) => { {filter.context.map((c, index) => ( diff --git a/src/components/Hr.tsx b/src/components/Hr.tsx index 1af52002..d7c87938 100644 --- a/src/components/Hr.tsx +++ b/src/components/Hr.tsx @@ -12,7 +12,7 @@ const Hr: React.FC<{ style?: ViewStyle }> = ({ style }) => { borderTopColor: colors.border, borderTopWidth: 1, height: 1, - marginVertical: StyleConstants.Spacing.S + paddingVertical: StyleConstants.Spacing.S }, style ]} diff --git a/src/components/SwipeToActions.tsx b/src/components/SwipeToActions.tsx new file mode 100644 index 00000000..1b391de3 --- /dev/null +++ b/src/components/SwipeToActions.tsx @@ -0,0 +1,57 @@ +import { StyleConstants } from '@utils/styles/constants' +import { ColorValue, TouchableNativeFeedback, View } from 'react-native' +import { SwipeListView } from 'react-native-swipe-list-view' +import haptics from './haptics' +import Icon, { IconName } from './Icon' +import ComponentSeparator from './Separator' + +export type Props = { + actions: { + onPress: (item: any) => void + color: ColorValue + icon: IconName + haptic?: Parameters['0'] + }[] +} + +export const SwipeToActions = ({ + actions, + ...rest +}: Props & SwipeListView['props']) => { + const perActionWidth = StyleConstants.Spacing.L * 2 + StyleConstants.Font.Size.L + + return ( + ( + + {actions.map((action, index) => ( + { + haptics(action.haptic || 'Light') + action.onPress({ item }) + }} + > + + + + + ))} + + )} + rightOpenValue={-perActionWidth * actions.length} + disableRightSwipe + closeOnRowPress + ItemSeparatorComponent={ComponentSeparator} + {...rest} + /> + ) +} diff --git a/src/screens/Compose/DraftsList.tsx b/src/screens/Compose/DraftsList.tsx index 0687d8d6..f00144e1 100644 --- a/src/screens/Compose/DraftsList.tsx +++ b/src/screens/Compose/DraftsList.tsx @@ -1,6 +1,6 @@ import { HeaderLeft } from '@components/Header' import Icon from '@components/Icon' -import ComponentSeparator from '@components/Separator' +import { SwipeToActions } from '@components/SwipeToActions' import CustomText from '@components/Text' import HeaderSharedCreated from '@components/Timeline/Shared/HeaderShared/Created' import apiInstance from '@utils/api/instance' @@ -10,10 +10,8 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { useContext, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Dimensions, Modal, Platform, Pressable, View } from 'react-native' +import { Dimensions, Modal, Pressable, View } from 'react-native' import FastImage from 'react-native-fast-image' -import { PanGestureHandler } from 'react-native-gesture-handler' -import { SwipeListView } from 'react-native-swipe-list-view' import ComposeContext from './utils/createContext' import { formatText } from './utils/processText' import { ComposeStateDraft, ExtendedAttachment } from './utils/types' @@ -39,9 +37,7 @@ const ComposeDraftsList: React.FC { navigation.setOptions({ title: t('content.draftsList.header.title'), - headerLeft: () => ( - navigation.goBack()} /> - ) + headerLeft: () => navigation.goBack()} /> }) }, []) @@ -49,8 +45,6 @@ const ComposeDraftsList: React.FC - - draft.timestamp !== timestamp)} - renderItem={({ item }: { item: ComposeStateDraft }) => { - return ( - { - setCheckingAttachments(true) - let tempDraft = item - let tempUploads: ExtendedAttachment[] = [] - if (item.attachments && item.attachments.uploads.length) { - for (const attachment of item.attachments.uploads) { - await apiInstance({ - method: 'get', - url: `media/${attachment.remote?.id}` - }) - .then(res => { - if (res.body.id === attachment.remote?.id) { - tempUploads.push(attachment) - } - }) - .catch(() => {}) - } - tempDraft = { - ...tempDraft, - attachments: { ...item.attachments, uploads: tempUploads } - } - } - - tempDraft.spoiler?.length && - formatText({ textInput: 'text', composeDispatch, content: tempDraft.spoiler }) - tempDraft.text?.length && - formatText({ textInput: 'text', composeDispatch, content: tempDraft.text }) - composeDispatch({ - type: 'loadDraft', - payload: tempDraft - }) - removeDraft(item.timestamp) - navigation.goBack() - }} - > - - - - {item.text || item.spoiler || t('content.draftsList.content.textEmpty')} - - {item.attachments?.uploads.length ? ( - - {item.attachments.uploads.map((attachment, index) => ( - - ))} - - ) : null} - - - ) - }} - renderHiddenItem={({ item }) => ( + removeDraft(item.timestamp), color: colors.red, icon: 'trash' } + ]} + data={drafts.filter(draft => draft.timestamp !== timestamp)} + renderItem={({ item }: { item: ComposeStateDraft }) => { + return ( removeDraft(item.timestamp)} - children={ - + onPress={async () => { + setCheckingAttachments(true) + let tempDraft = item + let tempUploads: ExtendedAttachment[] = [] + if (item.attachments && item.attachments.uploads.length) { + for (const attachment of item.attachments.uploads) { + await apiInstance({ + method: 'get', + url: `media/${attachment.remote?.id}` + }) + .then(res => { + if (res.body.id === attachment.remote?.id) { + tempUploads.push(attachment) + } + }) + .catch(() => {}) } - /> - } - /> - )} - disableRightSwipe={true} - rightOpenValue={-actionWidth} - previewOpenValue={-actionWidth / 2} - ItemSeparatorComponent={ComponentSeparator} - keyExtractor={item => item.timestamp?.toString()} - /> - + tempDraft = { + ...tempDraft, + attachments: { ...item.attachments, uploads: tempUploads } + } + } + + tempDraft.spoiler?.length && + formatText({ textInput: 'text', composeDispatch, content: tempDraft.spoiler }) + tempDraft.text?.length && + formatText({ textInput: 'text', composeDispatch, content: tempDraft.text }) + composeDispatch({ + type: 'loadDraft', + payload: tempDraft + }) + removeDraft(item.timestamp) + navigation.goBack() + }} + > + + + + {item.text || item.spoiler || t('content.draftsList.content.textEmpty')} + + {item.attachments?.uploads.length ? ( + + {item.attachments.uploads.map((attachment, index) => ( + + ))} + + ) : null} + + + ) + }} + keyExtractor={item => item.timestamp?.toString()} + /> > = ({ diff --git a/src/screens/Tabs/Me/Preferences/Filters.tsx b/src/screens/Tabs/Me/Preferences/Filters.tsx index 8eff2ab0..4af1099e 100644 --- a/src/screens/Tabs/Me/Preferences/Filters.tsx +++ b/src/screens/Tabs/Me/Preferences/Filters.tsx @@ -1,22 +1,17 @@ import { Filter } from '@components/Filter' import { HeaderLeft, HeaderRight } from '@components/Header' -import Icon from '@components/Icon' -import ComponentSeparator from '@components/Separator' +import { SwipeToActions } from '@components/SwipeToActions' import apiInstance from '@utils/api/instance' import { TabMePreferencesStackScreenProps } from '@utils/navigation/navigators' import { useFiltersQuery } from '@utils/queryHooks/filters' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { useEffect } from 'react' -import { useTranslation } from 'react-i18next' -import { Pressable, View } from 'react-native' -import { SwipeListView } from 'react-native-swipe-list-view' const TabMePreferencesFilters: React.FC< TabMePreferencesStackScreenProps<'Tab-Me-Preferences-Filters'> > = ({ navigation }) => { const { colors } = useTheme() - const { t } = useTranslation(['common', 'screenTabs']) useEffect(() => { navigation.setOptions({ @@ -38,40 +33,31 @@ const TabMePreferencesFilters: React.FC< const { data, refetch } = useFiltersQuery<'v2'>({ version: 'v2' }) return ( - ( - { + { apiInstance({ method: 'delete', version: 'v2', url: `filters/${item.id}` }).then(() => refetch() ) - }} - > - - - - - )} - rightOpenValue={-(StyleConstants.Spacing.L * 2 + StyleConstants.Font.Size.L)} - disableRightSwipe - closeOnRowPress + }, + color: colors.red, + icon: 'trash' + } + ]} data={data?.sort(filter => filter.expires_at ? new Date().getTime() - new Date(filter.expires_at).getTime() : 1 )} renderItem={({ item: filter }) => ( navigation.navigate('Tab-Me-Preferences-Filter', { type: 'edit', filter })} /> )} - ItemSeparatorComponent={ComponentSeparator} /> ) } diff --git a/src/screens/Tabs/Me/Preferences/index.tsx b/src/screens/Tabs/Me/Preferences/index.tsx index 411a39d4..1022be31 100644 --- a/src/screens/Tabs/Me/Preferences/index.tsx +++ b/src/screens/Tabs/Me/Preferences/index.tsx @@ -1,8 +1,9 @@ import { HeaderLeft } from '@components/Header' import { Message } from '@components/Message' +import { useNavigationState } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' import { TabMePreferencesStackParamList, TabMeStackScreenProps } from '@utils/navigation/navigators' -import React, { useRef } from 'react' +import React, { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import FlashMessage from 'react-native-flash-message' import TabMePreferencesFilter from './Filter' @@ -17,6 +18,15 @@ const TabMePreferences: React.FC> = const { t } = useTranslation('screenTabs') const messageRef = useRef(null) + const isNested = + (useNavigationState( + state => state.routes.find(route => route.name === 'Tab-Me-Preferences')?.state?.routes.length + ) || 0) > 1 + + useEffect(() => { + navigation.setOptions({ gestureEnabled: !isNested }) + }, [isNested]) + return ( <>