mirror of https://github.com/tooot-app/app
Merge branch 'main' into candidate
This commit is contained in:
commit
c4e94c6f5a
|
@ -1,6 +1,7 @@
|
||||||
Enjoy toooting! This version includes following improvements and fixes:
|
Enjoy toooting! This version includes following improvements and fixes:
|
||||||
- Auto fetch remote content in conversations!
|
- Auto fetch remote content in conversations!
|
||||||
- Remember last read position in timeline!
|
- Remember last read position in timeline!
|
||||||
|
- Follow a user with other logged in accounts
|
||||||
- Allowing adding more context of reports
|
- Allowing adding more context of reports
|
||||||
- Option to disable autoplay gif
|
- Option to disable autoplay gif
|
||||||
- Hide boosts from users
|
- Hide boosts from users
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
toooting愉快!此版本包括以下改进和修复:
|
toooting愉快!此版本包括以下改进和修复:
|
||||||
- 主动获取对话的远程内容
|
- 主动获取对话的远程内容
|
||||||
- 自动加载上次我的关注的阅读位置
|
- 自动加载上次我的关注的阅读位置
|
||||||
|
- 用其它已登陆的账户关注用户
|
||||||
- 可添加举报细节
|
- 可添加举报细节
|
||||||
- 新增暂停自动播放gif动画选项
|
- 新增暂停自动播放gif动画选项
|
||||||
- 隐藏用户的转嘟
|
- 隐藏用户的转嘟
|
||||||
|
|
|
@ -1,44 +1,30 @@
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { generateAccountKey, getAccountDetails, setAccount } from '@utils/storage/actions'
|
import { ReadableAccountType, setAccount } from '@utils/storage/actions'
|
||||||
import { StorageGlobal } from '@utils/storage/global'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Button from './Button'
|
import Button from './Button'
|
||||||
import haptics from './haptics'
|
import haptics from './haptics'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
account: NonNullable<StorageGlobal['accounts']>[number]
|
account: ReadableAccountType
|
||||||
selected?: boolean
|
|
||||||
additionalActions?: () => void
|
additionalActions?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const AccountButton: React.FC<Props> = ({ account, selected = false, additionalActions }) => {
|
const AccountButton: React.FC<Props> = ({ account, additionalActions }) => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const accountDetails = getAccountDetails(
|
|
||||||
['auth.domain', 'auth.account.acct', 'auth.account.domain', 'auth.account.id'],
|
|
||||||
account
|
|
||||||
)
|
|
||||||
if (!accountDetails) return null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
selected={selected}
|
selected={account.active}
|
||||||
style={{
|
style={{
|
||||||
marginBottom: StyleConstants.Spacing.M,
|
marginBottom: StyleConstants.Spacing.M,
|
||||||
marginRight: StyleConstants.Spacing.M
|
marginRight: StyleConstants.Spacing.M
|
||||||
}}
|
}}
|
||||||
content={`@${accountDetails['auth.account.acct']}@${accountDetails['auth.account.domain']}${
|
content={account.acct}
|
||||||
selected ? ' ✓' : ''
|
|
||||||
}`}
|
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
haptics('Light')
|
haptics('Light')
|
||||||
setAccount(
|
setAccount(account.key)
|
||||||
generateAccountKey({
|
|
||||||
domain: accountDetails['auth.domain'],
|
|
||||||
id: accountDetails['auth.account.id']
|
|
||||||
})
|
|
||||||
)
|
|
||||||
navigation.goBack()
|
navigation.goBack()
|
||||||
if (additionalActions) {
|
if (additionalActions) {
|
||||||
additionalActions()
|
additionalActions()
|
||||||
|
|
|
@ -97,7 +97,7 @@ const Button: React.FC<Props> = ({
|
||||||
fontSize: StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
|
fontSize: StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
|
||||||
opacity: loading ? 0 : 1
|
opacity: loading ? 0 : 1
|
||||||
}}
|
}}
|
||||||
fontWeight={fontBold ? 'Bold' : 'Normal'}
|
fontWeight={fontBold || selected ? 'Bold' : 'Normal'}
|
||||||
children={content}
|
children={content}
|
||||||
testID='text'
|
testID='text'
|
||||||
/>
|
/>
|
||||||
|
@ -125,7 +125,7 @@ const Button: React.FC<Props> = ({
|
||||||
borderRadius: 100,
|
borderRadius: 100,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
borderWidth: overlay ? 0 : 1,
|
borderWidth: overlay ? 0 : selected ? 1.5 : 1,
|
||||||
borderColor: mainColor(),
|
borderColor: mainColor(),
|
||||||
backgroundColor: overlay ? colors.backgroundOverlayInvert : colors.backgroundDefault,
|
backgroundColor: overlay ? colors.backgroundOverlayInvert : colors.backgroundDefault,
|
||||||
paddingVertical: StyleConstants.Spacing[spacing],
|
paddingVertical: StyleConstants.Spacing[spacing],
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { StorageAccount } from '@utils/storage/account'
|
||||||
import {
|
import {
|
||||||
generateAccountKey,
|
generateAccountKey,
|
||||||
getGlobalStorage,
|
getGlobalStorage,
|
||||||
|
setAccount,
|
||||||
setAccountStorage,
|
setAccountStorage,
|
||||||
setGlobalStorage
|
setGlobalStorage
|
||||||
} from '@utils/storage/actions'
|
} from '@utils/storage/actions'
|
||||||
|
@ -95,7 +96,10 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
scopes: ['read', 'write', 'follow', 'push'],
|
scopes: ['read', 'write', 'follow', 'push'],
|
||||||
redirectUri,
|
redirectUri,
|
||||||
code: promptResult.params.code,
|
code: promptResult.params.code,
|
||||||
extraParams: { grant_type: 'authorization_code' }
|
extraParams: {
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
...(request.codeVerifier && { code_verifier: request.codeVerifier })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ tokenEndpoint: `https://${variables.domain}/oauth/token` }
|
{ tokenEndpoint: `https://${variables.domain}/oauth/token` }
|
||||||
)
|
)
|
||||||
|
@ -175,12 +179,11 @@ const ComponentInstance: React.FC<Props> = ({
|
||||||
})),
|
})),
|
||||||
accountKey
|
accountKey
|
||||||
)
|
)
|
||||||
storage.account = new MMKV({ id: accountKey })
|
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
setGlobalStorage('accounts', accounts?.concat([accountKey]))
|
setGlobalStorage('accounts', accounts?.concat([accountKey]))
|
||||||
}
|
}
|
||||||
setGlobalStorage('account.active', accountKey)
|
setAccount(accountKey)
|
||||||
|
|
||||||
goBack && navigation.goBack()
|
goBack && navigation.goBack()
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ const Message = React.forwardRef<FlashMessage>((_, ref) => {
|
||||||
...StyleConstants.FontStyle.S
|
...StyleConstants.FontStyle.S
|
||||||
}}
|
}}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
textProps={{ numberOfLines: 2 }}
|
textProps={{ numberOfLines: 3 }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -192,16 +192,48 @@ const TimelineDefault: React.FC<Props> = ({
|
||||||
</ContextMenu.Trigger>
|
</ContextMenu.Trigger>
|
||||||
|
|
||||||
<ContextMenu.Content>
|
<ContextMenu.Content>
|
||||||
{[mShare, mStatus, mInstance].map((type, i) => (
|
{[mShare, mStatus, mInstance].map((menu, i) => (
|
||||||
<Fragment key={i}>
|
<Fragment key={i}>
|
||||||
{type.map((mGroup, index) => (
|
{menu.map((group, index) => (
|
||||||
<ContextMenu.Group key={index}>
|
<ContextMenu.Group key={index}>
|
||||||
{mGroup.map(menu => (
|
{group.map(item => {
|
||||||
<ContextMenu.Item key={menu.key} {...menu.item}>
|
switch (item.type) {
|
||||||
<ContextMenu.ItemTitle children={menu.title} />
|
case 'item':
|
||||||
<ContextMenu.ItemIcon ios={{ name: menu.icon }} />
|
return (
|
||||||
</ContextMenu.Item>
|
<ContextMenu.Item key={item.key} {...item.props}>
|
||||||
))}
|
<ContextMenu.ItemTitle children={item.title} />
|
||||||
|
{item.icon ? (
|
||||||
|
<ContextMenu.ItemIcon ios={{ name: item.icon }} />
|
||||||
|
) : null}
|
||||||
|
</ContextMenu.Item>
|
||||||
|
)
|
||||||
|
case 'sub':
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<ContextMenu.Sub key={item.key}>
|
||||||
|
<ContextMenu.SubTrigger
|
||||||
|
key={item.trigger.key}
|
||||||
|
{...item.trigger.props}
|
||||||
|
>
|
||||||
|
<ContextMenu.ItemTitle children={item.trigger.title} />
|
||||||
|
{item.trigger.icon ? (
|
||||||
|
<ContextMenu.ItemIcon ios={{ name: item.trigger.icon }} />
|
||||||
|
) : null}
|
||||||
|
</ContextMenu.SubTrigger>
|
||||||
|
<ContextMenu.SubContent>
|
||||||
|
{item.items.map(sub => (
|
||||||
|
<ContextMenu.Item key={sub.key} {...sub.props}>
|
||||||
|
<ContextMenu.ItemTitle children={sub.title} />
|
||||||
|
{sub.icon ? (
|
||||||
|
<ContextMenu.ItemIcon ios={{ name: sub.icon }} />
|
||||||
|
) : null}
|
||||||
|
</ContextMenu.Item>
|
||||||
|
))}
|
||||||
|
</ContextMenu.SubContent>
|
||||||
|
</ContextMenu.Sub>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
</ContextMenu.Group>
|
</ContextMenu.Group>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -162,16 +162,43 @@ const TimelineNotifications: React.FC<Props> = ({ notification, queryKey }) => {
|
||||||
</ContextMenu.Trigger>
|
</ContextMenu.Trigger>
|
||||||
|
|
||||||
<ContextMenu.Content>
|
<ContextMenu.Content>
|
||||||
{[mShare, mStatus, mInstance].map((type, i) => (
|
{[mShare, mStatus, mInstance].map((menu, i) => (
|
||||||
<Fragment key={i}>
|
<Fragment key={i}>
|
||||||
{type.map((mGroup, index) => (
|
{menu.map((group, index) => (
|
||||||
<ContextMenu.Group key={index}>
|
<ContextMenu.Group key={index}>
|
||||||
{mGroup.map(menu => (
|
{group.map(item => {
|
||||||
<ContextMenu.Item key={menu.key} {...menu.item}>
|
switch (item.type) {
|
||||||
<ContextMenu.ItemTitle children={menu.title} />
|
case 'item':
|
||||||
<ContextMenu.ItemIcon ios={{ name: menu.icon }} />
|
return (
|
||||||
</ContextMenu.Item>
|
<ContextMenu.Item key={item.key} {...item.props}>
|
||||||
))}
|
<ContextMenu.ItemTitle children={item.title} />
|
||||||
|
{item.icon ? <ContextMenu.ItemIcon ios={{ name: item.icon }} /> : null}
|
||||||
|
</ContextMenu.Item>
|
||||||
|
)
|
||||||
|
case 'sub':
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<ContextMenu.Sub key={item.key}>
|
||||||
|
<ContextMenu.SubTrigger key={item.trigger.key} {...item.trigger.props}>
|
||||||
|
<ContextMenu.ItemTitle children={item.trigger.title} />
|
||||||
|
{item.trigger.icon ? (
|
||||||
|
<ContextMenu.ItemIcon ios={{ name: item.trigger.icon }} />
|
||||||
|
) : null}
|
||||||
|
</ContextMenu.SubTrigger>
|
||||||
|
<ContextMenu.SubContent>
|
||||||
|
{item.items.map(sub => (
|
||||||
|
<ContextMenu.Item key={sub.key} {...sub.props}>
|
||||||
|
<ContextMenu.ItemTitle children={sub.title} />
|
||||||
|
{sub.icon ? (
|
||||||
|
<ContextMenu.ItemIcon ios={{ name: sub.icon }} />
|
||||||
|
) : null}
|
||||||
|
</ContextMenu.Item>
|
||||||
|
))}
|
||||||
|
</ContextMenu.SubContent>
|
||||||
|
</ContextMenu.Sub>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
</ContextMenu.Group>
|
</ContextMenu.Group>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -28,7 +28,7 @@ const TimelineActions: React.FC = () => {
|
||||||
const navigationState = useNavState()
|
const navigationState = useNavState()
|
||||||
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
const { t } = useTranslation(['common', 'componentTimeline'])
|
const { t } = useTranslation(['common', 'componentTimeline'])
|
||||||
const { colors, theme } = useTheme()
|
const { colors } = useTheme()
|
||||||
const iconColor = colors.secondary
|
const iconColor = colors.secondary
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
@ -56,7 +56,6 @@ const TimelineActions: React.FC = () => {
|
||||||
onError: (err: any, params) => {
|
onError: (err: any, params) => {
|
||||||
const correctParam = params as MutationVarsTimelineUpdateStatusProperty
|
const correctParam = params as MutationVarsTimelineUpdateStatusProperty
|
||||||
displayMessage({
|
displayMessage({
|
||||||
theme,
|
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: t('common:message.error.message', {
|
message: t('common:message.error.message', {
|
||||||
function: t(
|
function: t(
|
||||||
|
|
|
@ -10,8 +10,7 @@ import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
import StatusContext from './Context'
|
import StatusContext from './Context'
|
||||||
|
|
||||||
const TimelineHeaderAndroid: React.FC = () => {
|
const TimelineHeaderAndroid: React.FC = () => {
|
||||||
const { queryKey, status, disableDetails, disableOnPress, rawContent } =
|
const { queryKey, status, disableDetails, disableOnPress, rawContent } = useContext(StatusContext)
|
||||||
useContext(StatusContext)
|
|
||||||
|
|
||||||
if (Platform.OS !== 'android' || !status || disableDetails || disableOnPress) return null
|
if (Platform.OS !== 'android' || !status || disableDetails || disableOnPress) return null
|
||||||
|
|
||||||
|
@ -52,16 +51,48 @@ const TimelineHeaderAndroid: React.FC = () => {
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
{[mShare, mAccount, mStatus].map((type, i) => (
|
{[mShare, mAccount, mStatus].map((menu, i) => (
|
||||||
<Fragment key={i}>
|
<Fragment key={i}>
|
||||||
{type.map((mGroup, index) => (
|
{menu.map((group, index) => (
|
||||||
<DropdownMenu.Group key={index}>
|
<DropdownMenu.Group key={index}>
|
||||||
{mGroup.map(menu => (
|
{group.map(item => {
|
||||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
switch (item.type) {
|
||||||
<DropdownMenu.ItemTitle children={menu.title} />
|
case 'item':
|
||||||
<DropdownMenu.ItemIcon ios={{ name: menu.icon }} />
|
return (
|
||||||
</DropdownMenu.Item>
|
<DropdownMenu.Item key={item.key} {...item.props}>
|
||||||
))}
|
<DropdownMenu.ItemTitle children={item.title} />
|
||||||
|
{item.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: item.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)
|
||||||
|
case 'sub':
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<DropdownMenu.Sub key={item.key}>
|
||||||
|
<DropdownMenu.SubTrigger
|
||||||
|
key={item.trigger.key}
|
||||||
|
{...item.trigger.props}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemTitle children={item.trigger.title} />
|
||||||
|
{item.trigger.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: item.trigger.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent>
|
||||||
|
{item.items.map(sub => (
|
||||||
|
<DropdownMenu.Item key={sub.key} {...sub.props}>
|
||||||
|
<DropdownMenu.ItemTitle children={sub.title} />
|
||||||
|
{sub.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: sub.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -17,8 +17,7 @@ import HeaderSharedReplies from './HeaderShared/Replies'
|
||||||
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
import HeaderSharedVisibility from './HeaderShared/Visibility'
|
||||||
|
|
||||||
const TimelineHeaderDefault: React.FC = () => {
|
const TimelineHeaderDefault: React.FC = () => {
|
||||||
const { queryKey, status, disableDetails, rawContent, isRemote } =
|
const { queryKey, status, disableDetails, rawContent, isRemote } = useContext(StatusContext)
|
||||||
useContext(StatusContext)
|
|
||||||
if (!status) return null
|
if (!status) return null
|
||||||
|
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
@ -88,16 +87,48 @@ const TimelineHeaderDefault: React.FC = () => {
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
{[mShare, mAccount, mStatus].map((type, i) => (
|
{[mShare, mAccount, mStatus].map((menu, i) => (
|
||||||
<Fragment key={i}>
|
<Fragment key={i}>
|
||||||
{type.map((mGroup, index) => (
|
{menu.map((group, index) => (
|
||||||
<DropdownMenu.Group key={index}>
|
<DropdownMenu.Group key={index}>
|
||||||
{mGroup.map(menu => (
|
{group.map(item => {
|
||||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
switch (item.type) {
|
||||||
<DropdownMenu.ItemTitle children={menu.title} />
|
case 'item':
|
||||||
<DropdownMenu.ItemIcon ios={{ name: menu.icon }} />
|
return (
|
||||||
</DropdownMenu.Item>
|
<DropdownMenu.Item key={item.key} {...item.props}>
|
||||||
))}
|
<DropdownMenu.ItemTitle children={item.title} />
|
||||||
|
{item.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: item.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)
|
||||||
|
case 'sub':
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<DropdownMenu.Sub key={item}>
|
||||||
|
<DropdownMenu.SubTrigger
|
||||||
|
key={item.trigger.key}
|
||||||
|
{...item.trigger.props}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemTitle children={item.trigger.title} />
|
||||||
|
{item.trigger.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: item.trigger.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent>
|
||||||
|
{item.items.map(sub => (
|
||||||
|
<DropdownMenu.Item key={sub.key} {...sub.props}>
|
||||||
|
<DropdownMenu.ItemTitle children={sub.title} />
|
||||||
|
{sub.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: sub.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -88,16 +88,50 @@ const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => {
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
{[mShare, mStatus, mAccount, mInstance].map((type, i) => (
|
{[mShare, mStatus, mAccount, mInstance].map((menu, i) => (
|
||||||
<Fragment key={i}>
|
<Fragment key={i}>
|
||||||
{type.map((mGroup, index) => (
|
{menu.map((group, index) => (
|
||||||
<DropdownMenu.Group key={index}>
|
<DropdownMenu.Group key={index}>
|
||||||
{mGroup.map(menu => (
|
{group.map(item => {
|
||||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
switch (item.type) {
|
||||||
<DropdownMenu.ItemTitle children={menu.title} />
|
case 'item':
|
||||||
<DropdownMenu.ItemIcon ios={{ name: menu.icon }} />
|
return (
|
||||||
</DropdownMenu.Item>
|
<DropdownMenu.Item key={item.key} {...item.props}>
|
||||||
))}
|
<DropdownMenu.ItemTitle children={item.title} />
|
||||||
|
{item.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: item.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)
|
||||||
|
case 'sub':
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<DropdownMenu.Sub key={item.key}>
|
||||||
|
<DropdownMenu.SubTrigger
|
||||||
|
key={item.trigger.key}
|
||||||
|
{...item.trigger.props}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemTitle children={item.trigger.title} />
|
||||||
|
{item.trigger.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon
|
||||||
|
ios={{ name: item.trigger.icon }}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent>
|
||||||
|
{item.items.map(sub => (
|
||||||
|
<DropdownMenu.Item key={sub.key} {...sub.props}>
|
||||||
|
<DropdownMenu.ItemTitle children={sub.title} />
|
||||||
|
{sub.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: sub.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { displayMessage } from '@components/Message'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||||
import { useQueryClient } from '@tanstack/react-query'
|
import { useQueryClient } from '@tanstack/react-query'
|
||||||
|
import apiInstance from '@utils/api/instance'
|
||||||
import { TabSharedStackParamList, useNavState } from '@utils/navigation/navigators'
|
import { TabSharedStackParamList, useNavState } from '@utils/navigation/navigators'
|
||||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||||
import {
|
import {
|
||||||
|
@ -14,7 +15,7 @@ import {
|
||||||
MutationVarsTimelineUpdateAccountProperty,
|
MutationVarsTimelineUpdateAccountProperty,
|
||||||
useTimelineMutation
|
useTimelineMutation
|
||||||
} from '@utils/queryHooks/timeline'
|
} from '@utils/queryHooks/timeline'
|
||||||
import { useAccountStorage } from '@utils/storage/actions'
|
import { getAccountStorage, getReadableAccounts, useAccountStorage } from '@utils/storage/actions'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert, Platform } from 'react-native'
|
import { Alert, Platform } from 'react-native'
|
||||||
|
@ -29,13 +30,13 @@ const menuAccount = ({
|
||||||
openChange: boolean
|
openChange: boolean
|
||||||
account?: Partial<Mastodon.Account> & Pick<Mastodon.Account, 'id' | 'username' | 'acct' | 'url'>
|
account?: Partial<Mastodon.Account> & Pick<Mastodon.Account, 'id' | 'username' | 'acct' | 'url'>
|
||||||
status?: Mastodon.Status
|
status?: Mastodon.Status
|
||||||
}): ContextMenu[][] => {
|
}): ContextMenu => {
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>()
|
useNavigation<NativeStackNavigationProp<TabSharedStackParamList, any, undefined>>()
|
||||||
const navState = useNavState()
|
const navState = useNavState()
|
||||||
const { t } = useTranslation(['common', 'componentContextMenu', 'componentRelationship'])
|
const { t } = useTranslation(['common', 'componentContextMenu', 'componentRelationship'])
|
||||||
|
|
||||||
const menus: ContextMenu[][] = [[]]
|
const menus: ContextMenu = [[]]
|
||||||
|
|
||||||
const [enabled, setEnabled] = useState(openChange)
|
const [enabled, setEnabled] = useState(openChange)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -135,8 +136,9 @@ const menuAccount = ({
|
||||||
|
|
||||||
if (!ownAccount && Platform.OS !== 'android' && type !== 'account') {
|
if (!ownAccount && Platform.OS !== 'android' && type !== 'account') {
|
||||||
menus[0].push({
|
menus[0].push({
|
||||||
|
type: 'item',
|
||||||
key: 'account-following',
|
key: 'account-following',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
data &&
|
data &&
|
||||||
actualAccount &&
|
actualAccount &&
|
||||||
|
@ -165,8 +167,9 @@ const menuAccount = ({
|
||||||
|
|
||||||
if (!ownAccount) {
|
if (!ownAccount) {
|
||||||
menus[0].push({
|
menus[0].push({
|
||||||
|
type: 'item',
|
||||||
key: 'account-list',
|
key: 'account-list',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () => navigation.navigate('Tab-Shared-Account-In-Lists', { account }),
|
onSelect: () => navigation.navigate('Tab-Shared-Account-In-Lists', { account }),
|
||||||
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
||||||
destructive: false,
|
destructive: false,
|
||||||
|
@ -176,8 +179,9 @@ const menuAccount = ({
|
||||||
icon: 'checklist'
|
icon: 'checklist'
|
||||||
})
|
})
|
||||||
menus[0].push({
|
menus[0].push({
|
||||||
|
type: 'item',
|
||||||
key: 'account-show-boosts',
|
key: 'account-show-boosts',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
actualAccount &&
|
actualAccount &&
|
||||||
relationshipMutation.mutate({
|
relationshipMutation.mutate({
|
||||||
|
@ -196,8 +200,9 @@ const menuAccount = ({
|
||||||
icon: data?.showing_reblogs ? 'rectangle.on.rectangle.slash' : 'rectangle.on.rectangle'
|
icon: data?.showing_reblogs ? 'rectangle.on.rectangle.slash' : 'rectangle.on.rectangle'
|
||||||
})
|
})
|
||||||
menus[0].push({
|
menus[0].push({
|
||||||
|
type: 'item',
|
||||||
key: 'account-mute',
|
key: 'account-mute',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
actualAccount &&
|
actualAccount &&
|
||||||
timelineMutation.mutate({
|
timelineMutation.mutate({
|
||||||
|
@ -216,58 +221,139 @@ const menuAccount = ({
|
||||||
icon: data?.muting ? 'eye' : 'eye.slash'
|
icon: data?.muting ? 'eye' : 'eye.slash'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const followAs = () => {
|
||||||
|
if (type !== 'account') return
|
||||||
|
const accounts = getReadableAccounts()
|
||||||
|
menus[0].push({
|
||||||
|
type: 'sub',
|
||||||
|
key: 'account-follow-as',
|
||||||
|
trigger: {
|
||||||
|
key: 'account-follow-as',
|
||||||
|
props: { destructive: false, disabled: false, hidden: !accounts.length },
|
||||||
|
title: t('componentContextMenu:account.followAs.trigger'),
|
||||||
|
icon: 'person.badge.plus'
|
||||||
|
},
|
||||||
|
items: accounts.map(a => ({
|
||||||
|
key: `account-${a.key}`,
|
||||||
|
props: {
|
||||||
|
onSelect: async () => {
|
||||||
|
const lookup = await apiInstance<Mastodon.Account>({
|
||||||
|
account: a.key,
|
||||||
|
method: 'get',
|
||||||
|
url: 'accounts/lookup',
|
||||||
|
params: {
|
||||||
|
acct:
|
||||||
|
account.acct === account.username
|
||||||
|
? `${account.acct}@${getAccountStorage.string('auth.account.domain')}`
|
||||||
|
: account.acct
|
||||||
|
}
|
||||||
|
}).then(res => res.body)
|
||||||
|
await apiInstance({
|
||||||
|
account: a.key,
|
||||||
|
method: 'post',
|
||||||
|
url: `accounts/${lookup.id}/follow`
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
displayMessage({
|
||||||
|
type: 'success',
|
||||||
|
message: t('componentContextMenu:account.followAs.succeed', {
|
||||||
|
context: account.locked ? 'locked' : 'default',
|
||||||
|
defaultValue: 'default',
|
||||||
|
target: account.acct,
|
||||||
|
source: a.acct
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch(err =>
|
||||||
|
displayMessage({
|
||||||
|
type: 'error',
|
||||||
|
message: t('common:message.error.message', {
|
||||||
|
function: t('componentContextMenu:account.followAs.failed')
|
||||||
|
}),
|
||||||
|
...(err.status &&
|
||||||
|
typeof err.status === 'number' &&
|
||||||
|
err.data &&
|
||||||
|
err.data.error &&
|
||||||
|
typeof err.data.error === 'string' && {
|
||||||
|
description: err.data.error
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
},
|
||||||
|
disabled: false,
|
||||||
|
destructive: false,
|
||||||
|
hidden: a.active
|
||||||
|
},
|
||||||
|
title: a.acct
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
followAs()
|
||||||
|
|
||||||
menus.push([
|
menus.push([
|
||||||
{
|
{
|
||||||
key: 'account-block',
|
type: 'sub',
|
||||||
item: {
|
key: 'account-block-report',
|
||||||
onSelect: () =>
|
trigger: {
|
||||||
Alert.alert(
|
key: 'account-block-report',
|
||||||
t('componentContextMenu:account.block.alert.title', {
|
props: { destructive: true, disabled: false, hidden: false },
|
||||||
username: actualAccount?.username
|
title: t('componentContextMenu:account.blockReport'),
|
||||||
}),
|
icon: 'hand.raised'
|
||||||
undefined,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: t('common:buttons.confirm'),
|
|
||||||
style: 'destructive',
|
|
||||||
onPress: () =>
|
|
||||||
actualAccount &&
|
|
||||||
timelineMutation.mutate({
|
|
||||||
type: 'updateAccountProperty',
|
|
||||||
id: actualAccount.id,
|
|
||||||
payload: { property: 'block', currentValue: data?.blocking }
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t('common:buttons.cancel')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
),
|
|
||||||
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
|
||||||
destructive: !data?.blocking,
|
|
||||||
hidden: false
|
|
||||||
},
|
},
|
||||||
title: t('componentContextMenu:account.block.action', {
|
items: [
|
||||||
defaultValue: 'false',
|
{
|
||||||
context: (data?.blocking || false).toString()
|
key: 'account-block',
|
||||||
}),
|
props: {
|
||||||
icon: data?.blocking ? 'checkmark.circle' : 'xmark.circle'
|
onSelect: () =>
|
||||||
},
|
Alert.alert(
|
||||||
{
|
t('componentContextMenu:account.block.alert.title', {
|
||||||
key: 'account-reports',
|
username: actualAccount?.username
|
||||||
item: {
|
}),
|
||||||
onSelect: () =>
|
undefined,
|
||||||
actualAccount &&
|
[
|
||||||
navigation.navigate('Tab-Shared-Report', {
|
{
|
||||||
account: actualAccount,
|
text: t('common:buttons.confirm'),
|
||||||
status
|
style: 'destructive',
|
||||||
|
onPress: () =>
|
||||||
|
actualAccount &&
|
||||||
|
timelineMutation.mutate({
|
||||||
|
type: 'updateAccountProperty',
|
||||||
|
id: actualAccount.id,
|
||||||
|
payload: { property: 'block', currentValue: data?.blocking }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t('common:buttons.cancel')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
||||||
|
destructive: !data?.blocking,
|
||||||
|
hidden: false
|
||||||
|
},
|
||||||
|
title: t('componentContextMenu:account.block.action', {
|
||||||
|
defaultValue: 'false',
|
||||||
|
context: (data?.blocking || false).toString()
|
||||||
}),
|
}),
|
||||||
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
icon: data?.blocking ? 'checkmark.circle' : 'xmark.circle'
|
||||||
destructive: true,
|
},
|
||||||
hidden: false
|
{
|
||||||
},
|
key: 'account-reports',
|
||||||
title: t('componentContextMenu:account.reports.action'),
|
props: {
|
||||||
icon: 'flag'
|
onSelect: () =>
|
||||||
|
actualAccount &&
|
||||||
|
navigation.navigate('Tab-Shared-Report', {
|
||||||
|
account: actualAccount,
|
||||||
|
status
|
||||||
|
}),
|
||||||
|
disabled: Platform.OS !== 'android' ? !data || !isFetched : false,
|
||||||
|
destructive: true,
|
||||||
|
hidden: false
|
||||||
|
},
|
||||||
|
title: t('componentContextMenu:account.reports.action'),
|
||||||
|
icon: 'flag'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,51 +3,51 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
||||||
import { RootStackParamList, useNavState } from '@utils/navigation/navigators'
|
import { RootStackParamList, useNavState } from '@utils/navigation/navigators'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const menuAt = ({ account }: { account: Mastodon.Account }): ContextMenu[][] => {
|
const menuAt = ({ account }: { account: Mastodon.Account }): ContextMenu => {
|
||||||
const { t } = useTranslation('componentContextMenu')
|
const { t } = useTranslation('componentContextMenu')
|
||||||
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>()
|
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>()
|
||||||
const navigationState = useNavState()
|
const navigationState = useNavState()
|
||||||
|
|
||||||
const menus: ContextMenu[][] = []
|
return [
|
||||||
|
[
|
||||||
menus.push([
|
{
|
||||||
{
|
type: 'item',
|
||||||
key: 'at-direct',
|
key: 'at-direct',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
navigation.navigate('Screen-Compose', {
|
navigation.navigate('Screen-Compose', {
|
||||||
type: 'conversation',
|
type: 'conversation',
|
||||||
accts: [account.acct],
|
accts: [account.acct],
|
||||||
visibility: 'direct',
|
visibility: 'direct',
|
||||||
navigationState
|
navigationState
|
||||||
}),
|
}),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
destructive: false,
|
destructive: false,
|
||||||
hidden: false
|
hidden: false
|
||||||
|
},
|
||||||
|
title: t('at.direct'),
|
||||||
|
icon: 'envelope'
|
||||||
},
|
},
|
||||||
title: t('at.direct'),
|
{
|
||||||
icon: 'envelope'
|
type: 'item',
|
||||||
},
|
key: 'at-public',
|
||||||
{
|
props: {
|
||||||
key: 'at-public',
|
onSelect: () =>
|
||||||
item: {
|
navigation.navigate('Screen-Compose', {
|
||||||
onSelect: () =>
|
type: 'conversation',
|
||||||
navigation.navigate('Screen-Compose', {
|
accts: [account.acct],
|
||||||
type: 'conversation',
|
visibility: 'public',
|
||||||
accts: [account.acct],
|
navigationState
|
||||||
visibility: 'public',
|
}),
|
||||||
navigationState
|
disabled: false,
|
||||||
}),
|
destructive: false,
|
||||||
disabled: false,
|
hidden: false
|
||||||
destructive: false,
|
},
|
||||||
hidden: false
|
title: t('at.public'),
|
||||||
},
|
icon: 'at'
|
||||||
title: t('at.public'),
|
}
|
||||||
icon: 'at'
|
]
|
||||||
}
|
]
|
||||||
])
|
|
||||||
|
|
||||||
return menus
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default menuAt
|
export default menuAt
|
||||||
|
|
|
@ -1,6 +1,53 @@
|
||||||
type ContextMenu = {
|
// type ContextMenu = (
|
||||||
|
// | {
|
||||||
|
// type: 'group'
|
||||||
|
// key: string
|
||||||
|
// items: ContextMenuItem[]
|
||||||
|
// }
|
||||||
|
// | {
|
||||||
|
// type: 'sub'
|
||||||
|
// key: string
|
||||||
|
// trigger: {
|
||||||
|
// key: string
|
||||||
|
// props: {
|
||||||
|
// disabled: boolean
|
||||||
|
// destructive: boolean
|
||||||
|
// hidden: boolean
|
||||||
|
// }
|
||||||
|
// title: string
|
||||||
|
// icon?: string
|
||||||
|
// }
|
||||||
|
// items: ContextMenuItem[]
|
||||||
|
// }
|
||||||
|
// )[]
|
||||||
|
|
||||||
|
type ContextMenu = (ContextMenuItem | ContextMenuSub)[][]
|
||||||
|
|
||||||
|
type ContextMenuItem = {
|
||||||
|
type: 'item'
|
||||||
key: string
|
key: string
|
||||||
item: { onSelect: () => void; disabled: boolean; destructive: boolean; hidden: boolean }
|
props: {
|
||||||
|
onSelect: () => void
|
||||||
|
disabled: boolean
|
||||||
|
destructive: boolean
|
||||||
|
hidden: boolean
|
||||||
|
}
|
||||||
title: string
|
title: string
|
||||||
icon: string
|
icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContextMenuSub = {
|
||||||
|
type: 'sub'
|
||||||
|
key: string
|
||||||
|
trigger: {
|
||||||
|
key: string
|
||||||
|
props: {
|
||||||
|
disabled: boolean
|
||||||
|
destructive: boolean
|
||||||
|
hidden: boolean
|
||||||
|
}
|
||||||
|
title: string
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
items: Omit<ContextMenuItem, 'type'>[]
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ const menuInstance = ({
|
||||||
}: {
|
}: {
|
||||||
status?: Mastodon.Status
|
status?: Mastodon.Status
|
||||||
queryKey?: QueryKeyTimeline
|
queryKey?: QueryKeyTimeline
|
||||||
}): ContextMenu[][] => {
|
}): ContextMenu => {
|
||||||
if (!status || !queryKey) return []
|
if (!status || !queryKey) return []
|
||||||
|
|
||||||
const { t } = useTranslation(['common', 'componentContextMenu'])
|
const { t } = useTranslation(['common', 'componentContextMenu'])
|
||||||
|
@ -30,15 +30,16 @@ const menuInstance = ({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const menus: ContextMenu[][] = []
|
const menus: ContextMenu = []
|
||||||
|
|
||||||
const instance = parse(status.uri).hostname
|
const instance = parse(status.uri).hostname
|
||||||
|
|
||||||
if (instance !== getAccountStorage.string('auth.domain')) {
|
if (instance !== getAccountStorage.string('auth.domain')) {
|
||||||
menus.push([
|
menus.push([
|
||||||
{
|
{
|
||||||
|
type: 'item',
|
||||||
key: 'instance-block',
|
key: 'instance-block',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
t('componentContextMenu:instance.block.alert.title', { instance }),
|
t('componentContextMenu:instance.block.alert.title', { instance }),
|
||||||
|
|
|
@ -15,18 +15,19 @@ const menuShare = (
|
||||||
type: 'account'
|
type: 'account'
|
||||||
url?: string
|
url?: string
|
||||||
}
|
}
|
||||||
): ContextMenu[][] => {
|
): ContextMenu => {
|
||||||
if (params.type === 'status' && params.visibility === 'direct') return []
|
if (params.type === 'status' && params.visibility === 'direct') return []
|
||||||
|
|
||||||
const { t } = useTranslation('componentContextMenu')
|
const { t } = useTranslation('componentContextMenu')
|
||||||
|
|
||||||
const menus: ContextMenu[][] = [[]]
|
const menus: ContextMenu = [[]]
|
||||||
|
|
||||||
if (params.url) {
|
if (params.url) {
|
||||||
const url = params.url
|
const url = params.url
|
||||||
menus[0].push({
|
menus[0].push({
|
||||||
|
type: 'item',
|
||||||
key: 'share',
|
key: 'share',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
switch (Platform.OS) {
|
switch (Platform.OS) {
|
||||||
case 'ios':
|
case 'ios':
|
||||||
|
@ -47,8 +48,9 @@ const menuShare = (
|
||||||
}
|
}
|
||||||
if (params.type === 'status')
|
if (params.type === 'status')
|
||||||
menus[0].push({
|
menus[0].push({
|
||||||
|
type: 'item',
|
||||||
key: 'copy',
|
key: 'copy',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
Clipboard.setString(params.rawContent?.current.join(`\n\n`) || '')
|
Clipboard.setString(params.rawContent?.current.join(`\n\n`) || '')
|
||||||
displayMessage({ type: 'success', message: t(`copy.succeed`) })
|
displayMessage({ type: 'success', message: t(`copy.succeed`) })
|
||||||
|
|
|
@ -21,7 +21,7 @@ const menuStatus = ({
|
||||||
}: {
|
}: {
|
||||||
status?: Mastodon.Status
|
status?: Mastodon.Status
|
||||||
queryKey?: QueryKeyTimeline
|
queryKey?: QueryKeyTimeline
|
||||||
}): ContextMenu[][] => {
|
}): ContextMenu => {
|
||||||
if (!status || !queryKey) return []
|
if (!status || !queryKey) return []
|
||||||
|
|
||||||
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Screen-Tabs'>>()
|
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Screen-Tabs'>>()
|
||||||
|
@ -55,7 +55,7 @@ const menuStatus = ({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const menus: ContextMenu[][] = []
|
const menus: ContextMenu = []
|
||||||
|
|
||||||
const [accountId] = useAccountStorage.string('auth.account.id')
|
const [accountId] = useAccountStorage.string('auth.account.id')
|
||||||
const ownAccount = accountId === status.account?.id
|
const ownAccount = accountId === status.account?.id
|
||||||
|
@ -64,8 +64,9 @@ const menuStatus = ({
|
||||||
|
|
||||||
menus.push([
|
menus.push([
|
||||||
{
|
{
|
||||||
|
type: 'item',
|
||||||
key: 'status-edit',
|
key: 'status-edit',
|
||||||
item: {
|
props: {
|
||||||
onSelect: async () => {
|
onSelect: async () => {
|
||||||
let replyToStatus: Mastodon.Status | undefined = undefined
|
let replyToStatus: Mastodon.Status | undefined = undefined
|
||||||
if (status.in_reply_to_id) {
|
if (status.in_reply_to_id) {
|
||||||
|
@ -102,8 +103,9 @@ const menuStatus = ({
|
||||||
icon: 'square.and.pencil'
|
icon: 'square.and.pencil'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
type: 'item',
|
||||||
key: 'status-delete-edit',
|
key: 'status-delete-edit',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
t('componentContextMenu:status.deleteEdit.alert.title'),
|
t('componentContextMenu:status.deleteEdit.alert.title'),
|
||||||
|
@ -145,8 +147,9 @@ const menuStatus = ({
|
||||||
icon: 'pencil.and.outline'
|
icon: 'pencil.and.outline'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
type: 'item',
|
||||||
key: 'status-delete',
|
key: 'status-delete',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
t('componentContextMenu:status.delete.alert.title'),
|
t('componentContextMenu:status.delete.alert.title'),
|
||||||
|
@ -176,8 +179,9 @@ const menuStatus = ({
|
||||||
|
|
||||||
menus.push([
|
menus.push([
|
||||||
{
|
{
|
||||||
|
type: 'item',
|
||||||
key: 'status-mute',
|
key: 'status-mute',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
type: 'updateStatusProperty',
|
type: 'updateStatusProperty',
|
||||||
|
@ -198,8 +202,9 @@ const menuStatus = ({
|
||||||
icon: status.muted ? 'speaker' : 'speaker.slash'
|
icon: status.muted ? 'speaker' : 'speaker.slash'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
type: 'item',
|
||||||
key: 'status-pin',
|
key: 'status-pin',
|
||||||
item: {
|
props: {
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
// Also note that reblogs cannot be pinned.
|
// Also note that reblogs cannot be pinned.
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Silencia l'usuari",
|
"action_false": "Silencia l'usuari",
|
||||||
"action_true": "Deixa de silenciar l'usuari"
|
"action_true": "Deixa de silenciar l'usuari"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Bloqueja l'usuari",
|
"action_false": "Bloqueja l'usuari",
|
||||||
"action_true": "Deixa de bloquejar l'usuari",
|
"action_true": "Deixa de bloquejar l'usuari",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "",
|
"action_false": "",
|
||||||
"action_true": ""
|
"action_true": ""
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "",
|
"action_false": "",
|
||||||
"action_true": "",
|
"action_true": "",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Profil stummschalten",
|
"action_false": "Profil stummschalten",
|
||||||
"action_true": "Stummschaltung des Nutzers aufheben"
|
"action_true": "Stummschaltung des Nutzers aufheben"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Nutzer blockieren",
|
"action_false": "Nutzer blockieren",
|
||||||
"action_true": "User entblocken",
|
"action_true": "User entblocken",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Σίγαση χρήστη",
|
"action_false": "Σίγαση χρήστη",
|
||||||
"action_true": "Κατάργηση σίγασης χρήστη"
|
"action_true": "Κατάργηση σίγασης χρήστη"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Αποκλεισμός χρήστη",
|
"action_false": "Αποκλεισμός χρήστη",
|
||||||
"action_true": "Κατάργηση αποκλεισμού χρήστη",
|
"action_true": "Κατάργηση αποκλεισμού χρήστη",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Mute user",
|
"action_false": "Mute user",
|
||||||
"action_true": "Unmute user"
|
"action_true": "Unmute user"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "Follow as...",
|
||||||
|
"succeed_default": "Now following @{{target}} with @{{source}}",
|
||||||
|
"succeed_locked": "Sent follow request to @{{target}} with {{source}}, pending approval",
|
||||||
|
"failed": "Follow as"
|
||||||
|
},
|
||||||
|
"blockReport": "Block and report...",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Block user",
|
"action_false": "Block user",
|
||||||
"action_true": "Unblock user",
|
"action_true": "Unblock user",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Silenciar usuario",
|
"action_false": "Silenciar usuario",
|
||||||
"action_true": "Dejar de silenciar al usuario"
|
"action_true": "Dejar de silenciar al usuario"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Bloquear usuario",
|
"action_false": "Bloquear usuario",
|
||||||
"action_true": "Desbloquear usuario",
|
"action_true": "Desbloquear usuario",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Rendre muet l'utilisateur",
|
"action_false": "Rendre muet l'utilisateur",
|
||||||
"action_true": "Rendre la parole"
|
"action_true": "Rendre la parole"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Bloquer l'utilisateur",
|
"action_false": "Bloquer l'utilisateur",
|
||||||
"action_true": "Débloquer l'utilisateur",
|
"action_true": "Débloquer l'utilisateur",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Muta utente",
|
"action_false": "Muta utente",
|
||||||
"action_true": "Riattiva utente"
|
"action_true": "Riattiva utente"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Blocca utente",
|
"action_false": "Blocca utente",
|
||||||
"action_true": "Sblocca utente",
|
"action_true": "Sblocca utente",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "ユーザーをミュート",
|
"action_false": "ユーザーをミュート",
|
||||||
"action_true": "ユーザーのミュートを解除"
|
"action_true": "ユーザーのミュートを解除"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "ユーザーをブロック",
|
"action_false": "ユーザーをブロック",
|
||||||
"action_true": "ユーザーのブロックを解除",
|
"action_true": "ユーザーのブロックを解除",
|
||||||
|
|
|
@ -6,26 +6,33 @@
|
||||||
"action_false": "사용자 팔로우",
|
"action_false": "사용자 팔로우",
|
||||||
"action_true": "사용자 팔로우 해제"
|
"action_true": "사용자 팔로우 해제"
|
||||||
},
|
},
|
||||||
"inLists": "",
|
"inLists": "사용자를 포함한 리스트",
|
||||||
"showBoosts": {
|
"showBoosts": {
|
||||||
"action_false": "",
|
"action_false": "사용자의 부스트 보이기",
|
||||||
"action_true": ""
|
"action_true": "사용자의 부스트 숨기기"
|
||||||
},
|
},
|
||||||
"mute": {
|
"mute": {
|
||||||
"action_false": "사용자 뮤트",
|
"action_false": "사용자 뮤트",
|
||||||
"action_true": "사용자 뮤트 해제"
|
"action_true": "사용자 뮤트 해제"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "사용자 차단",
|
"action_false": "사용자 차단",
|
||||||
"action_true": "사용자 차단 해제",
|
"action_true": "사용자 차단 해제",
|
||||||
"alert": {
|
"alert": {
|
||||||
"title": ""
|
"title": "정말 @{{username}} 사용자를 차단할까요?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reports": {
|
"reports": {
|
||||||
"action": "사용자 신고 및 차단",
|
"action": "사용자 신고 및 차단",
|
||||||
"alert": {
|
"alert": {
|
||||||
"title": ""
|
"title": "정말 @{{username}} 사용자를 차단하고 신고할까요?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
"message": "마지막 읽음"
|
"message": "마지막 읽음"
|
||||||
},
|
},
|
||||||
"refresh": {
|
"refresh": {
|
||||||
"fetchPreviousPage": "여기부터 더 새로운",
|
"fetchPreviousPage": "이 시점에 이어서 불러오기",
|
||||||
"refetch": "가장 최신으로"
|
"refetch": "최신 내용 불러오기"
|
||||||
},
|
},
|
||||||
"shared": {
|
"shared": {
|
||||||
"actioned": {
|
"actioned": {
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
"text": "불러오기 오류",
|
"text": "불러오기 오류",
|
||||||
"button": "원격 링크 시도"
|
"button": "원격 링크 시도"
|
||||||
},
|
},
|
||||||
"altText": ""
|
"altText": "대체 텍스트"
|
||||||
},
|
},
|
||||||
"avatar": {
|
"avatar": {
|
||||||
"accessibilityLabel": "{{name}}의 아바타",
|
"accessibilityLabel": "{{name}}의 아바타",
|
||||||
|
@ -116,14 +116,14 @@
|
||||||
"accessibilityHint": "사용자 계정"
|
"accessibilityHint": "사용자 계정"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"application": "",
|
"application": "{{application}}으로 툿",
|
||||||
"edited": {
|
"edited": {
|
||||||
"accessibilityLabel": "툿 수정됨"
|
"accessibilityLabel": "툿 수정됨"
|
||||||
},
|
},
|
||||||
"muted": {
|
"muted": {
|
||||||
"accessibilityLabel": "툿 음소거됨"
|
"accessibilityLabel": "툿 음소거됨"
|
||||||
},
|
},
|
||||||
"replies": "",
|
"replies": "답장 <0 />",
|
||||||
"visibility": {
|
"visibility": {
|
||||||
"direct": {
|
"direct": {
|
||||||
"accessibilityLabel": "툿이 개인 메시지에요"
|
"accessibilityLabel": "툿이 개인 메시지에요"
|
||||||
|
|
|
@ -140,7 +140,7 @@
|
||||||
},
|
},
|
||||||
"editAttachment": {
|
"editAttachment": {
|
||||||
"header": {
|
"header": {
|
||||||
"title": "첨부파일 편집",
|
"title": "첨부 파일 편집",
|
||||||
"right": {
|
"right": {
|
||||||
"accessibilityLabel": "첨부 파일 편집 저장",
|
"accessibilityLabel": "첨부 파일 편집 저장",
|
||||||
"failed": {
|
"failed": {
|
||||||
|
@ -152,7 +152,7 @@
|
||||||
"content": {
|
"content": {
|
||||||
"altText": {
|
"altText": {
|
||||||
"heading": "시각장애인을 위한 미디어 설명",
|
"heading": "시각장애인을 위한 미디어 설명",
|
||||||
"placeholder": "미디어에 alt-text라고도 하는 설명을 추가하여 시각 장애가 있는 사람들을 포함하여 더 많은 사람들이 액세스할 수 있도록 할 수 있어요. \n\n좋은 설명은 간결하지만 미디어에 있는 내용을 정확하게 제시하여 컨텍스트를 파악할 수 있는 것이에요."
|
"placeholder": "미디어에 '대체 텍스트'라고도 하는 설명을 추가하여, 시각 장애가 있는 사람들을 포함해 더 많은 사람들이 접근하도록 할 수 있어요.\n\n좋은 설명은 간결하지만, 미디어의 내용을 정확하게 표현하여 문맥을 파악할 수 있는 것이에요."
|
||||||
},
|
},
|
||||||
"imageFocus": "포커스 원을 드래그하여 포커스 포인트를 업데이트할 수 있어요"
|
"imageFocus": "포커스 원을 드래그하여 포커스 포인트를 업데이트할 수 있어요"
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,7 @@
|
||||||
"heading": "다크 테마",
|
"heading": "다크 테마",
|
||||||
"options": {
|
"options": {
|
||||||
"lighter": "기본값",
|
"lighter": "기본값",
|
||||||
"darker": ""
|
"darker": "완전히 검게"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
|
@ -277,7 +277,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoplayGifv": {
|
"autoplayGifv": {
|
||||||
"heading": ""
|
"heading": "타임라인의 GIF 파일 자동 재생"
|
||||||
},
|
},
|
||||||
"feedback": {
|
"feedback": {
|
||||||
"heading": "기능 제안"
|
"heading": "기능 제안"
|
||||||
|
@ -330,22 +330,22 @@
|
||||||
"name": "수정 이력"
|
"name": "수정 이력"
|
||||||
},
|
},
|
||||||
"report": {
|
"report": {
|
||||||
"name": "",
|
"name": "@{{acct}} 신고",
|
||||||
"report": "",
|
"report": "신고",
|
||||||
"forward": {
|
"forward": {
|
||||||
"heading": ""
|
"heading": "원격 인스턴스 {{instance}}에도 익명으로 전달"
|
||||||
},
|
},
|
||||||
"reasons": {
|
"reasons": {
|
||||||
"heading": "",
|
"heading": "이 계정에 어떤 문제가 있나요?",
|
||||||
"spam": "",
|
"spam": "스팸입니다",
|
||||||
"other": "",
|
"other": "다른 문제가 있습니다",
|
||||||
"violation": ""
|
"violation": "서버 규칙을 위반합니다"
|
||||||
},
|
},
|
||||||
"comment": {
|
"comment": {
|
||||||
"heading": ""
|
"heading": "그 밖에 추가로 작성할 내용이 있나요?"
|
||||||
},
|
},
|
||||||
"violatedRules": {
|
"violatedRules": {
|
||||||
"heading": ""
|
"heading": "서버 규칙 위반 내용"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
@ -378,7 +378,7 @@
|
||||||
"toot": {
|
"toot": {
|
||||||
"name": "대화",
|
"name": "대화",
|
||||||
"remoteFetch": {
|
"remoteFetch": {
|
||||||
"title": "",
|
"title": "원격 컨텐츠를 포함",
|
||||||
"message": ""
|
"message": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Gebruiker dempen",
|
"action_false": "Gebruiker dempen",
|
||||||
"action_true": "Dempen opheffen voor gebruiker"
|
"action_true": "Dempen opheffen voor gebruiker"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Gebruiker blokkeren",
|
"action_false": "Gebruiker blokkeren",
|
||||||
"action_true": "Gebruiker deblokkeren",
|
"action_true": "Gebruiker deblokkeren",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Wycisz użytkownika",
|
"action_false": "Wycisz użytkownika",
|
||||||
"action_true": "Wyłącz wyciszenie"
|
"action_true": "Wyłącz wyciszenie"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Zablokuj użytkownika",
|
"action_false": "Zablokuj użytkownika",
|
||||||
"action_true": "Odblokuj użytkownika",
|
"action_true": "Odblokuj użytkownika",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Silenciar usuário",
|
"action_false": "Silenciar usuário",
|
||||||
"action_true": "Desativar o silêncio do usuário"
|
"action_true": "Desativar o silêncio do usuário"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Bloquear usuário",
|
"action_false": "Bloquear usuário",
|
||||||
"action_true": "Desbloquear usuário",
|
"action_true": "Desbloquear usuário",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "",
|
"action_false": "",
|
||||||
"action_true": ""
|
"action_true": ""
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "",
|
"action_false": "",
|
||||||
"action_true": "",
|
"action_true": "",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Tysta användare",
|
"action_false": "Tysta användare",
|
||||||
"action_true": "Sluta tysta användare"
|
"action_true": "Sluta tysta användare"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Blockera användare",
|
"action_false": "Blockera användare",
|
||||||
"action_true": "Avblockera användare",
|
"action_true": "Avblockera användare",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Заглушити користувача",
|
"action_false": "Заглушити користувача",
|
||||||
"action_true": "Зняти заглушення з користувача"
|
"action_true": "Зняти заглушення з користувача"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Заблокувати користувача",
|
"action_false": "Заблокувати користувача",
|
||||||
"action_true": "Розблокувати користувача",
|
"action_true": "Розблокувати користувача",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "Ẩn người này",
|
"action_false": "Ẩn người này",
|
||||||
"action_true": "Bỏ ẩn người dùng"
|
"action_true": "Bỏ ẩn người dùng"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "Chặn người này",
|
"action_false": "Chặn người này",
|
||||||
"action_true": "Bỏ chặn người dùng",
|
"action_true": "Bỏ chặn người dùng",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "静音用户",
|
"action_false": "静音用户",
|
||||||
"action_true": "取消静音用户"
|
"action_true": "取消静音用户"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "关注…",
|
||||||
|
"succeed_default": "{{source}} 正在关注 {{target}}",
|
||||||
|
"succeed_locked": "已从 {{source}} 发送关注请求至 {{target}},等待通过",
|
||||||
|
"failed": "用其它账户关注"
|
||||||
|
},
|
||||||
|
"blockReport": "屏蔽与举报…",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "屏蔽用户",
|
"action_false": "屏蔽用户",
|
||||||
"action_true": "取消屏蔽用户",
|
"action_true": "取消屏蔽用户",
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
"action_false": "靜音使用者",
|
"action_false": "靜音使用者",
|
||||||
"action_true": "解除靜音使用者"
|
"action_true": "解除靜音使用者"
|
||||||
},
|
},
|
||||||
|
"followAs": {
|
||||||
|
"trigger": "",
|
||||||
|
"succeed_default": "",
|
||||||
|
"succeed_locked": "",
|
||||||
|
"failed": ""
|
||||||
|
},
|
||||||
|
"blockReport": "",
|
||||||
"block": {
|
"block": {
|
||||||
"action_false": "封鎖使用者",
|
"action_false": "封鎖使用者",
|
||||||
"action_true": "解除封鎖使用者",
|
"action_true": "解除封鎖使用者",
|
||||||
|
|
|
@ -2,7 +2,7 @@ import AccountButton from '@components/AccountButton'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import navigationRef from '@utils/navigation/navigationRef'
|
import navigationRef from '@utils/navigation/navigationRef'
|
||||||
import { RootStackScreenProps } from '@utils/navigation/navigators'
|
import { RootStackScreenProps } from '@utils/navigation/navigators'
|
||||||
import { getGlobalStorage } from '@utils/storage/actions'
|
import { getReadableAccounts } from '@utils/storage/actions'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import * as VideoThumbnails from 'expo-video-thumbnails'
|
import * as VideoThumbnails from 'expo-video-thumbnails'
|
||||||
|
@ -92,7 +92,7 @@ const ScreenAccountSelection = ({
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const { t } = useTranslation('screenAccountSelection')
|
const { t } = useTranslation('screenAccountSelection')
|
||||||
|
|
||||||
const accounts = getGlobalStorage.object('accounts')
|
const accounts = getReadableAccounts()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
@ -125,24 +125,20 @@ const ScreenAccountSelection = ({
|
||||||
marginTop: StyleConstants.Spacing.M
|
marginTop: StyleConstants.Spacing.M
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{accounts &&
|
{accounts.map((account, index) => {
|
||||||
accounts
|
return (
|
||||||
.slice()
|
<AccountButton
|
||||||
.sort((a, b) => a.localeCompare(b))
|
key={index}
|
||||||
.map((account, index) => {
|
account={account}
|
||||||
return (
|
additionalActions={() =>
|
||||||
<AccountButton
|
navigationRef.navigate('Screen-Compose', {
|
||||||
key={index}
|
type: 'share',
|
||||||
account={account}
|
...share
|
||||||
additionalActions={() =>
|
})
|
||||||
navigationRef.navigate('Screen-Compose', {
|
}
|
||||||
type: 'share',
|
/>
|
||||||
...share
|
)
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import AccountButton from '@components/AccountButton'
|
import AccountButton from '@components/AccountButton'
|
||||||
import ComponentInstance from '@components/Instance'
|
import ComponentInstance from '@components/Instance'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import { getGlobalStorage } from '@utils/storage/actions'
|
import { getReadableAccounts } from '@utils/storage/actions'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useEffect, useRef } from 'react'
|
import React, { useEffect, useRef } from 'react'
|
||||||
|
@ -12,8 +12,7 @@ import { ScrollView } from 'react-native-gesture-handler'
|
||||||
const TabMeSwitch: React.FC = () => {
|
const TabMeSwitch: React.FC = () => {
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
const accounts = getGlobalStorage.object('accounts')
|
const accounts = getReadableAccounts()
|
||||||
const accountActive = getGlobalStorage.string('account.active')
|
|
||||||
|
|
||||||
const scrollViewRef = useRef<ScrollView>(null)
|
const scrollViewRef = useRef<ScrollView>(null)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -71,19 +70,9 @@ const TabMeSwitch: React.FC = () => {
|
||||||
marginTop: StyleConstants.Spacing.M
|
marginTop: StyleConstants.Spacing.M
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{accounts &&
|
{accounts.map((account, index) => {
|
||||||
accounts
|
return <AccountButton key={index} account={account} />
|
||||||
.slice()
|
})}
|
||||||
.sort((a, b) => a.localeCompare(b))
|
|
||||||
.map((account, index) => {
|
|
||||||
return (
|
|
||||||
<AccountButton
|
|
||||||
key={index}
|
|
||||||
account={account}
|
|
||||||
selected={account === accountActive}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { createContext } from 'react'
|
||||||
|
|
||||||
type AccountContextType = {
|
type AccountContextType = {
|
||||||
account?: Mastodon.Account
|
account?: Mastodon.Account
|
||||||
|
relationship?: Mastodon.Relationship
|
||||||
pageMe?: boolean
|
pageMe?: boolean
|
||||||
}
|
}
|
||||||
const AccountContext = createContext<AccountContextType>({} as AccountContextType)
|
const AccountContext = createContext<AccountContextType>({} as AccountContextType)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { useRoute } from '@react-navigation/native'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
@ -11,16 +10,19 @@ import AccountInformationCreated from './Information/Created'
|
||||||
import AccountInformationFields from './Information/Fields'
|
import AccountInformationFields from './Information/Fields'
|
||||||
import AccountInformationName from './Information/Name'
|
import AccountInformationName from './Information/Name'
|
||||||
import AccountInformationNote from './Information/Note'
|
import AccountInformationNote from './Information/Note'
|
||||||
|
import AccountInformationPrivateNote from './Information/PrivateNotes'
|
||||||
import AccountInformationStats from './Information/Stats'
|
import AccountInformationStats from './Information/Stats'
|
||||||
|
|
||||||
const AccountInformation: React.FC = () => {
|
const AccountInformation: React.FC = () => {
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
|
||||||
const { name } = useRoute()
|
|
||||||
const myInfo = name !== 'Tab-Shared-Account'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View
|
||||||
|
style={{
|
||||||
|
marginTop: -StyleConstants.Avatar.L / 2,
|
||||||
|
padding: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Placeholder
|
<Placeholder
|
||||||
Animation={props => (
|
Animation={props => (
|
||||||
<Fade {...props} style={{ backgroundColor: colors.shimmerHighlight }} />
|
<Fade {...props} style={{ backgroundColor: colors.shimmerHighlight }} />
|
||||||
|
@ -35,6 +37,8 @@ const AccountInformation: React.FC = () => {
|
||||||
|
|
||||||
<AccountInformationAccount />
|
<AccountInformationAccount />
|
||||||
|
|
||||||
|
<AccountInformationPrivateNote />
|
||||||
|
|
||||||
<AccountInformationFields />
|
<AccountInformationFields />
|
||||||
|
|
||||||
<AccountInformationNote />
|
<AccountInformationNote />
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
|
||||||
import { getAccountStorage, useAccountStorage } from '@utils/storage/actions'
|
import { getAccountStorage, useAccountStorage } from '@utils/storage/actions'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
@ -11,7 +10,7 @@ import { PlaceholderLine } from 'rn-placeholder'
|
||||||
import AccountContext from '../Context'
|
import AccountContext from '../Context'
|
||||||
|
|
||||||
const AccountInformationAccount: React.FC = () => {
|
const AccountInformationAccount: React.FC = () => {
|
||||||
const { account, pageMe } = useContext(AccountContext)
|
const { account, relationship, pageMe } = useContext(AccountContext)
|
||||||
|
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
const { colors } = useTheme()
|
const { colors } = useTheme()
|
||||||
|
@ -19,8 +18,6 @@ const AccountInformationAccount: React.FC = () => {
|
||||||
const [acct] = useAccountStorage.string('auth.account.acct')
|
const [acct] = useAccountStorage.string('auth.account.acct')
|
||||||
const domain = getAccountStorage.string('auth.account.domain')
|
const domain = getAccountStorage.string('auth.account.domain')
|
||||||
|
|
||||||
const { data: relationship } = useRelationshipQuery({ id: account?.id })
|
|
||||||
|
|
||||||
const localInstance = account?.acct.includes('@') ? account?.acct.includes(`@${domain}`) : true
|
const localInstance = account?.acct.includes('@') ? account?.acct.includes(`@${domain}`) : true
|
||||||
|
|
||||||
if (account || pageMe) {
|
if (account || pageMe) {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import Button from '@components/Button'
|
||||||
import menuAt from '@components/contextMenu/at'
|
import menuAt from '@components/contextMenu/at'
|
||||||
import { RelationshipOutgoing } from '@components/Relationship'
|
import { RelationshipOutgoing } from '@components/Relationship'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
|
||||||
import { useAccountStorage } from '@utils/storage/actions'
|
import { useAccountStorage } from '@utils/storage/actions'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
|
@ -12,7 +11,7 @@ import * as DropdownMenu from 'zeego/dropdown-menu'
|
||||||
import AccountContext from '../Context'
|
import AccountContext from '../Context'
|
||||||
|
|
||||||
const AccountInformationActions: React.FC = () => {
|
const AccountInformationActions: React.FC = () => {
|
||||||
const { account, pageMe } = useContext(AccountContext)
|
const { account, relationship, pageMe } = useContext(AccountContext)
|
||||||
|
|
||||||
if (!account || account.suspended) {
|
if (!account || account.suspended) {
|
||||||
return null
|
return null
|
||||||
|
@ -50,13 +49,12 @@ const AccountInformationActions: React.FC = () => {
|
||||||
const [accountId] = useAccountStorage.string('auth.account.id')
|
const [accountId] = useAccountStorage.string('auth.account.id')
|
||||||
const ownAccount = account?.id === accountId
|
const ownAccount = account?.id === accountId
|
||||||
|
|
||||||
const query = useRelationshipQuery({ id: account.id })
|
|
||||||
const mAt = menuAt({ account })
|
const mAt = menuAt({ account })
|
||||||
|
|
||||||
if (!ownAccount && account) {
|
if (!ownAccount && account) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={styles.base}>
|
||||||
{query.data && !query.data.blocked_by ? (
|
{relationship && !relationship.blocked_by ? (
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger>
|
<DropdownMenu.Trigger>
|
||||||
<Button
|
<Button
|
||||||
|
@ -69,14 +67,41 @@ const AccountInformationActions: React.FC = () => {
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
{mAt.map((mGroup, index) => (
|
{mAt.map((group, index) => (
|
||||||
<DropdownMenu.Group key={index}>
|
<DropdownMenu.Group key={index}>
|
||||||
{mGroup.map(menu => (
|
{group.map(item => {
|
||||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
switch (item.type) {
|
||||||
<DropdownMenu.ItemTitle children={menu.title} />
|
case 'item':
|
||||||
<DropdownMenu.ItemIcon ios={{ name: menu.icon }} />
|
return (
|
||||||
</DropdownMenu.Item>
|
<DropdownMenu.Item key={item.key} {...item.props}>
|
||||||
))}
|
<DropdownMenu.ItemTitle children={item.title} />
|
||||||
|
{item.icon ? <DropdownMenu.ItemIcon ios={{ name: item.icon }} /> : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)
|
||||||
|
case 'sub':
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<DropdownMenu.Sub key={item.key}>
|
||||||
|
<DropdownMenu.SubTrigger key={item.trigger.key} {...item.trigger.props}>
|
||||||
|
<DropdownMenu.ItemTitle children={item.trigger.title} />
|
||||||
|
{item.trigger.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: item.trigger.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent>
|
||||||
|
{item.items.map(sub => (
|
||||||
|
<DropdownMenu.Item key={sub.key} {...sub.props}>
|
||||||
|
<DropdownMenu.ItemTitle children={sub.title} />
|
||||||
|
{sub.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: sub.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
))}
|
))}
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { ParseHTML } from '@components/Parse'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import React, { useContext } from 'react'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
import AccountContext from '../Context'
|
||||||
|
|
||||||
|
const AccountInformationPrivateNote: React.FC = () => {
|
||||||
|
const { relationship, pageMe } = useContext(AccountContext)
|
||||||
|
if (pageMe) return null
|
||||||
|
|
||||||
|
const { colors } = useTheme()
|
||||||
|
|
||||||
|
return relationship?.note ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
marginBottom: StyleConstants.Spacing.L,
|
||||||
|
borderLeftColor: colors.border,
|
||||||
|
borderLeftWidth: StyleConstants.Spacing.XS,
|
||||||
|
paddingLeft: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ParseHTML content={relationship.note} size={'S'} selectable numberOfLines={2} />
|
||||||
|
</View>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AccountInformationPrivateNote
|
|
@ -7,6 +7,7 @@ import SegmentedControl from '@react-native-community/segmented-control'
|
||||||
import { useQueryClient } from '@tanstack/react-query'
|
import { useQueryClient } from '@tanstack/react-query'
|
||||||
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||||
|
import { useRelationshipQuery } from '@utils/queryHooks/relationship'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
@ -51,6 +52,10 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
onError: () => navigation.goBack()
|
onError: () => navigation.goBack()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const { data: dataRelationship } = useRelationshipQuery({
|
||||||
|
id: account._remote ? data?.id : account.id,
|
||||||
|
options: { enabled: account._remote ? !!data?.id : true }
|
||||||
|
})
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>([
|
const [queryKey, setQueryKey] = useState<QueryKeyTimeline>([
|
||||||
|
@ -89,16 +94,48 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
{[mShare, mAccount].map((type, i) => (
|
{[mShare, mAccount].map((menu, i) => (
|
||||||
<Fragment key={i}>
|
<Fragment key={i}>
|
||||||
{type.map((mGroup, index) => (
|
{menu.map((group, index) => (
|
||||||
<DropdownMenu.Group key={index}>
|
<DropdownMenu.Group key={index}>
|
||||||
{mGroup.map(menu => (
|
{group.map(item => {
|
||||||
<DropdownMenu.Item key={menu.key} {...menu.item}>
|
switch (item.type) {
|
||||||
<DropdownMenu.ItemTitle children={menu.title} />
|
case 'item':
|
||||||
<DropdownMenu.ItemIcon ios={{ name: menu.icon }} />
|
return (
|
||||||
</DropdownMenu.Item>
|
<DropdownMenu.Item key={item.key} {...item.props}>
|
||||||
))}
|
<DropdownMenu.ItemTitle children={item.title} />
|
||||||
|
{item.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: item.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)
|
||||||
|
case 'sub':
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<DropdownMenu.Sub key={item.key}>
|
||||||
|
<DropdownMenu.SubTrigger
|
||||||
|
key={item.trigger.key}
|
||||||
|
{...item.trigger.props}
|
||||||
|
>
|
||||||
|
<DropdownMenu.ItemTitle children={item.trigger.title} />
|
||||||
|
{item.trigger.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: item.trigger.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent>
|
||||||
|
{item.items.map(sub => (
|
||||||
|
<DropdownMenu.Item key={sub.key} {...sub.props}>
|
||||||
|
<DropdownMenu.ItemTitle children={sub.title} />
|
||||||
|
{sub.icon ? (
|
||||||
|
<DropdownMenu.ItemIcon ios={{ name: sub.icon }} />
|
||||||
|
) : null}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@ -191,7 +228,7 @@ const TabSharedAccount: React.FC<TabSharedStackScreenProps<'Tab-Shared-Account'>
|
||||||
}, [segment, dataUpdatedAt, mode])
|
}, [segment, dataUpdatedAt, mode])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccountContext.Provider value={{ account: data }}>
|
<AccountContext.Provider value={{ account: data, relationship: dataRelationship }}>
|
||||||
<AccountNav scrollY={scrollY} />
|
<AccountNav scrollY={scrollY} />
|
||||||
|
|
||||||
{data?.suspended ? (
|
{data?.suspended ? (
|
||||||
|
|
|
@ -21,14 +21,14 @@ const apiGeneral = async <T = unknown>({
|
||||||
body
|
body
|
||||||
}: Params): Promise<PagedResponse<T>> => {
|
}: Params): Promise<PagedResponse<T>> => {
|
||||||
console.log(
|
console.log(
|
||||||
ctx.bgGreen.bold(' API general ') +
|
ctx.bgMagenta.bold(' General ') +
|
||||||
' ' +
|
' ' +
|
||||||
domain +
|
domain +
|
||||||
' ' +
|
' ' +
|
||||||
method +
|
method +
|
||||||
ctx.green(' -> ') +
|
ctx.magenta(' -> ') +
|
||||||
`/${url}` +
|
`/${url}` +
|
||||||
(params ? ctx.green(' -> ') : ''),
|
(params ? ctx.magenta(' -> ') : ''),
|
||||||
params ? params : ''
|
params ? params : ''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,9 @@ const handleError =
|
||||||
|
|
||||||
if (error?.response) {
|
if (error?.response) {
|
||||||
if (config?.captureResponse) {
|
if (config?.captureResponse) {
|
||||||
|
Sentry.setTag('error_status', error.response.status)
|
||||||
Sentry.setContext('Error response', {
|
Sentry.setContext('Error response', {
|
||||||
data: error.response.data,
|
data: error.response.data,
|
||||||
status: error.response.status,
|
|
||||||
headers: error.response.headers
|
headers: error.response.headers
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { getAccountDetails } from '@utils/storage/actions'
|
import { getAccountDetails } from '@utils/storage/actions'
|
||||||
|
import { StorageGlobal } from '@utils/storage/global'
|
||||||
import axios, { AxiosRequestConfig } from 'axios'
|
import axios, { AxiosRequestConfig } from 'axios'
|
||||||
import { ctx, handleError, PagedResponse, parseHeaderLinks, userAgent } from './helpers'
|
import { ctx, handleError, PagedResponse, parseHeaderLinks, userAgent } from './helpers'
|
||||||
|
|
||||||
export type Params = {
|
export type Params = {
|
||||||
|
account?: StorageGlobal['account.active']
|
||||||
method: 'get' | 'post' | 'put' | 'delete' | 'patch'
|
method: 'get' | 'post' | 'put' | 'delete' | 'patch'
|
||||||
version?: 'v1' | 'v2'
|
version?: 'v1' | 'v2'
|
||||||
url: string
|
url: string
|
||||||
|
@ -15,6 +17,7 @@ export type Params = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiInstance = async <T = unknown>({
|
const apiInstance = async <T = unknown>({
|
||||||
|
account,
|
||||||
method,
|
method,
|
||||||
version = 'v1',
|
version = 'v1',
|
||||||
url,
|
url,
|
||||||
|
@ -23,7 +26,7 @@ const apiInstance = async <T = unknown>({
|
||||||
body,
|
body,
|
||||||
extras
|
extras
|
||||||
}: Params): Promise<PagedResponse<T>> => {
|
}: Params): Promise<PagedResponse<T>> => {
|
||||||
const accountDetails = getAccountDetails(['auth.domain', 'auth.token'])
|
const accountDetails = getAccountDetails(['auth.domain', 'auth.token'], account)
|
||||||
if (!accountDetails) {
|
if (!accountDetails) {
|
||||||
console.warn(ctx.bgRed.white.bold(' API instance '), 'No account detail available')
|
console.warn(ctx.bgRed.white.bold(' API instance '), 'No account detail available')
|
||||||
return Promise.reject()
|
return Promise.reject()
|
||||||
|
@ -35,9 +38,9 @@ const apiInstance = async <T = unknown>({
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
ctx.bgGreen.bold(' API instance '),
|
ctx.bgBlue.bold(' Instance '),
|
||||||
accountDetails['auth.domain'],
|
accountDetails['auth.domain'],
|
||||||
method + ctx.green(' -> ') + `/${url}` + (params ? ctx.green(' -> ') : ''),
|
method + ctx.blue(' -> ') + `/${url}` + (params ? ctx.blue(' -> ') : ''),
|
||||||
params ? params : ''
|
params ? params : ''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ const apiTooot = async <T = unknown>({
|
||||||
body
|
body
|
||||||
}: Params): Promise<{ body: T }> => {
|
}: Params): Promise<{ body: T }> => {
|
||||||
console.log(
|
console.log(
|
||||||
ctx.bgGreen.bold(' API tooot ') +
|
ctx.bgGreen.bold(' tooot ') +
|
||||||
' ' +
|
' ' +
|
||||||
method +
|
method +
|
||||||
ctx.green(' -> ') +
|
ctx.green(' -> ') +
|
||||||
|
|
|
@ -96,7 +96,7 @@ const pushUseConnect = () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Sentry.setContext('Push', { expoToken, pushEnabledCount })
|
Sentry.setTags({ expoToken, pushEnabledCount })
|
||||||
|
|
||||||
if (expoToken && pushEnabledCount) {
|
if (expoToken && pushEnabledCount) {
|
||||||
connectQuery.refetch()
|
connectQuery.refetch()
|
||||||
|
|
|
@ -6,9 +6,9 @@ const log = (type: 'log' | 'warn' | 'error', func: string, message: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'log':
|
case 'log':
|
||||||
console.log(
|
console.log(
|
||||||
ctx.bgBlue.white.bold(' Start up ') +
|
ctx.bgGrey.white.bold(' Start up ') +
|
||||||
' ' +
|
' ' +
|
||||||
ctx.bgBlueBright.black(` ${func} `) +
|
ctx.bgGrey.black(` ${func} `) +
|
||||||
' ' +
|
' ' +
|
||||||
message
|
message
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,8 +4,10 @@ import log from './log'
|
||||||
const timezone = () => {
|
const timezone = () => {
|
||||||
log('log', 'Timezone', Localization.getCalendars()[0].timeZone || 'unknown')
|
log('log', 'Timezone', Localization.getCalendars()[0].timeZone || 'unknown')
|
||||||
if ('__setDefaultTimeZone' in Intl.DateTimeFormat) {
|
if ('__setDefaultTimeZone' in Intl.DateTimeFormat) {
|
||||||
// @ts-ignore
|
try {
|
||||||
Intl.DateTimeFormat.__setDefaultTimeZone(Localization.getCalendars()[0].timeZone)
|
// @ts-ignore
|
||||||
|
Intl.DateTimeFormat.__setDefaultTimeZone(Intl.DateTimeFormat.__setDefaultTimeZone('xxx'))
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -228,6 +228,41 @@ export const setAccount = async (account: string) => {
|
||||||
queryClient.clear()
|
queryClient.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ReadableAccountType = {
|
||||||
|
acct: string
|
||||||
|
key: string
|
||||||
|
active: boolean
|
||||||
|
}
|
||||||
|
export const getReadableAccounts = (): ReadableAccountType[] => {
|
||||||
|
const accountActive = getGlobalStorage.string('account.active')
|
||||||
|
const accounts = getGlobalStorage.object('accounts')?.sort((a, b) => a.localeCompare(b))
|
||||||
|
accounts?.splice(
|
||||||
|
accounts.findIndex(a => a === accountActive),
|
||||||
|
1
|
||||||
|
)
|
||||||
|
accounts?.unshift(accountActive || '')
|
||||||
|
return (
|
||||||
|
accounts?.map(account => {
|
||||||
|
const details = getAccountDetails(
|
||||||
|
['auth.account.acct', 'auth.account.domain', 'auth.domain', 'auth.account.id'],
|
||||||
|
account
|
||||||
|
)
|
||||||
|
if (details) {
|
||||||
|
return {
|
||||||
|
acct: `@${details['auth.account.acct']}@${details['auth.account.domain']}`,
|
||||||
|
key: generateAccountKey({
|
||||||
|
domain: details['auth.domain'],
|
||||||
|
id: details['auth.account.id']
|
||||||
|
}),
|
||||||
|
active: account === accountActive
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return { acct: '', key: '', active: false }
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
|
).filter(a => a.acct.length)
|
||||||
|
}
|
||||||
|
|
||||||
export const removeAccount = async (account: string) => {
|
export const removeAccount = async (account: string) => {
|
||||||
const currAccounts: NonNullable<StorageGlobal['accounts']> = JSON.parse(
|
const currAccounts: NonNullable<StorageGlobal['accounts']> = JSON.parse(
|
||||||
storage.global.getString('accounts') || '[]'
|
storage.global.getString('accounts') || '[]'
|
||||||
|
|
Loading…
Reference in New Issue