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