Partial fix #495

Add list
Edit list info
This commit is contained in:
xmflsct 2022-11-30 22:42:42 +01:00
parent de7498b218
commit bb3ddd2779
29 changed files with 506 additions and 224 deletions

View File

@ -274,6 +274,7 @@ declare namespace Mastodon {
type List = {
id: string
title: string
replies_policy: 'none' | 'list' | 'followed'
}
type Instance = {

View File

@ -121,7 +121,8 @@ const contextMenuStatus = ({ actions, status, queryKey, rootQueryKey }: Props) =
}
},
{
text: t('common:buttons.cancel')
text: t('common:buttons.cancel'),
style: 'default'
}
])
}

View File

@ -60,11 +60,11 @@ const ComponentInstance: React.FC<Props> = ({
if (instances && instances.filter(instance => instance.url === domain).length) {
Alert.alert(t('update.alert.title'), t('update.alert.message'), [
{
text: t('update.alert.buttons.cancel'),
text: t('common:buttons.cancel'),
style: 'cancel'
},
{
text: t('update.alert.buttons.continue'),
text: t('common:buttons.continue'),
onPress: () => {
appsQuery.refetch()
}

View File

@ -125,7 +125,7 @@ const Message = React.forwardRef<FlashMessage>((_, ref) => {
shadowOpacity: theme === 'light' ? 0.16 : 0.24,
shadowRadius: 4,
paddingRight: StyleConstants.Spacing.M * 2,
marginTop: insets.top
marginTop: ref ? undefined : insets.top
}}
titleStyle={{
color: colors.primaryDefault,

View File

@ -0,0 +1,71 @@
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react'
import { Pressable, View } from 'react-native'
import haptics from './haptics'
import Icon from './Icon'
import { ParseEmojis } from './Parse'
import CustomText from './Text'
export interface Props {
multiple?: boolean
options: { selected: boolean; content: string }[]
setOptions: React.Dispatch<React.SetStateAction<{ selected: boolean; content: string }[]>>
}
const Selections: React.FC<Props> = ({ multiple = false, options, setOptions }) => {
const { colors } = useTheme()
const isSelected = (index: number): string =>
options[index].selected
? `Check${multiple ? 'Square' : 'Circle'}`
: `${multiple ? 'Square' : 'Circle'}`
return (
<>
{options.map((option, index) => (
<Pressable
key={index}
style={{ flex: 1, paddingVertical: StyleConstants.Spacing.S }}
onPress={() => {
if (multiple) {
haptics('Light')
setOptions(options.map((o, i) => (i === index ? { ...o, selected: !o.selected } : o)))
} else {
if (!option.selected) {
haptics('Light')
setOptions(
options.map((o, i) => {
if (i === index) {
return { ...o, selected: true }
} else {
return { ...o, selected: false }
}
})
)
}
}
}}
>
<View style={{ flex: 1, flexDirection: 'row' }}>
<Icon
style={{
paddingTop: StyleConstants.Font.LineHeight.M - StyleConstants.Font.Size.M,
marginRight: StyleConstants.Spacing.S
}}
name={isSelected(index)}
size={StyleConstants.Font.Size.M}
color={colors.primaryDefault}
/>
<CustomText style={{ flex: 1 }}>
<ParseEmojis content={option.content} />
</CustomText>
</View>
</Pressable>
))}
</>
)
}
export default Selections

View File

@ -2,7 +2,9 @@
"buttons": {
"OK": "OK",
"apply": "Apply",
"cancel": "Cancel"
"cancel": "Cancel",
"discard": "Discard",
"continue": "Continue"
},
"customEmoji": {
"accessibilityLabel": "Custom emoji {{emoji}}"
@ -18,5 +20,9 @@
"message": "{{function}} failed, please retry"
}
},
"separator": ", "
"separator": ", ",
"discard": {
"title": "Change Not Saved",
"message": "Your change has not been saved. Would you discard saving the changes?"
}
}

View File

@ -20,11 +20,7 @@
"update": {
"alert": {
"title": "Logged in to this instance",
"message": "You can login to another account, keeping existing logged in account",
"buttons": {
"cancel": "$t(common:buttons.cancel)",
"continue": "Continue"
}
"message": "You can login to another account, keeping existing logged in account"
}
}
}

View File

@ -28,7 +28,6 @@
"removeReply": {
"title": "Replied toot could not be found",
"description": "Replied toot could have been deleted. Do you want to remove it from your reference?",
"cancel": "$t(common:buttons.cancel)",
"confirm": "Remove reference"
}
}
@ -89,8 +88,7 @@
"heading": "Choice type",
"options": {
"single": "Single choice",
"multiple": "Multiple choice",
"cancel": "$t(common:buttons.cancel)"
"multiple": "Multiple choice"
}
},
"expiration": {
@ -102,8 +100,7 @@
"21600": "6 hours",
"86400": "1 day",
"259200": "3 days",
"604800": "7 days",
"cancel": "$t(common:buttons.cancel)"
"604800": "7 days"
}
}
}
@ -130,8 +127,7 @@
"public": "Public",
"unlisted": "Unlisted",
"private": "Followers only",
"direct": "Direct message",
"cancel": "$t(common:buttons.cancel)"
"direct": "Direct message"
}
},
"spoiler": {

View File

@ -6,8 +6,7 @@
},
"options": {
"save": "Save image",
"share": "Share image",
"cancel": "$t(common:buttons.cancel)"
"share": "Share image"
},
"save": {
"succeed": "Image saved",

View File

@ -49,6 +49,12 @@
"lists": {
"name": "Lists"
},
"listAdd": {
"name": "Add a List"
},
"listEdit": {
"name": "Edit List"
},
"list": {
"name": "List: {{list}}"
},
@ -87,15 +93,18 @@
"XXL": "XXL"
}
},
"profile": {
"cancellation": {
"title": "Change Not Saved",
"message": "Your change has not been saved. Would you discard saving the changes?",
"buttons": {
"cancel": "$t(common:buttons.cancel)",
"discard": "Discard"
"listEdit": {
"title": "Title",
"repliesPolicy": {
"heading": "Show replies to:",
"options": {
"none": "No one",
"list": "Members of the list",
"followed": "Any followed user"
}
},
}
},
"profile": {
"feedback": {
"succeed": "{{type}} updated",
"failed": "{{type}} update failed, please try again"
@ -125,8 +134,7 @@
"options": {
"public": "Public",
"unlisted": "Unlisted",
"private": "Followers only",
"cancel": "$t(common:buttons.cancel)"
"private": "Followers only"
}
},
"sensitive": {
@ -211,8 +219,7 @@
"title": "Logging out?",
"message": "After logging out, you need to log in again",
"buttons": {
"logout": "Logout",
"cancel": "$t(common:buttons.cancel)"
"logout": "Logout"
}
}
}
@ -229,34 +236,28 @@
}
},
"language": {
"heading": "$t(me.stacks.language.name)",
"options": {
"cancel": "$t(common:buttons.cancel)"
}
"heading": "$t(me.stacks.language.name)"
},
"theme": {
"heading": "Appearance",
"options": {
"auto": "As system",
"light": "Light mode",
"dark": "Dark mode",
"cancel": "$t(common:buttons.cancel)"
"dark": "Dark mode"
}
},
"darkTheme": {
"heading": "Dark theme",
"options": {
"lighter": "Lighter",
"darker": "Darker",
"cancel": "$t(common:buttons.cancel)"
"darker": "Darker"
}
},
"browser": {
"heading": "Opening Link",
"options": {
"internal": "Inside app",
"external": "Use system browser",
"cancel": "$t(common:buttons.cancel)"
"external": "Use system browser"
}
},
"staticEmoji": {

View File

@ -43,10 +43,6 @@ const ActionsDomain: React.FC<Props> = ({ queryKey, rootQueryKey, domain, dismis
t('shared.header.actions.domain.alert.title', { domain }),
t('shared.header.actions.domain.alert.message'),
[
{
text: t('shared.header.actions.domain.alert.buttons.cancel'),
style: 'cancel'
},
{
text: t('shared.header.actions.domain.alert.buttons.confirm'),
style: 'destructive',
@ -58,6 +54,10 @@ const ActionsDomain: React.FC<Props> = ({ queryKey, rootQueryKey, domain, dismis
domain: domain
})
}
},
{
text: t('shared.header.actions.domain.alert.buttons.cancel'),
style: 'default'
}
]
)

View File

@ -38,8 +38,7 @@ const ActionsStatus: React.FC<Props> = ({
const mutation = useTimelineMutation({
onMutate: true,
onError: (err: any, params, oldData) => {
const theFunction = (params as MutationVarsTimelineUpdateStatusProperty)
.payload
const theFunction = (params as MutationVarsTimelineUpdateStatusProperty).payload
? (params as MutationVarsTimelineUpdateStatusProperty).payload.property
: 'delete'
displayMessage({
@ -108,15 +107,7 @@ const ActionsStatus: React.FC<Props> = ({
t('shared.header.actions.status.delete.alert.message'),
[
{
text: t(
'shared.header.actions.status.delete.alert.buttons.cancel'
),
style: 'cancel'
},
{
text: t(
'shared.header.actions.status.delete.alert.buttons.confirm'
),
text: t('shared.header.actions.status.delete.alert.buttons.confirm'),
style: 'destructive',
onPress: async () => {
dismiss()
@ -128,6 +119,10 @@ const ActionsStatus: React.FC<Props> = ({
id: status.id
})
}
},
{
text: t('shared.header.actions.status.delete.alert.buttons.cancel'),
style: 'default'
}
]
)
@ -142,15 +137,7 @@ const ActionsStatus: React.FC<Props> = ({
t('shared.header.actions.status.deleteEdit.alert.message'),
[
{
text: t(
'shared.header.actions.status.deleteEdit.alert.buttons.cancel'
),
style: 'cancel'
},
{
text: t(
'shared.header.actions.status.deleteEdit.alert.buttons.confirm'
),
text: t('shared.header.actions.status.deleteEdit.alert.buttons.confirm'),
style: 'destructive',
onPress: async () => {
let replyToStatus: Mastodon.Status | undefined = undefined
@ -177,6 +164,10 @@ const ActionsStatus: React.FC<Props> = ({
})
})
}
},
{
text: t('shared.header.actions.status.deleteEdit.alert.buttons.cancel'),
style: 'default'
}
]
)

View File

@ -300,7 +300,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
t('heading.right.alert.removeReply.description'),
[
{
text: t('heading.right.alert.removeReply.cancel'),
text: t('common:buttons.cancel'),
onPress: () => {
composeDispatch({ type: 'posting', payload: false })
},

View File

@ -86,7 +86,7 @@ const ComposeActions: React.FC = () => {
t('content.root.actions.visibility.options.unlisted'),
t('content.root.actions.visibility.options.private'),
t('content.root.actions.visibility.options.direct'),
t('content.root.actions.visibility.options.cancel')
t('common:buttons.cancel')
],
cancelButtonIndex: 4,
userInterfaceStyle: mode

View File

@ -195,7 +195,7 @@ const ComposePoll: React.FC = () => {
options: [
t('content.root.footer.poll.multiple.options.single'),
t('content.root.footer.poll.multiple.options.multiple'),
t('content.root.footer.poll.multiple.options.cancel')
t('common:buttons.cancel')
],
cancelButtonIndex: 2,
userInterfaceStyle: mode
@ -235,7 +235,7 @@ const ComposePoll: React.FC = () => {
...expirations.map(e =>
t(`content.root.footer.poll.expiration.options.${e}`)
),
t('content.root.footer.poll.expiration.options.cancel')
t('common:buttons.cancel')
],
cancelButtonIndex: expirations.length,
userInterfaceStyle: mode

View File

@ -53,7 +53,7 @@ const ScreenImagesViewer = ({
options: [
t('content.options.save'),
t('content.options.share'),
t('content.options.cancel')
t('common:buttons.cancel')
],
cancelButtonIndex: 2,
userInterfaceStyle: mode

View File

@ -7,8 +7,9 @@ import { Platform } from 'react-native'
import TabMeBookmarks from './Me/Bookmarks'
import TabMeConversations from './Me/Cconversations'
import TabMeFavourites from './Me/Favourites'
import TabMeList from './Me/List'
import TabMeListEdit from './Me/ListEdit'
import TabMeLists from './Me/Lists'
import TabMeListsList from './Me/ListsList'
import TabMeProfile from './Me/Profile'
import TabMePush from './Me/Push'
import TabMeRoot from './Me/Root'
@ -41,9 +42,7 @@ const TabMe = React.memo(
options={({ navigation }: any) => ({
title: t('me.stacks.bookmarks.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.bookmarks.name')} />
)
headerCenter: () => <HeaderCenter content={t('me.stacks.bookmarks.name')} />
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
@ -54,9 +53,7 @@ const TabMe = React.memo(
options={({ navigation }: any) => ({
title: t('me.stacks.conversations.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.conversations.name')} />
)
headerCenter: () => <HeaderCenter content={t('me.stacks.conversations.name')} />
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
@ -67,29 +64,14 @@ const TabMe = React.memo(
options={({ navigation }: any) => ({
title: t('me.stacks.favourites.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.favourites.name')} />
)
headerCenter: () => <HeaderCenter content={t('me.stacks.favourites.name')} />
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/>
<Stack.Screen
name='Tab-Me-Lists'
component={TabMeLists}
options={({ navigation }: any) => ({
title: t('me.stacks.lists.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.lists.name')} />
)
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/>
<Stack.Screen
name='Tab-Me-Lists-List'
component={TabMeListsList}
name='Tab-Me-List'
component={TabMeList}
options={({ route, navigation }: any) => ({
title: t('me.stacks.list.name', { list: route.params.title }),
...(Platform.OS === 'android' && {
@ -104,6 +86,29 @@ const TabMe = React.memo(
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/>
<Stack.Screen
name='Tab-Me-List-Edit'
component={TabMeListEdit}
options={{
gestureEnabled: false,
presentation: 'modal',
title: t('me.stacks.listsAdd.name'),
...(Platform.OS === 'android' && {
headerCenter: () => <HeaderCenter content={t('me.stacks.listsAdd.name')} />
})
}}
/>
<Stack.Screen
name='Tab-Me-Lists'
component={TabMeLists}
options={({ navigation }: any) => ({
title: t('me.stacks.lists.name'),
...(Platform.OS === 'android' && {
headerCenter: () => <HeaderCenter content={t('me.stacks.lists.name')} />
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/>
<Stack.Screen
name='Tab-Me-Profile'
component={TabMeProfile}
@ -120,15 +125,10 @@ const TabMe = React.memo(
headerShown: true,
title: t('me.stacks.push.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.push.name')} />
)
headerCenter: () => <HeaderCenter content={t('me.stacks.push.name')} />
}),
headerLeft: () => (
<HeaderLeft
content='ChevronDown'
onPress={() => navigation.goBack()}
/>
<HeaderLeft content='ChevronDown' onPress={() => navigation.goBack()} />
)
})}
/>
@ -146,9 +146,7 @@ const TabMe = React.memo(
options={({ navigation }: any) => ({
title: t('me.stacks.fontSize.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.fontSize.name')} />
)
headerCenter: () => <HeaderCenter content={t('me.stacks.fontSize.name')} />
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
@ -159,9 +157,7 @@ const TabMe = React.memo(
options={({ navigation }: any) => ({
title: t('me.stacks.language.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.language.name')} />
)
headerCenter: () => <HeaderCenter content={t('me.stacks.language.name')} />
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
@ -174,15 +170,10 @@ const TabMe = React.memo(
headerShown: true,
title: t('me.stacks.switch.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.switch.name')} />
)
headerCenter: () => <HeaderCenter content={t('me.stacks.switch.name')} />
}),
headerLeft: () => (
<HeaderLeft
content='ChevronDown'
onPress={() => navigation.goBack()}
/>
<HeaderLeft content='ChevronDown' onPress={() => navigation.goBack()} />
)
})}
/>

View File

@ -0,0 +1,40 @@
import { HeaderRight } from '@components/Header'
import Timeline from '@components/Timeline'
import TimelineDefault from '@components/Timeline/Default'
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({
navigation,
route: { key, params }
}) => {
const { t } = useTranslation('screenTabs')
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'List', list: params.id }]
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<HeaderRight
accessibilityLabel={t('me.stacks.listEdit.name')}
content='Edit'
onPress={() =>
navigation.navigate('Tab-Me-List-Edit', { type: 'edit', payload: params, key })
}
/>
)
})
}, [params])
return (
<Timeline
queryKey={queryKey}
customProps={{
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />
}}
/>
)
}
export default TabMeList

View File

@ -0,0 +1,163 @@
import { EmojisState } from '@components/Emojis/helpers/EmojisContext'
import haptics from '@components/haptics'
import { HeaderLeft, HeaderRight } from '@components/Header'
import ComponentInput from '@components/Input'
import { displayMessage, Message } from '@components/Message'
import Selections from '@components/Selections'
import CustomText from '@components/Text'
import { CommonActions } from '@react-navigation/native'
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
import { QueryKeyLists, useListsMutation } from '@utils/queryHooks/lists'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Alert, ScrollView, TextInput } from 'react-native'
import { useQueryClient } from 'react-query'
const TabMeListEdit: React.FC<TabMeStackScreenProps<'Tab-Me-List-Edit'>> = ({
navigation,
route: { params }
}) => {
const { colors, theme } = useTheme()
const { t } = useTranslation('screenTabs')
const messageRef = useRef(null)
const queryKeyLists: QueryKeyLists = ['Lists']
const queryClient = useQueryClient()
const mutation = useListsMutation({
onSuccess: res => {
haptics('Success')
queryClient.refetchQueries(queryKeyLists)
navigation.pop(1)
if (params.type === 'edit') {
navigation.dispatch({
...CommonActions.setParams(res),
source: params.key
})
}
},
onError: () => {
displayMessage({
ref: messageRef,
theme,
type: 'error',
message: t('common:message.error.message', {
function:
params.type === 'add' ? t('me.stacks.listAdd.name') : t('me.stacks.listEdit.name')
})
})
}
})
const ref = useRef<TextInput>(null)
const [title, setTitle] = useState(params.type === 'edit' ? params.payload.title : '')
const inputProps: EmojisState['inputProps'][0] = {
ref,
value: [title, setTitle],
selection: useState({ start: title.length }),
isFocused: useRef<boolean>(false),
maxLength: 50
}
const [options, setOptions] = useState<
{ id: 'none' | 'list' | 'followed'; content: string; selected: boolean }[]
>([
{
id: 'none',
content: t('me.listEdit.repliesPolicy.options.none'),
selected: params.type === 'edit' ? params.payload.replies_policy === 'none' : false
},
{
id: 'list',
content: t('me.listEdit.repliesPolicy.options.list'),
selected: params.type === 'edit' ? params.payload.replies_policy === 'list' : true
},
{
id: 'followed',
content: t('me.listEdit.repliesPolicy.options.followed'),
selected: params.type === 'edit' ? params.payload.replies_policy === 'followed' : false
}
])
useEffect(() => {
navigation.setOptions({
headerLeft: () => (
<HeaderLeft
content='X'
onPress={() => {
if (params.type === 'edit' ? params.payload.title !== title : title.length) {
Alert.alert(t('common:discard.title'), t('common:discard.message'), [
{
text: t('common:buttons.discard'),
style: 'destructive',
onPress: () => navigation.pop(1)
},
{
text: t('common:buttons.cancel'),
style: 'default'
}
])
} else {
navigation.pop(1)
}
}}
/>
),
headerRight: () => (
<HeaderRight
content='Save'
disabled={!title.length}
loading={mutation.isLoading}
onPress={() => {
switch (params.type) {
case 'add':
mutation.mutate({
type: 'add',
payload: {
title,
replies_policy: options.find(option => option.selected)?.id || 'list'
}
})
break
case 'edit':
mutation.mutate({
type: 'edit',
payload: {
id: params.payload.id,
title,
replies_policy: options.find(option => option.selected)?.id || 'list'
}
})
break
}
}}
/>
)
})
}, [title.length, options])
return (
<ScrollView style={{ paddingHorizontal: StyleConstants.Spacing.Global.PagePadding }}>
<ComponentInput {...inputProps} autoFocus title={t('me.listEdit.title')} />
<CustomText
fontStyle='M'
fontWeight='Bold'
style={{
color: colors.primaryDefault,
marginBottom: StyleConstants.Spacing.XS,
marginTop: StyleConstants.Spacing.M
}}
>
{t('me.listEdit.repliesPolicy.heading')}
</CustomText>
<Selections options={options} setOptions={setOptions} />
<Message ref={messageRef} />
</ScrollView>
)
}
export default TabMeListEdit

View File

@ -1,12 +1,25 @@
import { HeaderRight } from '@components/Header'
import { MenuContainer, MenuRow } from '@components/Menu'
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
import { useListsQuery } from '@utils/queryHooks/lists'
import React from 'react'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
const TabMeLists: React.FC<TabMeStackScreenProps<'Tab-Me-Lists'>> = ({
navigation
}) => {
const TabMeLists: React.FC<TabMeStackScreenProps<'Tab-Me-Lists'>> = ({ navigation }) => {
const { data } = useListsQuery({})
const { t } = useTranslation('screenTabs')
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<HeaderRight
accessibilityLabel={t('me.stacks.listAdd.name')}
content='Plus'
onPress={() => navigation.navigate('Tab-Me-List-Edit', { type: 'add' })}
/>
)
})
}, [])
return (
<MenuContainer>
@ -16,12 +29,7 @@ const TabMeLists: React.FC<TabMeStackScreenProps<'Tab-Me-Lists'>> = ({
iconFront='List'
iconBack='ChevronRight'
title={d.title}
onPress={() =>
navigation.navigate('Tab-Me-Lists-List', {
list: d.id,
title: d.title
})
}
onPress={() => navigation.navigate('Tab-Me-List', d)}
/>
))}
</MenuContainer>

View File

@ -1,26 +0,0 @@
import Timeline from '@components/Timeline'
import TimelineDefault from '@components/Timeline/Default'
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import React from 'react'
const TabMeListsList: React.FC<TabMeStackScreenProps<'Tab-Me-Lists-List'>> = ({
route: {
params: { list }
}
}) => {
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'List', list }]
return (
<Timeline
queryKey={queryKey}
customProps={{
renderItem: ({ item }) => (
<TimelineDefault item={item} queryKey={queryKey} />
)
}}
/>
)
}
export default TabMeListsList

View File

@ -90,20 +90,21 @@ const TabMeProfileFields: React.FC<
navigation.setOptions({
headerLeft: () => (
<HeaderLeft
content='X'
onPress={() => {
if (dirty) {
Alert.alert(
t('me.profile.cancellation.title'),
t('me.profile.cancellation.message'),
t('common:discard.title'),
t('common:discard.message'),
[
{
text: t('me.profile.cancellation.buttons.cancel'),
style: 'default'
},
{
text: t('me.profile.cancellation.buttons.discard'),
text: t('common:buttons.discard'),
style: 'destructive',
onPress: () => navigation.navigate('Tab-Me-Profile-Root')
},
{
text: t('common:buttons.cancel'),
style: 'default'
}
]
)

View File

@ -44,20 +44,21 @@ const TabMeProfileName: React.FC<
navigation.setOptions({
headerLeft: () => (
<HeaderLeft
content='X'
onPress={() => {
if (dirty) {
Alert.alert(
t('me.profile.cancellation.title'),
t('me.profile.cancellation.message'),
t('common:discard.title'),
t('common:discard.message'),
[
{
text: t('me.profile.cancellation.buttons.cancel'),
style: 'default'
},
{
text: t('me.profile.cancellation.buttons.discard'),
text: t('common:buttons.discard'),
style: 'destructive',
onPress: () => navigation.navigate('Tab-Me-Profile-Root')
},
{
text: t('common:buttons.cancel'),
style: 'default'
}
]
)

View File

@ -44,20 +44,21 @@ const TabMeProfileNote: React.FC<
navigation.setOptions({
headerLeft: () => (
<HeaderLeft
content='X'
onPress={() => {
if (dirty) {
Alert.alert(
t('me.profile.cancellation.title'),
t('me.profile.cancellation.message'),
t('common:discard.title'),
t('common:discard.message'),
[
{
text: t('me.profile.cancellation.buttons.cancel'),
style: 'default'
},
{
text: t('me.profile.cancellation.buttons.discard'),
text: t('common:buttons.discard'),
style: 'destructive',
onPress: () => navigation.navigate('Tab-Me-Profile-Root')
},
{
text: t('common:buttons.cancel'),
style: 'default'
}
]
)

View File

@ -33,7 +33,7 @@ const TabMeProfileRoot: React.FC<
t('me.profile.root.visibility.options.public'),
t('me.profile.root.visibility.options.unlisted'),
t('me.profile.root.visibility.options.private'),
t('me.profile.root.visibility.options.cancel')
t('common:buttons.cancel')
],
cancelButtonIndex: 3,
userInterfaceStyle: mode

View File

@ -27,27 +27,23 @@ const Logout: React.FC = () => {
}}
destructive
onPress={() =>
Alert.alert(
t('me.root.logout.alert.title'),
t('me.root.logout.alert.message'),
[
{
text: t('me.root.logout.alert.buttons.logout'),
style: 'destructive' as const,
onPress: () => {
if (instance) {
haptics('Success')
queryClient.clear()
dispatch(removeInstance(instance))
}
Alert.alert(t('me.root.logout.alert.title'), t('me.root.logout.alert.message'), [
{
text: t('me.root.logout.alert.buttons.logout'),
style: 'destructive',
onPress: () => {
if (instance) {
haptics('Success')
queryClient.clear()
dispatch(removeInstance(instance))
}
},
{
text: t('me.root.logout.alert.buttons.cancel'),
style: 'cancel' as const
}
]
)
},
{
text: t('common:buttons.cancel'),
style: 'default'
}
])
}
/>
)

View File

@ -69,7 +69,7 @@ const SettingsApp: React.FC = () => {
t('me.settings.theme.options.auto'),
t('me.settings.theme.options.light'),
t('me.settings.theme.options.dark'),
t('me.settings.theme.options.cancel')
t('common:buttons.cancel')
],
cancelButtonIndex: 3
},
@ -103,7 +103,7 @@ const SettingsApp: React.FC = () => {
options: [
t('me.settings.darkTheme.options.lighter'),
t('me.settings.darkTheme.options.darker'),
t('me.settings.darkTheme.options.cancel')
t('common:buttons.cancel')
],
cancelButtonIndex: 2
},
@ -133,7 +133,7 @@ const SettingsApp: React.FC = () => {
options: [
t('me.settings.browser.options.internal'),
t('me.settings.browser.options.external'),
t('me.settings.browser.options.cancel')
t('common:buttons.cancel')
],
cancelButtonIndex: 2
},

View File

@ -63,17 +63,18 @@ export type RootStackParamList = {
share?: { text?: string; media?: { uri: string; mime: string }[] }
}
}
export type RootStackScreenProps<T extends keyof RootStackParamList> =
NativeStackScreenProps<RootStackParamList, T>
export type RootStackScreenProps<T extends keyof RootStackParamList> = NativeStackScreenProps<
RootStackParamList,
T
>
export type ScreenComposeStackParamList = {
'Screen-Compose-Root': undefined
'Screen-Compose-EditAttachment': { index: number }
'Screen-Compose-DraftsList': { timestamp: number }
}
export type ScreenComposeStackScreenProps<
T extends keyof ScreenComposeStackParamList
> = NativeStackScreenProps<ScreenComposeStackParamList, T>
export type ScreenComposeStackScreenProps<T extends keyof ScreenComposeStackParamList> =
NativeStackScreenProps<ScreenComposeStackParamList, T>
export type ScreenTabsStackParamList = {
'Tab-Local': NavigatorScreenParams<TabLocalStackParamList>
@ -82,8 +83,10 @@ export type ScreenTabsStackParamList = {
'Tab-Notifications': NavigatorScreenParams<TabNotificationsStackParamList>
'Tab-Me': NavigatorScreenParams<TabMeStackParamList>
}
export type ScreenTabsScreenProps<T extends keyof ScreenTabsStackParamList> =
BottomTabScreenProps<ScreenTabsStackParamList, T>
export type ScreenTabsScreenProps<T extends keyof ScreenTabsStackParamList> = BottomTabScreenProps<
ScreenTabsStackParamList,
T
>
export type TabSharedStackParamList = {
'Tab-Shared-Account': {
@ -135,11 +138,17 @@ export type TabMeStackParamList = {
'Tab-Me-Bookmarks': undefined
'Tab-Me-Conversations': undefined
'Tab-Me-Favourites': undefined
'Tab-Me-List': Mastodon.List
'Tab-Me-List-Edit':
| {
type: 'add'
}
| {
type: 'edit'
payload: Mastodon.List
key: string // To update title after successful mutation
}
'Tab-Me-Lists': undefined
'Tab-Me-Lists-List': {
list: Mastodon.List['id']
title: Mastodon.List['title']
}
'Tab-Me-Profile': undefined
'Tab-Me-Push': undefined
'Tab-Me-Settings': undefined
@ -147,11 +156,12 @@ export type TabMeStackParamList = {
'Tab-Me-Settings-Language': undefined
'Tab-Me-Switch': undefined
} & TabSharedStackParamList
export type TabMeStackScreenProps<T extends keyof TabMeStackParamList> =
NativeStackScreenProps<TabMeStackParamList, T>
export type TabMeStackNavigationProp<
RouteName extends keyof TabMeStackParamList
> = StackNavigationProp<TabMeStackParamList, RouteName>
export type TabMeStackScreenProps<T extends keyof TabMeStackParamList> = NativeStackScreenProps<
TabMeStackParamList,
T
>
export type TabMeStackNavigationProp<RouteName extends keyof TabMeStackParamList> =
StackNavigationProp<TabMeStackParamList, RouteName>
export type TabMeProfileStackParamList = {
'Tab-Me-Profile-Root': undefined
@ -165,6 +175,5 @@ export type TabMeProfileStackParamList = {
fields?: Mastodon.Source['fields']
}
}
export type TabMeProfileStackScreenProps<
T extends keyof TabMeProfileStackParamList
> = NativeStackScreenProps<TabMeProfileStackParamList, T>
export type TabMeProfileStackScreenProps<T extends keyof TabMeProfileStackParamList> =
NativeStackScreenProps<TabMeProfileStackParamList, T>

View File

@ -1,8 +1,8 @@
import apiInstance from '@api/instance'
import { AxiosError } from 'axios'
import { useQuery, UseQueryOptions } from 'react-query'
import { useMutation, UseMutationOptions, useQuery, UseQueryOptions } from 'react-query'
export type QueryKey = ['Lists']
export type QueryKeyLists = ['Lists']
const queryFunction = async () => {
const res = await apiInstance<Mastodon.List[]>({
@ -12,13 +12,49 @@ const queryFunction = async () => {
return res.body
}
const useListsQuery = ({
options
}: {
options?: UseQueryOptions<Mastodon.List[], AxiosError>
}) => {
const queryKey: QueryKey = ['Lists']
const useListsQuery = ({ options }: { options?: UseQueryOptions<Mastodon.List[], AxiosError> }) => {
const queryKey: QueryKeyLists = ['Lists']
return useQuery(queryKey, queryFunction, options)
}
export { useListsQuery }
type MutationVarsLists =
| {
type: 'add'
payload: Omit<Mastodon.List, 'id'>
}
| {
type: 'edit'
payload: Mastodon.List
}
const mutationFunction = async (params: MutationVarsLists) => {
const body = new FormData()
switch (params.type) {
case 'add':
body.append('title', params.payload.title)
body.append('replies_policy', params.payload.replies_policy)
return apiInstance<Mastodon.List>({
method: 'post',
url: 'lists',
body
}).then(res => res.body)
case 'edit':
body.append('title', params.payload.title)
body.append('replies_policy', params.payload.replies_policy)
return apiInstance<Mastodon.List>({
method: 'put',
url: `lists/${params.payload.id}`,
body
}).then(res => res.body)
}
}
const useListsMutation = (
options: UseMutationOptions<Mastodon.List, AxiosError, MutationVarsLists>
) => {
return useMutation(mutationFunction, options)
}
export { useListsQuery, useListsMutation }