mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	@@ -4,39 +4,47 @@ import { StackNavigationProp } from '@react-navigation/stack'
 | 
			
		||||
import { TabLocalStackParamList } from '@utils/navigation/navigators'
 | 
			
		||||
import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import React, { useCallback } from 'react'
 | 
			
		||||
import React, { PropsWithChildren } from 'react'
 | 
			
		||||
import { Pressable, View } from 'react-native'
 | 
			
		||||
import GracefullyImage from './GracefullyImage'
 | 
			
		||||
import CustomText from './Text'
 | 
			
		||||
 | 
			
		||||
export interface Props {
 | 
			
		||||
  account: Mastodon.Account
 | 
			
		||||
  onPress?: () => void
 | 
			
		||||
  Component?: typeof View | typeof Pressable
 | 
			
		||||
  props?: {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ComponentAccount: React.FC<Props> = ({ account, onPress: customOnPress }) => {
 | 
			
		||||
const ComponentAccount: React.FC<PropsWithChildren & Props> = ({
 | 
			
		||||
  account,
 | 
			
		||||
  Component,
 | 
			
		||||
  props,
 | 
			
		||||
  children
 | 
			
		||||
}) => {
 | 
			
		||||
  const { colors } = useTheme()
 | 
			
		||||
  const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
 | 
			
		||||
 | 
			
		||||
  const onPress = useCallback(() => navigation.push('Tab-Shared-Account', { account }), [])
 | 
			
		||||
  if (!props) {
 | 
			
		||||
    props = { onPress: () => navigation.push('Tab-Shared-Account', { account }) }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Pressable
 | 
			
		||||
      accessibilityRole='button'
 | 
			
		||||
      style={{
 | 
			
		||||
  return React.createElement(
 | 
			
		||||
    Component || Pressable,
 | 
			
		||||
    {
 | 
			
		||||
      ...props,
 | 
			
		||||
      style: {
 | 
			
		||||
        flex: 1,
 | 
			
		||||
        paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
 | 
			
		||||
        paddingVertical: StyleConstants.Spacing.M,
 | 
			
		||||
        flexDirection: 'row',
 | 
			
		||||
        alignSelf: 'flex-start',
 | 
			
		||||
        justifyContent: 'space-between',
 | 
			
		||||
        alignItems: 'center'
 | 
			
		||||
      }}
 | 
			
		||||
      onPress={customOnPress || onPress}
 | 
			
		||||
    >
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
 | 
			
		||||
      <GracefullyImage
 | 
			
		||||
        uri={{ original: account.avatar, static: account.avatar_static }}
 | 
			
		||||
        style={{
 | 
			
		||||
          alignSelf: 'flex-start',
 | 
			
		||||
          width: StyleConstants.Avatar.S,
 | 
			
		||||
          height: StyleConstants.Avatar.S,
 | 
			
		||||
          borderRadius: 6,
 | 
			
		||||
@@ -62,7 +70,8 @@ const ComponentAccount: React.FC<Props> = ({ account, onPress: customOnPress })
 | 
			
		||||
          @{account.acct}
 | 
			
		||||
        </CustomText>
 | 
			
		||||
      </View>
 | 
			
		||||
    </Pressable>
 | 
			
		||||
    </View>,
 | 
			
		||||
    children
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ const TimelineEmpty = React.memo(
 | 
			
		||||
                style={{
 | 
			
		||||
                  marginTop: StyleConstants.Spacing.S,
 | 
			
		||||
                  marginBottom: StyleConstants.Spacing.L,
 | 
			
		||||
                  color: colors.primaryDefault
 | 
			
		||||
                  color: colors.secondary
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                {t('empty.success.message')}
 | 
			
		||||
 
 | 
			
		||||
@@ -46,17 +46,20 @@
 | 
			
		||||
      "language": {
 | 
			
		||||
        "name": "Language"
 | 
			
		||||
      },
 | 
			
		||||
      "lists": {
 | 
			
		||||
        "name": "Lists"
 | 
			
		||||
      "list": {
 | 
			
		||||
        "name": "List: {{list}}"
 | 
			
		||||
      },
 | 
			
		||||
      "listAccounts": {
 | 
			
		||||
        "name": "Users in list: {{list}}"
 | 
			
		||||
      },
 | 
			
		||||
      "listAdd": {
 | 
			
		||||
        "name": "Add a List"
 | 
			
		||||
      },
 | 
			
		||||
      "listEdit": {
 | 
			
		||||
        "name": "Edit List"
 | 
			
		||||
        "name": "Edit List Details"
 | 
			
		||||
      },
 | 
			
		||||
      "list": {
 | 
			
		||||
        "name": "List: {{list}}"
 | 
			
		||||
      "lists": {
 | 
			
		||||
        "name": "Lists"
 | 
			
		||||
      },
 | 
			
		||||
      "push": {
 | 
			
		||||
        "name": "Push Notification"
 | 
			
		||||
@@ -93,7 +96,13 @@
 | 
			
		||||
        "XXL": "XXL"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "listAccounts": {
 | 
			
		||||
      "heading": "Manage users",
 | 
			
		||||
      "error": "Delete user from list",
 | 
			
		||||
      "empty": "No user added in this list"
 | 
			
		||||
    },
 | 
			
		||||
    "listEdit": {
 | 
			
		||||
      "heading": "Edit list details",
 | 
			
		||||
      "title": "Title",
 | 
			
		||||
      "repliesPolicy": {
 | 
			
		||||
        "heading": "Show replies to:",
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ const ComposeRootSuggestion: React.FC<Props> = ({ item }) => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return item.acct ? (
 | 
			
		||||
    <ComponentAccount account={item} onPress={onPress} />
 | 
			
		||||
    <ComponentAccount account={item} props={{ onPress }} />
 | 
			
		||||
  ) : (
 | 
			
		||||
    <ComponentHashtag hashtag={item} onPress={onPress} origin='suggestion' />
 | 
			
		||||
  )
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,9 @@ 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 TabMeListAccounts from './Me/List/Accounts'
 | 
			
		||||
import TabMeListEdit from './Me/List/Edit'
 | 
			
		||||
import TabMeListList from './Me/List/List'
 | 
			
		||||
import TabMeProfile from './Me/Profile'
 | 
			
		||||
import TabMePush from './Me/Push'
 | 
			
		||||
import TabMeRoot from './Me/Root'
 | 
			
		||||
@@ -86,21 +87,28 @@ const TabMe = React.memo(
 | 
			
		||||
            headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
 | 
			
		||||
          })}
 | 
			
		||||
        />
 | 
			
		||||
        <Stack.Screen
 | 
			
		||||
          name='Tab-Me-List-Accounts'
 | 
			
		||||
          component={TabMeListAccounts}
 | 
			
		||||
          options={({ navigation, route: { params } }) => ({
 | 
			
		||||
            title: t('me.stacks.listAccounts.name', { list: params.title }),
 | 
			
		||||
            ...(Platform.OS === 'android' && {
 | 
			
		||||
              headerCenter: () => <HeaderCenter content={t('me.stacks.listsAdd.name')} />
 | 
			
		||||
            }),
 | 
			
		||||
            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')} />
 | 
			
		||||
            })
 | 
			
		||||
            presentation: 'modal'
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
        <Stack.Screen
 | 
			
		||||
          name='Tab-Me-Lists'
 | 
			
		||||
          component={TabMeLists}
 | 
			
		||||
          name='Tab-Me-List-List'
 | 
			
		||||
          component={TabMeListList}
 | 
			
		||||
          options={({ navigation }: any) => ({
 | 
			
		||||
            title: t('me.stacks.lists.name'),
 | 
			
		||||
            ...(Platform.OS === 'android' && {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										99
									
								
								src/screens/Tabs/Me/List/Accounts.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/screens/Tabs/Me/List/Accounts.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
import ComponentAccount from '@components/Account'
 | 
			
		||||
import Button from '@components/Button'
 | 
			
		||||
import haptics from '@components/haptics'
 | 
			
		||||
import { displayMessage } from '@components/Message'
 | 
			
		||||
import CustomText from '@components/Text'
 | 
			
		||||
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
 | 
			
		||||
import {
 | 
			
		||||
  QueryKeyListAccounts,
 | 
			
		||||
  useListAccountsMutation,
 | 
			
		||||
  useListAccountsQuery
 | 
			
		||||
} from '@utils/queryHooks/lists'
 | 
			
		||||
import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import { useTranslation } from 'react-i18next'
 | 
			
		||||
import { FlatList, View } from 'react-native'
 | 
			
		||||
 | 
			
		||||
const TabMeListAccounts: React.FC<TabMeStackScreenProps<'Tab-Me-List-Accounts'>> = ({
 | 
			
		||||
  route: { params }
 | 
			
		||||
}) => {
 | 
			
		||||
  const { colors, theme } = useTheme()
 | 
			
		||||
  const { t } = useTranslation('screenTabs')
 | 
			
		||||
 | 
			
		||||
  const queryKey: QueryKeyListAccounts = ['ListAccounts', { id: params.id }]
 | 
			
		||||
  const { data, refetch, fetchNextPage, hasNextPage } = useListAccountsQuery({
 | 
			
		||||
    ...queryKey[1],
 | 
			
		||||
    options: {
 | 
			
		||||
      getNextPageParam: lastPage =>
 | 
			
		||||
        lastPage?.links?.next && {
 | 
			
		||||
          max_id: lastPage.links.next
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const flattenData = data?.pages ? data.pages?.flatMap(page => [...page.body]) : []
 | 
			
		||||
 | 
			
		||||
  const mutation = useListAccountsMutation({
 | 
			
		||||
    onSuccess: () => {
 | 
			
		||||
      haptics('Light')
 | 
			
		||||
      refetch()
 | 
			
		||||
    },
 | 
			
		||||
    onError: () => {
 | 
			
		||||
      displayMessage({
 | 
			
		||||
        theme,
 | 
			
		||||
        type: 'error',
 | 
			
		||||
        message: t('common:message.error.message', {
 | 
			
		||||
          function: t('me.listAccounts.error')
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <FlatList
 | 
			
		||||
      data={flattenData}
 | 
			
		||||
      renderItem={({ item, index }) => (
 | 
			
		||||
        <ComponentAccount
 | 
			
		||||
          key={index}
 | 
			
		||||
          account={item}
 | 
			
		||||
          Component={View}
 | 
			
		||||
          children={
 | 
			
		||||
            <Button
 | 
			
		||||
              type='icon'
 | 
			
		||||
              content='X'
 | 
			
		||||
              round
 | 
			
		||||
              onPress={() =>
 | 
			
		||||
                mutation.mutate({ type: 'delete', payload: { id: params.id, accounts: [item.id] } })
 | 
			
		||||
              }
 | 
			
		||||
            />
 | 
			
		||||
          }
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
      ListEmptyComponent={
 | 
			
		||||
        <View
 | 
			
		||||
          style={{
 | 
			
		||||
            flex: 1,
 | 
			
		||||
            minHeight: '100%',
 | 
			
		||||
            justifyContent: 'center',
 | 
			
		||||
            alignItems: 'center'
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <CustomText
 | 
			
		||||
            fontStyle='M'
 | 
			
		||||
            style={{
 | 
			
		||||
              marginTop: StyleConstants.Spacing.S,
 | 
			
		||||
              marginBottom: StyleConstants.Spacing.L,
 | 
			
		||||
              color: colors.secondary
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            {t('me.listAccounts.empty')}
 | 
			
		||||
          </CustomText>
 | 
			
		||||
        </View>
 | 
			
		||||
      }
 | 
			
		||||
      onEndReached={() => hasNextPage && fetchNextPage()}
 | 
			
		||||
    />
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default TabMeListAccounts
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { EmojisState } from '@components/Emojis/helpers/EmojisContext'
 | 
			
		||||
import haptics from '@components/haptics'
 | 
			
		||||
import { HeaderLeft, HeaderRight } from '@components/Header'
 | 
			
		||||
import { HeaderCenter, HeaderLeft, HeaderRight } from '@components/Header'
 | 
			
		||||
import ComponentInput from '@components/Input'
 | 
			
		||||
import { displayMessage, Message } from '@components/Message'
 | 
			
		||||
import Selections from '@components/Selections'
 | 
			
		||||
@@ -12,7 +12,7 @@ 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 { Alert, Platform, ScrollView, TextInput } from 'react-native'
 | 
			
		||||
import { useQueryClient } from 'react-query'
 | 
			
		||||
 | 
			
		||||
const TabMeListEdit: React.FC<TabMeStackScreenProps<'Tab-Me-List-Edit'>> = ({
 | 
			
		||||
@@ -83,6 +83,16 @@ const TabMeListEdit: React.FC<TabMeStackScreenProps<'Tab-Me-List-Edit'>> = ({
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    navigation.setOptions({
 | 
			
		||||
      title: params.type === 'add' ? t('me.stacks.listAdd.name') : t('me.stacks.listEdit.name'),
 | 
			
		||||
      ...(Platform.OS === 'android' && {
 | 
			
		||||
        headerCenter: () => (
 | 
			
		||||
          <HeaderCenter
 | 
			
		||||
            content={
 | 
			
		||||
              params.type === 'add' ? t('me.stacks.listAdd.name') : t('me.stacks.listEdit.name')
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
        )
 | 
			
		||||
      }),
 | 
			
		||||
      headerLeft: () => (
 | 
			
		||||
        <HeaderLeft
 | 
			
		||||
          content='X'
 | 
			
		||||
@@ -5,7 +5,7 @@ import { useListsQuery } from '@utils/queryHooks/lists'
 | 
			
		||||
import React, { useEffect } from 'react'
 | 
			
		||||
import { useTranslation } from 'react-i18next'
 | 
			
		||||
 | 
			
		||||
const TabMeLists: React.FC<TabMeStackScreenProps<'Tab-Me-Lists'>> = ({ navigation }) => {
 | 
			
		||||
const TabMeListList: React.FC<TabMeStackScreenProps<'Tab-Me-List-List'>> = ({ navigation }) => {
 | 
			
		||||
  const { data } = useListsQuery({})
 | 
			
		||||
  const { t } = useTranslation('screenTabs')
 | 
			
		||||
 | 
			
		||||
@@ -23,17 +23,17 @@ const TabMeLists: React.FC<TabMeStackScreenProps<'Tab-Me-Lists'>> = ({ navigatio
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <MenuContainer>
 | 
			
		||||
      {data?.map((d: Mastodon.List, i: number) => (
 | 
			
		||||
      {data?.map((params, index) => (
 | 
			
		||||
        <MenuRow
 | 
			
		||||
          key={i}
 | 
			
		||||
          key={index}
 | 
			
		||||
          iconFront='List'
 | 
			
		||||
          iconBack='ChevronRight'
 | 
			
		||||
          title={d.title}
 | 
			
		||||
          onPress={() => navigation.navigate('Tab-Me-List', d)}
 | 
			
		||||
          title={params.title}
 | 
			
		||||
          onPress={() => navigation.navigate('Tab-Me-List', params)}
 | 
			
		||||
        />
 | 
			
		||||
      ))}
 | 
			
		||||
    </MenuContainer>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default TabMeLists
 | 
			
		||||
export default TabMeListList
 | 
			
		||||
@@ -9,9 +9,9 @@ import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import React, { useEffect } from 'react'
 | 
			
		||||
import { useTranslation } from 'react-i18next'
 | 
			
		||||
import { Alert } from 'react-native'
 | 
			
		||||
import { useQueryClient } from 'react-query'
 | 
			
		||||
import * as DropdownMenu from 'zeego/dropdown-menu'
 | 
			
		||||
import { menuListAccounts, menuListDelete, menuListEdit } from './menus'
 | 
			
		||||
 | 
			
		||||
const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({
 | 
			
		||||
  navigation,
 | 
			
		||||
@@ -40,6 +40,10 @@ const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const listAccounts = menuListAccounts({ params })
 | 
			
		||||
    const listEdit = menuListEdit({ params, key })
 | 
			
		||||
    const listDelete = menuListDelete({ params, mutation })
 | 
			
		||||
 | 
			
		||||
    navigation.setOptions({
 | 
			
		||||
      headerRight: () => (
 | 
			
		||||
        <DropdownMenu.Root>
 | 
			
		||||
@@ -52,41 +56,24 @@ const TabMeList: React.FC<TabMeStackScreenProps<'Tab-Me-List'>> = ({
 | 
			
		||||
          </DropdownMenu.Trigger>
 | 
			
		||||
 | 
			
		||||
          <DropdownMenu.Content>
 | 
			
		||||
            <DropdownMenu.Item
 | 
			
		||||
              key='list-edit'
 | 
			
		||||
              onSelect={() =>
 | 
			
		||||
                navigation.navigate('Tab-Me-List-Edit', {
 | 
			
		||||
                  type: 'edit',
 | 
			
		||||
                  payload: params,
 | 
			
		||||
                  key
 | 
			
		||||
                })
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
              <DropdownMenu.ItemTitle children={t('me.stacks.listEdit.name')} />
 | 
			
		||||
              <DropdownMenu.ItemIcon iosIconName='square.and.pencil' />
 | 
			
		||||
            </DropdownMenu.Item>
 | 
			
		||||
            <DropdownMenu.Group>
 | 
			
		||||
              <DropdownMenu.Item key={listAccounts.key} onSelect={listAccounts.onSelect}>
 | 
			
		||||
                <DropdownMenu.ItemTitle children={listAccounts.title} />
 | 
			
		||||
                <DropdownMenu.ItemIcon iosIconName={listAccounts.icon} />
 | 
			
		||||
              </DropdownMenu.Item>
 | 
			
		||||
            </DropdownMenu.Group>
 | 
			
		||||
 | 
			
		||||
            <DropdownMenu.Item
 | 
			
		||||
              key='list-delete'
 | 
			
		||||
              destructive
 | 
			
		||||
              onSelect={() =>
 | 
			
		||||
                Alert.alert(
 | 
			
		||||
                  t('me.listDelete.confirm.title', { list: params.title.slice(0, 6) }),
 | 
			
		||||
                  t('me.listDelete.confirm.message'),
 | 
			
		||||
                  [
 | 
			
		||||
                    {
 | 
			
		||||
                      text: t('common:buttons.delete'),
 | 
			
		||||
                      style: 'destructive',
 | 
			
		||||
                      onPress: () => mutation.mutate({ type: 'delete', payload: params })
 | 
			
		||||
                    },
 | 
			
		||||
                    { text: t('common:buttons.cancel') }
 | 
			
		||||
                  ]
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
              <DropdownMenu.ItemTitle children={t('me.listDelete.heading')} />
 | 
			
		||||
              <DropdownMenu.ItemIcon iosIconName='trash' />
 | 
			
		||||
            </DropdownMenu.Item>
 | 
			
		||||
            <DropdownMenu.Group>
 | 
			
		||||
              <DropdownMenu.Item key={listEdit.key} onSelect={listEdit.onSelect}>
 | 
			
		||||
                <DropdownMenu.ItemTitle children={listEdit.title} />
 | 
			
		||||
                <DropdownMenu.ItemIcon iosIconName={listEdit.icon} />
 | 
			
		||||
              </DropdownMenu.Item>
 | 
			
		||||
 | 
			
		||||
              <DropdownMenu.Item key={listDelete.key} destructive onSelect={listDelete.onSelect}>
 | 
			
		||||
                <DropdownMenu.ItemTitle children={listDelete.title} />
 | 
			
		||||
                <DropdownMenu.ItemIcon iosIconName={listDelete.icon} />
 | 
			
		||||
              </DropdownMenu.Item>
 | 
			
		||||
            </DropdownMenu.Group>
 | 
			
		||||
          </DropdownMenu.Content>
 | 
			
		||||
        </DropdownMenu.Root>
 | 
			
		||||
      )
 | 
			
		||||
							
								
								
									
										48
									
								
								src/screens/Tabs/Me/List/menus.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/screens/Tabs/Me/List/menus.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
import navigationRef from '@helpers/navigationRef'
 | 
			
		||||
import i18next from 'i18next'
 | 
			
		||||
import { Alert } from 'react-native'
 | 
			
		||||
import { UseMutationResult } from 'react-query'
 | 
			
		||||
 | 
			
		||||
export const menuListAccounts = ({ params }: { params: Mastodon.List }) => ({
 | 
			
		||||
  key: 'list-accounts',
 | 
			
		||||
  onSelect: () => navigationRef.navigate<any>('Tab-Me-List-Accounts', params),
 | 
			
		||||
  title: i18next.t('screenTabs:me.listAccounts.heading'),
 | 
			
		||||
  icon: 'person.crop.circle.fill.badge.checkmark'
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const menuListEdit = ({ params, key }: { params: Mastodon.List; key: string }) => ({
 | 
			
		||||
  key: 'list-edit',
 | 
			
		||||
  onSelect: () =>
 | 
			
		||||
    navigationRef.navigate<any>('Tab-Me-List-Edit', {
 | 
			
		||||
      type: 'edit',
 | 
			
		||||
      payload: params,
 | 
			
		||||
      key
 | 
			
		||||
    }),
 | 
			
		||||
  title: i18next.t('screenTabs:me.listEdit.heading'),
 | 
			
		||||
  icon: 'square.and.pencil'
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const menuListDelete = ({
 | 
			
		||||
  params,
 | 
			
		||||
  mutation
 | 
			
		||||
}: {
 | 
			
		||||
  params: Mastodon.List
 | 
			
		||||
  mutation: UseMutationResult<any, any, unknown, unknown>
 | 
			
		||||
}) => ({
 | 
			
		||||
  key: 'list-delete',
 | 
			
		||||
  onSelect: () =>
 | 
			
		||||
    Alert.alert(
 | 
			
		||||
      i18next.t('screenTabs:me.listDelete.confirm.title', { list: params.title.slice(0, 6) }),
 | 
			
		||||
      i18next.t('screenTabs:me.listDelete.confirm.message'),
 | 
			
		||||
      [
 | 
			
		||||
        {
 | 
			
		||||
          text: i18next.t('common:buttons.delete'),
 | 
			
		||||
          style: 'destructive',
 | 
			
		||||
          onPress: () => mutation.mutate({ type: 'delete', payload: params })
 | 
			
		||||
        },
 | 
			
		||||
        { text: i18next.t('common:buttons.cancel') }
 | 
			
		||||
      ]
 | 
			
		||||
    ),
 | 
			
		||||
  title: i18next.t('screenTabs:me.listDelete.heading'),
 | 
			
		||||
  icon: 'trash'
 | 
			
		||||
})
 | 
			
		||||
@@ -85,7 +85,7 @@ const Collections: React.FC = () => {
 | 
			
		||||
          iconFront='List'
 | 
			
		||||
          iconBack='ChevronRight'
 | 
			
		||||
          title={t('me.stacks.lists.name')}
 | 
			
		||||
          onPress={() => navigation.navigate('Tab-Me-Lists')}
 | 
			
		||||
          onPress={() => navigation.navigate('Tab-Me-List-List')}
 | 
			
		||||
        />
 | 
			
		||||
      ) : null}
 | 
			
		||||
      {mePage.announcements.shown ? (
 | 
			
		||||
 
 | 
			
		||||
@@ -139,6 +139,7 @@ export type TabMeStackParamList = {
 | 
			
		||||
  'Tab-Me-Conversations': undefined
 | 
			
		||||
  'Tab-Me-Favourites': undefined
 | 
			
		||||
  'Tab-Me-List': Mastodon.List
 | 
			
		||||
  'Tab-Me-List-Accounts': Omit<Mastodon.List, 'replies_policy'>
 | 
			
		||||
  'Tab-Me-List-Edit':
 | 
			
		||||
    | {
 | 
			
		||||
        type: 'add'
 | 
			
		||||
@@ -148,7 +149,7 @@ export type TabMeStackParamList = {
 | 
			
		||||
        payload: Mastodon.List
 | 
			
		||||
        key: string // To update title after successful mutation
 | 
			
		||||
      }
 | 
			
		||||
  'Tab-Me-Lists': undefined
 | 
			
		||||
  'Tab-Me-List-List': undefined
 | 
			
		||||
  'Tab-Me-Profile': undefined
 | 
			
		||||
  'Tab-Me-Push': undefined
 | 
			
		||||
  'Tab-Me-Settings': undefined
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,14 @@
 | 
			
		||||
import apiInstance from '@api/instance'
 | 
			
		||||
import apiInstance, { InstanceResponse } from '@api/instance'
 | 
			
		||||
import { AxiosError } from 'axios'
 | 
			
		||||
import { useMutation, UseMutationOptions, useQuery, UseQueryOptions } from 'react-query'
 | 
			
		||||
import {
 | 
			
		||||
  QueryFunctionContext,
 | 
			
		||||
  useInfiniteQuery,
 | 
			
		||||
  UseInfiniteQueryOptions,
 | 
			
		||||
  useMutation,
 | 
			
		||||
  UseMutationOptions,
 | 
			
		||||
  useQuery,
 | 
			
		||||
  UseQueryOptions
 | 
			
		||||
} from 'react-query'
 | 
			
		||||
 | 
			
		||||
export type QueryKeyLists = ['Lists']
 | 
			
		||||
 | 
			
		||||
@@ -53,10 +61,10 @@ const mutationFunction = async (params: MutationVarsLists) => {
 | 
			
		||||
        body
 | 
			
		||||
      }).then(res => res.body)
 | 
			
		||||
    case 'delete':
 | 
			
		||||
      return apiInstance({
 | 
			
		||||
      return apiInstance<{}>({
 | 
			
		||||
        method: 'delete',
 | 
			
		||||
        url: `lists/${params.payload.id}`
 | 
			
		||||
      })
 | 
			
		||||
      }).then(res => res.body)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -66,4 +74,54 @@ const useListsMutation = (
 | 
			
		||||
  return useMutation(mutationFunction, options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { useListsQuery, useListsMutation }
 | 
			
		||||
/* ----- */
 | 
			
		||||
 | 
			
		||||
export type QueryKeyListAccounts = ['ListAccounts', { id: Mastodon.List['id'] }]
 | 
			
		||||
 | 
			
		||||
const accountsQueryFunction = async ({
 | 
			
		||||
  queryKey,
 | 
			
		||||
  pageParam
 | 
			
		||||
}: QueryFunctionContext<QueryKeyListAccounts>) => {
 | 
			
		||||
  const { id } = queryKey[1]
 | 
			
		||||
 | 
			
		||||
  return await apiInstance<Mastodon.Account[]>({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: `lists/${id}/accounts`,
 | 
			
		||||
    params: { ...pageParam, limit: 40 }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const useListAccountsQuery = ({
 | 
			
		||||
  options,
 | 
			
		||||
  ...queryKeyParams
 | 
			
		||||
}: QueryKeyListAccounts[1] & {
 | 
			
		||||
  options?: UseInfiniteQueryOptions<InstanceResponse<Mastodon.Account[]>, AxiosError>
 | 
			
		||||
}) => {
 | 
			
		||||
  const queryKey: QueryKeyListAccounts = ['ListAccounts', queryKeyParams]
 | 
			
		||||
  return useInfiniteQuery(queryKey, accountsQueryFunction, options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccountsMutationVarsLists = {
 | 
			
		||||
  type: 'add' | 'delete'
 | 
			
		||||
  payload: Pick<Mastodon.List, 'id'> & { accounts: Mastodon.Account['id'][] }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const accountsMutationFunction = async (params: AccountsMutationVarsLists) => {
 | 
			
		||||
  const body = new FormData()
 | 
			
		||||
  for (const account of params.payload.accounts) {
 | 
			
		||||
    body.append('account_ids[]', account)
 | 
			
		||||
  }
 | 
			
		||||
  return apiInstance<{}>({
 | 
			
		||||
    method: params.type === 'add' ? 'post' : 'delete',
 | 
			
		||||
    url: `lists/${params.payload.id}/accounts`,
 | 
			
		||||
    body
 | 
			
		||||
  }).then(res => res.body)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const useListAccountsMutation = (
 | 
			
		||||
  options: UseMutationOptions<Mastodon.List, AxiosError, AccountsMutationVarsLists>
 | 
			
		||||
) => {
 | 
			
		||||
  return useMutation(accountsMutationFunction, options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { useListsQuery, useListsMutation, useListAccountsQuery, useListAccountsMutation }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user