mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Merge branch 'main' into candidate
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tooot",
|
||||
"version": "4.8.7",
|
||||
"version": "4.8.8",
|
||||
"description": "tooot for Mastodon",
|
||||
"author": "xmflsct <me@xmflsct.com>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
@ -160,7 +160,7 @@ const Timeline: React.FC<Props> = ({
|
||||
viewabilityConfig: {
|
||||
minimumViewTime: 300,
|
||||
itemVisiblePercentThreshold: 80,
|
||||
waitForInteraction: true
|
||||
waitForInteraction: false
|
||||
},
|
||||
onViewableItemsChanged: ({ viewableItems }) => {
|
||||
const marker = readMarker ? getAccountStorage.string(readMarker) : undefined
|
||||
|
@ -46,17 +46,9 @@ const openLink = async (url: string, navigation?: any) => {
|
||||
|
||||
// If an account can be found
|
||||
if (match?.account) {
|
||||
if (!match.account._remote && match.account.id) {
|
||||
handleNavigation('Tab-Shared-Account', { account: match.account.id })
|
||||
return
|
||||
}
|
||||
|
||||
let response: Mastodon.Account | undefined = undefined
|
||||
|
||||
const queryKey: QueryKeyAccount = [
|
||||
'Account',
|
||||
{ id: match.account.id, url: url, _remote: match.account._remote }
|
||||
]
|
||||
const queryKey: QueryKeyAccount = ['Account', { url: url, _remote: match.account._remote }]
|
||||
const cache = queryClient.getQueryData<Mastodon.Status>(queryKey)
|
||||
|
||||
if (cache) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Button from '@components/Button'
|
||||
import Icon from '@components/Icon'
|
||||
import { Loading } from '@components/Loading'
|
||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||
import { displayMessage } from '@components/Message'
|
||||
import CustomText from '@components/Text'
|
||||
@ -7,7 +8,6 @@ import * as Sentry from '@sentry/react-native'
|
||||
import apiInstance from '@utils/api/instance'
|
||||
import apiTooot, { TOOOT_API_DOMAIN } from '@utils/api/tooot'
|
||||
import browserPackage from '@utils/helpers/browserPackage'
|
||||
import { isDevelopment } from '@utils/helpers/checkEnvironment'
|
||||
import { PUSH_ADMIN, PUSH_DEFAULT, setChannels } from '@utils/push/constants'
|
||||
import { updateExpoToken } from '@utils/push/updateExpoToken'
|
||||
import { useAppsQuery } from '@utils/queryHooks/apps'
|
||||
@ -35,24 +35,7 @@ const TabMePush: React.FC = () => {
|
||||
const [accountDomain] = useAccountStorage.string('auth.account.domain')
|
||||
const [accountId] = useAccountStorage.string('auth.account.id')
|
||||
|
||||
const appsQuery = useAppsQuery({
|
||||
options: {
|
||||
enabled: false,
|
||||
onSuccess: async data => {
|
||||
if (data.vapid_key) {
|
||||
await checkPush()
|
||||
if (isDevelopment) {
|
||||
setPushAvailable(true)
|
||||
} else {
|
||||
setPushAvailable(!!expoToken?.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
useEffect(() => {
|
||||
appsQuery.refetch()
|
||||
}, [])
|
||||
const appsQuery = useAppsQuery()
|
||||
|
||||
const checkPush = async () => {
|
||||
const permissions = await Notifications.getPermissionsAsync()
|
||||
@ -61,11 +44,9 @@ const TabMePush: React.FC = () => {
|
||||
layoutAnimation()
|
||||
await updateExpoToken()
|
||||
}
|
||||
|
||||
const [pushAvailable, setPushAvailable] = useState<boolean>()
|
||||
const [pushEnabled, setPushEnabled] = useState<boolean>()
|
||||
const [pushCanAskAgain, setPushCanAskAgain] = useState<boolean>()
|
||||
|
||||
useEffect(() => {
|
||||
checkPush()
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
const subscription = AppState.addEventListener('change', checkPush)
|
||||
return () => {
|
||||
@ -73,6 +54,9 @@ const TabMePush: React.FC = () => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const [pushEnabled, setPushEnabled] = useState<boolean>()
|
||||
const [pushCanAskAgain, setPushCanAskAgain] = useState<boolean>()
|
||||
|
||||
const alerts = () =>
|
||||
push?.alerts
|
||||
? PUSH_DEFAULT().map(alert => (
|
||||
@ -133,11 +117,11 @@ const TabMePush: React.FC = () => {
|
||||
const pushPath = `${expoToken}/${domain}/${accountId}`
|
||||
const accountFull = `@${accountAcct}@${accountDomain}`
|
||||
|
||||
return (
|
||||
return appsQuery.isFetched ? (
|
||||
<ScrollView>
|
||||
{!!appsQuery.data?.vapid_key ? (
|
||||
<>
|
||||
{!!pushAvailable ? (
|
||||
{!!expoToken?.length ? (
|
||||
<>
|
||||
{pushEnabled === false ? (
|
||||
<MenuContainer>
|
||||
@ -249,6 +233,7 @@ const TabMePush: React.FC = () => {
|
||||
method: 'delete',
|
||||
url: 'push/subscription'
|
||||
})
|
||||
return Promise.reject()
|
||||
})
|
||||
|
||||
setAccountStorage([
|
||||
@ -352,6 +337,8 @@ const TabMePush: React.FC = () => {
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
) : (
|
||||
<Loading style={{ flex: 1 }} />
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,11 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
||||
body: data.map(remote => {
|
||||
const localMatch = old?.pages[0].body.find(local => local.uri === remote.uri)
|
||||
if (localMatch) {
|
||||
return { ...localMatch, _level: remote._level }
|
||||
return {
|
||||
...localMatch,
|
||||
_level: remote._level,
|
||||
key: `${localMatch.id}_remote`
|
||||
}
|
||||
} else {
|
||||
return appendRemote.status(remote)
|
||||
}
|
||||
@ -275,6 +279,7 @@ const TabSharedToot: React.FC<TabSharedStackScreenProps<'Tab-Shared-Toot'>> = ({
|
||||
ref={flRef}
|
||||
windowSize={5}
|
||||
data={query.data?.pages?.[0].body}
|
||||
extraData={query.dataUpdatedAt}
|
||||
renderItem={({ item, index }) => {
|
||||
const prev = query.data?.pages[0].body[index - 1]?._level || 0
|
||||
const curr = item._level || 0
|
||||
|
@ -9,7 +9,7 @@ export const urlMatcher = (
|
||||
):
|
||||
| {
|
||||
domain: string
|
||||
account?: Partial<Pick<Mastodon.Account, 'id' | 'acct' | '_remote'>>
|
||||
account?: Partial<Pick<Mastodon.Account, 'acct' | '_remote'>>
|
||||
status?: Partial<Pick<Mastodon.Status, 'id' | '_remote'>>
|
||||
}
|
||||
| undefined => {
|
||||
@ -24,13 +24,18 @@ export const urlMatcher = (
|
||||
const _remote = parsed.hostname !== getAccountStorage.string('auth.domain')
|
||||
|
||||
let statusId: string | undefined
|
||||
let accountId: string | undefined
|
||||
let accountAcct: string | undefined
|
||||
|
||||
const segments = parsed.pathname.split('/')
|
||||
const last = segments[segments.length - 1]
|
||||
const length = segments.length // there is a starting slash
|
||||
|
||||
const testAndAssignStatusId = (id: string) => {
|
||||
if (!!parseInt(id)) {
|
||||
statusId = id
|
||||
}
|
||||
}
|
||||
|
||||
switch (last?.startsWith('@')) {
|
||||
case true:
|
||||
if (length === 2 || (length === 3 && segments[length - 2] === 'web')) {
|
||||
@ -45,14 +50,14 @@ export const urlMatcher = (
|
||||
if (nextToLast === 'statuses') {
|
||||
if (length === 4 && segments[length - 3] === 'web') {
|
||||
// https://social.xmflsct.com/web/statuses/105590085754428765 <- old
|
||||
statusId = last
|
||||
testAndAssignStatusId(last)
|
||||
} else if (
|
||||
length === 5 &&
|
||||
segments[length - 2] === 'statuses' &&
|
||||
segments[length - 4] === 'users'
|
||||
) {
|
||||
// https://social.xmflsct.com/users/tooot/statuses/105590085754428765 <- default Mastodon
|
||||
statusId = last
|
||||
testAndAssignStatusId(last)
|
||||
// accountAcct = `@${segments[length - 3]}@${domain}`
|
||||
}
|
||||
} else if (
|
||||
@ -61,7 +66,7 @@ export const urlMatcher = (
|
||||
) {
|
||||
// https://social.xmflsct.com/web/@tooot/105590085754428765 <- pretty Mastodon v3.5 and below
|
||||
// https://social.xmflsct.com/@tooot/105590085754428765 <- pretty Mastodon v4.0 and above
|
||||
statusId = last
|
||||
testAndAssignStatusId(last)
|
||||
// accountAcct = `${nextToLast}@${domain}`
|
||||
}
|
||||
}
|
||||
@ -70,7 +75,7 @@ export const urlMatcher = (
|
||||
|
||||
return {
|
||||
domain,
|
||||
...((accountId || accountAcct) && { account: { id: accountId, acct: accountAcct, _remote } }),
|
||||
...(accountAcct && { account: { acct: accountAcct, _remote } }),
|
||||
...(statusId && { status: { id: statusId, _remote } })
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ const accountQueryFunction = async ({ queryKey }: QueryFunctionContext<QueryKeyA
|
||||
const match = urlMatcher(key.url)
|
||||
|
||||
const domain = match?.domain
|
||||
const id = key.id || match?.account?.id
|
||||
const id = key.id
|
||||
const acct = key.acct || key.username || match?.account?.acct
|
||||
|
||||
if (!key._local && domain) {
|
||||
|
@ -43,6 +43,11 @@ const useColorSchemeDelay = (delay = 250) => {
|
||||
return colorScheme
|
||||
}
|
||||
|
||||
const determineMode = (
|
||||
osTheme: 'light' | 'dark' | null | undefined,
|
||||
userTheme: StorageGlobal['app.theme']
|
||||
): 'light' | 'dark' =>
|
||||
!userTheme || userTheme === 'auto' ? osTheme || 'light' : userTheme || 'light'
|
||||
const determineTheme = (
|
||||
osTheme: 'light' | 'dark' | null | undefined,
|
||||
userTheme: StorageGlobal['app.theme'],
|
||||
@ -75,13 +80,11 @@ const ThemeManager: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
const [userTheme] = useGlobalStorage.string('app.theme')
|
||||
const [darkTheme] = useGlobalStorage.string('app.theme.dark')
|
||||
|
||||
const [mode, setMode] = useState<'light' | 'dark'>(
|
||||
userTheme === 'auto' ? osTheme || 'light' : userTheme || 'light'
|
||||
)
|
||||
const [mode, setMode] = useState<'light' | 'dark'>(determineMode(osTheme, userTheme))
|
||||
const [theme, setTheme] = useState<Theme>(determineTheme(osTheme, userTheme, darkTheme))
|
||||
|
||||
useEffect(() => {
|
||||
setMode(userTheme === 'auto' ? osTheme || 'light' : userTheme || 'light')
|
||||
setMode(determineMode(osTheme, userTheme))
|
||||
}, [osTheme, userTheme])
|
||||
useEffect(() => {
|
||||
setTheme(determineTheme(osTheme, userTheme, darkTheme))
|
||||
|
Reference in New Issue
Block a user