mirror of
https://github.com/tooot-app/app
synced 2025-04-19 04:37:24 +02:00
Restructure removing remote
This commit is contained in:
parent
9fdf3ab640
commit
45681fc1f5
@ -30,6 +30,9 @@ export default (): ExpoConfig => ({
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
ios: {
|
||||||
|
bundleIdentifier: 'com.xmflsct.app.tooot'
|
||||||
|
},
|
||||||
android: {
|
android: {
|
||||||
versionCode: 4,
|
versionCode: 4,
|
||||||
package: 'com.xmflsct.app.tooot',
|
package: 'com.xmflsct.app.tooot',
|
||||||
|
@ -49,6 +49,9 @@ PODS:
|
|||||||
- UMCore
|
- UMCore
|
||||||
- UMPermissionsInterface
|
- UMPermissionsInterface
|
||||||
- UMTaskManagerInterface
|
- UMTaskManagerInterface
|
||||||
|
- EXNotifications (0.8.2):
|
||||||
|
- UMCore
|
||||||
|
- UMPermissionsInterface
|
||||||
- EXPermissions (10.0.0):
|
- EXPermissions (10.0.0):
|
||||||
- UMCore
|
- UMCore
|
||||||
- UMPermissionsInterface
|
- UMPermissionsInterface
|
||||||
@ -499,6 +502,7 @@ DEPENDENCIES:
|
|||||||
- EXLinearGradient (from `../node_modules/expo-linear-gradient/ios`)
|
- EXLinearGradient (from `../node_modules/expo-linear-gradient/ios`)
|
||||||
- EXLocalization (from `../node_modules/expo-localization/ios`)
|
- EXLocalization (from `../node_modules/expo-localization/ios`)
|
||||||
- EXLocation (from `../node_modules/expo-location/ios`)
|
- EXLocation (from `../node_modules/expo-location/ios`)
|
||||||
|
- EXNotifications (from `../node_modules/expo-notifications/ios`)
|
||||||
- EXPermissions (from `../node_modules/expo-permissions/ios`)
|
- EXPermissions (from `../node_modules/expo-permissions/ios`)
|
||||||
- EXRandom (from `../node_modules/expo-random/ios`)
|
- EXRandom (from `../node_modules/expo-random/ios`)
|
||||||
- EXScreenCapture (from `../node_modules/expo-screen-capture/ios`)
|
- EXScreenCapture (from `../node_modules/expo-screen-capture/ios`)
|
||||||
@ -622,6 +626,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-localization/ios"
|
:path: "../node_modules/expo-localization/ios"
|
||||||
EXLocation:
|
EXLocation:
|
||||||
:path: "../node_modules/expo-location/ios"
|
:path: "../node_modules/expo-location/ios"
|
||||||
|
EXNotifications:
|
||||||
|
:path: "../node_modules/expo-notifications/ios"
|
||||||
EXPermissions:
|
EXPermissions:
|
||||||
:path: "../node_modules/expo-permissions/ios"
|
:path: "../node_modules/expo-permissions/ios"
|
||||||
EXRandom:
|
EXRandom:
|
||||||
@ -769,6 +775,7 @@ SPEC CHECKSUMS:
|
|||||||
EXLinearGradient: c803fbd1aa974be038177b1e45524bc35759fe9c
|
EXLinearGradient: c803fbd1aa974be038177b1e45524bc35759fe9c
|
||||||
EXLocalization: 8b9463c81843da214476b541a27811dd885c9a76
|
EXLocalization: 8b9463c81843da214476b541a27811dd885c9a76
|
||||||
EXLocation: d55e2a37f61bcfb4eba9c813b3f4621d896c4c00
|
EXLocation: d55e2a37f61bcfb4eba9c813b3f4621d896c4c00
|
||||||
|
EXNotifications: fba3319b1555961b99ddd185021b4c7ff978d5dd
|
||||||
EXPermissions: 17d4846ad1880f6891c74ae58ca1acb43e47ed47
|
EXPermissions: 17d4846ad1880f6891c74ae58ca1acb43e47ed47
|
||||||
EXRandom: d7e0f3dd64810aabd27d59f8ecffee359177e2c3
|
EXRandom: d7e0f3dd64810aabd27d59f8ecffee359177e2c3
|
||||||
EXScreenCapture: 5b8447139e56e2b922e93ccdc7c773c103fb44fd
|
EXScreenCapture: 5b8447139e56e2b922e93ccdc7c773c103fb44fd
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
"expo-linear-gradient": "~8.4.0",
|
"expo-linear-gradient": "~8.4.0",
|
||||||
"expo-linking": "~2.0.1",
|
"expo-linking": "~2.0.1",
|
||||||
"expo-localization": "~9.1.0",
|
"expo-localization": "~9.1.0",
|
||||||
|
"expo-notifications": "~0.8.2",
|
||||||
"expo-random": "~10.0.0",
|
"expo-random": "~10.0.0",
|
||||||
"expo-screen-capture": "^3.0.0",
|
"expo-screen-capture": "^3.0.0",
|
||||||
"expo-secure-store": "~9.3.0",
|
"expo-secure-store": "~9.3.0",
|
||||||
|
13
src/@types/mastodon.d.ts
vendored
13
src/@types/mastodon.d.ts
vendored
@ -343,6 +343,19 @@ declare namespace Mastodon {
|
|||||||
'reading:expand:spoilers'?: boolean
|
'reading:expand:spoilers'?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PushSubscription = {
|
||||||
|
id: string
|
||||||
|
endpoint: string
|
||||||
|
alerts: {
|
||||||
|
follow: boolean
|
||||||
|
favourite: boolean
|
||||||
|
reblog: boolean
|
||||||
|
mention: boolean
|
||||||
|
poll: boolean
|
||||||
|
}
|
||||||
|
server_key: string
|
||||||
|
}
|
||||||
|
|
||||||
type Relationship = {
|
type Relationship = {
|
||||||
id: string
|
id: string
|
||||||
following: boolean
|
following: boolean
|
||||||
|
2
src/@types/react-navigation.d.ts
vendored
2
src/@types/react-navigation.d.ts
vendored
@ -117,7 +117,7 @@ declare namespace Nav {
|
|||||||
title: Mastodon.List['title']
|
title: Mastodon.List['title']
|
||||||
}
|
}
|
||||||
'Tab-Me-Settings': undefined
|
'Tab-Me-Settings': undefined
|
||||||
'Tab-Me-Settings-UpdateRemote': undefined
|
'Tab-Me-Settings-Notification': undefined
|
||||||
'Tab-Me-Switch': undefined
|
'Tab-Me-Switch': undefined
|
||||||
} & TabSharedStackParamList
|
} & TabSharedStackParamList
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { toast, toastConfig } from '@components/toast'
|
import { toast, toastConfig } from '@components/toast'
|
||||||
import {
|
import {
|
||||||
NavigationContainer,
|
NavigationContainer,
|
||||||
@ -10,10 +10,8 @@ import ScreenCompose from '@screens/Compose'
|
|||||||
import ScreenImagesViewer from '@screens/ImagesViewer'
|
import ScreenImagesViewer from '@screens/ImagesViewer'
|
||||||
import ScreenTabs from '@screens/Tabs'
|
import ScreenTabs from '@screens/Tabs'
|
||||||
import { updatePreviousTab } from '@utils/slices/contextsSlice'
|
import { updatePreviousTab } from '@utils/slices/contextsSlice'
|
||||||
import {
|
import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences'
|
||||||
getLocalActiveIndex,
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
updateLocalAccountPreferences
|
|
||||||
} from '@utils/slices/instancesSlice'
|
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { themes } from '@utils/styles/themes'
|
import { themes } from '@utils/styles/themes'
|
||||||
import * as Analytics from 'expo-firebase-analytics'
|
import * as Analytics from 'expo-firebase-analytics'
|
||||||
@ -36,7 +34,7 @@ export const navigationRef = createRef<NavigationContainerRef>()
|
|||||||
const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const { mode, theme } = useTheme()
|
const { mode, theme } = useTheme()
|
||||||
enum barStyle {
|
enum barStyle {
|
||||||
light = 'dark-content',
|
light = 'dark-content',
|
||||||
@ -89,10 +87,9 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
|||||||
|
|
||||||
// On launch check if there is any unread announcements
|
// On launch check if there is any unread announcements
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localActiveIndex !== null &&
|
instanceActive !== -1 &&
|
||||||
client<Mastodon.Announcement[]>({
|
apiInstance<Mastodon.Announcement[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `announcements`
|
url: `announcements`
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
@ -107,8 +104,8 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
|||||||
|
|
||||||
// Lazily update users's preferences, for e.g. composing default visibility
|
// Lazily update users's preferences, for e.g. composing default visibility
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localActiveIndex !== null) {
|
if (instanceActive !== -1) {
|
||||||
dispatch(updateLocalAccountPreferences())
|
dispatch(updateAccountPreferences())
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
87
src/api/general.ts
Normal file
87
src/api/general.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import chalk from 'chalk'
|
||||||
|
|
||||||
|
const ctx = new chalk.Instance({ level: 3 })
|
||||||
|
|
||||||
|
export type Params = {
|
||||||
|
method: 'get' | 'post' | 'put' | 'delete'
|
||||||
|
domain?: string
|
||||||
|
url: string
|
||||||
|
params?: {
|
||||||
|
[key: string]: string | number | boolean
|
||||||
|
}
|
||||||
|
headers?: { [key: string]: string }
|
||||||
|
body?: FormData | Object
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiGeneral = async <T = unknown>({
|
||||||
|
method,
|
||||||
|
domain,
|
||||||
|
url,
|
||||||
|
params,
|
||||||
|
headers,
|
||||||
|
body
|
||||||
|
}: Params): Promise<{ body: T }> => {
|
||||||
|
if (!domain) {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
ctx.bgGreen.bold(' API general ') +
|
||||||
|
' ' +
|
||||||
|
domain +
|
||||||
|
' ' +
|
||||||
|
method +
|
||||||
|
ctx.green(' -> ') +
|
||||||
|
`/${url}` +
|
||||||
|
(params ? ctx.green(' -> ') : ''),
|
||||||
|
params ? params : ''
|
||||||
|
)
|
||||||
|
|
||||||
|
return axios({
|
||||||
|
timeout: method === 'post' ? 1000 * 60 : 1000 * 15,
|
||||||
|
method,
|
||||||
|
baseURL: `https://${domain}/`,
|
||||||
|
url,
|
||||||
|
params,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...headers
|
||||||
|
},
|
||||||
|
...(body && { data: body })
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
return Promise.resolve({
|
||||||
|
body: response.data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if (error.response) {
|
||||||
|
// The request was made and the server responded with a status code
|
||||||
|
// that falls out of the range of 2xx
|
||||||
|
console.error(
|
||||||
|
ctx.bold(' API general '),
|
||||||
|
ctx.bold('response'),
|
||||||
|
error.response.status,
|
||||||
|
error.response.data.error
|
||||||
|
)
|
||||||
|
return Promise.reject(error.response)
|
||||||
|
} else if (error.request) {
|
||||||
|
// The request was made but no response was received
|
||||||
|
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||||
|
// http.ClientRequest in node.js
|
||||||
|
console.error(ctx.bold(' API general '), ctx.bold('request'), error)
|
||||||
|
return Promise.reject()
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
ctx.bold(' API general '),
|
||||||
|
ctx.bold('internal'),
|
||||||
|
error.message,
|
||||||
|
url
|
||||||
|
)
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default apiGeneral
|
@ -5,22 +5,8 @@ import li from 'li'
|
|||||||
|
|
||||||
const ctx = new chalk.Instance({ level: 3 })
|
const ctx = new chalk.Instance({ level: 3 })
|
||||||
|
|
||||||
const client = async <T = unknown>({
|
export type Params = {
|
||||||
method,
|
|
||||||
instance,
|
|
||||||
localIndex,
|
|
||||||
instanceDomain,
|
|
||||||
version = 'v1',
|
|
||||||
url,
|
|
||||||
params,
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
onUploadProgress
|
|
||||||
}: {
|
|
||||||
method: 'get' | 'post' | 'put' | 'delete'
|
method: 'get' | 'post' | 'put' | 'delete'
|
||||||
instance: 'local' | 'remote'
|
|
||||||
localIndex?: number
|
|
||||||
instanceDomain?: string
|
|
||||||
version?: 'v1' | 'v2'
|
version?: 'v1' | 'v2'
|
||||||
url: string
|
url: string
|
||||||
params?: {
|
params?: {
|
||||||
@ -29,30 +15,37 @@ const client = async <T = unknown>({
|
|||||||
headers?: { [key: string]: string }
|
headers?: { [key: string]: string }
|
||||||
body?: FormData
|
body?: FormData
|
||||||
onUploadProgress?: (progressEvent: any) => void
|
onUploadProgress?: (progressEvent: any) => void
|
||||||
}): Promise<{ body: T; links: { prev?: string; next?: string } }> => {
|
}
|
||||||
const { store } = require('@root/store')
|
|
||||||
const state = (store.getState() as RootState).instances
|
|
||||||
const theLocalIndex =
|
|
||||||
localIndex !== undefined ? localIndex : state.local.activeIndex
|
|
||||||
|
|
||||||
let domain = null
|
const apiInstance = async <T = unknown>({
|
||||||
let token = null
|
method,
|
||||||
if (instance === 'remote') {
|
version = 'v1',
|
||||||
domain = instanceDomain || state.remote.url
|
url,
|
||||||
} else {
|
params,
|
||||||
if (theLocalIndex !== null && state.local.instances[theLocalIndex]) {
|
headers,
|
||||||
domain = state.local.instances[theLocalIndex].url
|
body,
|
||||||
token = state.local.instances[theLocalIndex].token
|
onUploadProgress
|
||||||
|
}: Params): Promise<{ body: T; links: { prev?: string; next?: string } }> => {
|
||||||
|
const { store } = require('@root/store')
|
||||||
|
const state = store.getState() as RootState
|
||||||
|
const instanceActive = state.instances.instances.findIndex(
|
||||||
|
instance => instance.active
|
||||||
|
)
|
||||||
|
|
||||||
|
let domain
|
||||||
|
let token
|
||||||
|
if (instanceActive !== -1 && state.instances.instances[instanceActive]) {
|
||||||
|
domain = state.instances.instances[instanceActive].url
|
||||||
|
token = state.instances.instances[instanceActive].token
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.error(
|
||||||
ctx.bgRed.white.bold(' API ') + ' ' + 'No instance domain is provided'
|
ctx.bgRed.white.bold(' API ') + ' ' + 'No instance domain is provided'
|
||||||
)
|
)
|
||||||
return Promise.reject()
|
return Promise.reject()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
ctx.bgGreen.bold(' API ') +
|
ctx.bgGreen.bold(' API instance ') +
|
||||||
' ' +
|
' ' +
|
||||||
domain +
|
domain +
|
||||||
' ' +
|
' ' +
|
||||||
@ -97,7 +90,7 @@ const client = async <T = unknown>({
|
|||||||
// The request was made and the server responded with a status code
|
// The request was made and the server responded with a status code
|
||||||
// that falls out of the range of 2xx
|
// that falls out of the range of 2xx
|
||||||
console.error(
|
console.error(
|
||||||
ctx.bold(' API '),
|
ctx.bold(' API instance '),
|
||||||
ctx.bold('response'),
|
ctx.bold('response'),
|
||||||
error.response.status,
|
error.response.status,
|
||||||
error.response.data.error
|
error.response.data.error
|
||||||
@ -107,11 +100,11 @@ const client = async <T = unknown>({
|
|||||||
// The request was made but no response was received
|
// The request was made but no response was received
|
||||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||||
// http.ClientRequest in node.js
|
// http.ClientRequest in node.js
|
||||||
console.error(ctx.bold(' API '), ctx.bold('request'), error)
|
console.error(ctx.bold(' API instance '), ctx.bold('request'), error)
|
||||||
return Promise.reject()
|
return Promise.reject()
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.error(
|
||||||
ctx.bold(' API '),
|
ctx.bold(' API instance '),
|
||||||
ctx.bold('internal'),
|
ctx.bold('internal'),
|
||||||
error.message,
|
error.message,
|
||||||
url
|
url
|
||||||
@ -121,4 +114,4 @@ const client = async <T = unknown>({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default client
|
export default apiInstance
|
@ -1,7 +1,7 @@
|
|||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import {
|
import {
|
||||||
getLocalInstance,
|
getInstance,
|
||||||
updateLocalNotification
|
updateInstanceNotification
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
@ -18,7 +18,7 @@ const useWebsocket = ({
|
|||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const localInstance = useSelector(
|
const localInstance = useSelector(
|
||||||
getLocalInstance,
|
getInstance,
|
||||||
(prev, next) =>
|
(prev, next) =>
|
||||||
prev?.urls.streaming_api === next?.urls.streaming_api &&
|
prev?.urls.streaming_api === next?.urls.streaming_api &&
|
||||||
prev?.token === next?.token
|
prev?.token === next?.token
|
||||||
@ -39,7 +39,7 @@ const useWebsocket = ({
|
|||||||
case 'notification':
|
case 'notification':
|
||||||
const payload: Mastodon.Notification = JSON.parse(message.payload)
|
const payload: Mastodon.Notification = JSON.parse(message.payload)
|
||||||
dispatch(
|
dispatch(
|
||||||
updateLocalNotification({ latestTime: payload.created_at })
|
updateInstanceNotification({ latestTime: payload.created_at })
|
||||||
)
|
)
|
||||||
const queryKey: QueryKeyTimeline = [
|
const queryKey: QueryKeyTimeline = [
|
||||||
'Timeline',
|
'Timeline',
|
||||||
|
@ -2,7 +2,7 @@ import Button from '@components/Button'
|
|||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { useAppsQuery } from '@utils/queryHooks/apps'
|
import { useAppsQuery } from '@utils/queryHooks/apps'
|
||||||
import { useInstanceQuery } from '@utils/queryHooks/instance'
|
import { useInstanceQuery } from '@utils/queryHooks/instance'
|
||||||
import { getLocalInstances } from '@utils/slices/instancesSlice'
|
import { getInstances } from '@utils/slices/instancesSlice'
|
||||||
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 WebBrowser from 'expo-web-browser'
|
import * as WebBrowser from 'expo-web-browser'
|
||||||
@ -14,7 +14,6 @@ import { useSelector } from 'react-redux'
|
|||||||
import { Placeholder, Fade } from 'rn-placeholder'
|
import { Placeholder, Fade } from 'rn-placeholder'
|
||||||
import analytics from './analytics'
|
import analytics from './analytics'
|
||||||
import InstanceAuth from './Instance/Auth'
|
import InstanceAuth from './Instance/Auth'
|
||||||
import EULA from './Instance/EULA'
|
|
||||||
import InstanceInfo from './Instance/Info'
|
import InstanceInfo from './Instance/Info'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -29,26 +28,23 @@ const ComponentInstance: React.FC<Props> = ({
|
|||||||
const { t } = useTranslation('componentInstance')
|
const { t } = useTranslation('componentInstance')
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const localInstances = useSelector(getLocalInstances, () => true)
|
const instances = useSelector(getInstances, () => true)
|
||||||
const [instanceDomain, setInstanceDomain] = useState<string>()
|
const [domain, setDomain] = useState<string>()
|
||||||
|
|
||||||
const instanceQuery = useInstanceQuery({
|
const instanceQuery = useInstanceQuery({
|
||||||
instanceDomain,
|
domain,
|
||||||
options: { enabled: false, retry: false }
|
options: { enabled: !!domain, retry: false }
|
||||||
})
|
})
|
||||||
const appsQuery = useAppsQuery({
|
const appsQuery = useAppsQuery({
|
||||||
instanceDomain,
|
domain,
|
||||||
options: { enabled: false, retry: false }
|
options: { enabled: false, retry: false }
|
||||||
})
|
})
|
||||||
|
|
||||||
const onChangeText = useCallback(
|
const onChangeText = useCallback(
|
||||||
debounce(
|
debounce(
|
||||||
text => {
|
text => {
|
||||||
setInstanceDomain(text.replace(/^http(s)?\:\/\//i, ''))
|
setDomain(text.replace(/^http(s)?\:\/\//i, ''))
|
||||||
appsQuery.remove()
|
appsQuery.remove()
|
||||||
if (text) {
|
|
||||||
instanceQuery.refetch()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
1000,
|
1000,
|
||||||
{ trailing: true }
|
{ trailing: true }
|
||||||
@ -57,40 +53,35 @@ const ComponentInstance: React.FC<Props> = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const processUpdate = useCallback(() => {
|
const processUpdate = useCallback(() => {
|
||||||
if (instanceDomain) {
|
if (domain) {
|
||||||
analytics('instance_local_login')
|
analytics('instance_login')
|
||||||
if (
|
if (
|
||||||
localInstances &&
|
instances &&
|
||||||
localInstances.filter(instance => instance.url === instanceDomain)
|
instances.filter(instance => instance.url === domain).length
|
||||||
.length
|
|
||||||
) {
|
) {
|
||||||
Alert.alert(
|
Alert.alert(t('update.alert.title'), t('update.alert.message'), [
|
||||||
t('update.local.alert.title'),
|
|
||||||
t('update.local.alert.message'),
|
|
||||||
[
|
|
||||||
{
|
{
|
||||||
text: t('update.local.alert.buttons.cancel'),
|
text: t('update.alert.buttons.cancel'),
|
||||||
style: 'cancel'
|
style: 'cancel'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('update.local.alert.buttons.continue'),
|
text: t('update.alert.buttons.continue'),
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
appsQuery.refetch()
|
appsQuery.refetch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
])
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
appsQuery.refetch()
|
appsQuery.refetch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [instanceDomain])
|
}, [domain])
|
||||||
|
|
||||||
const onSubmitEditing = useCallback(
|
const onSubmitEditing = useCallback(
|
||||||
({ nativeEvent: { text } }) => {
|
({ nativeEvent: { text } }) => {
|
||||||
analytics('instance_textinput_submit', { match: text === instanceDomain })
|
analytics('instance_textinput_submit', { match: text === domain })
|
||||||
if (
|
if (
|
||||||
text === instanceDomain &&
|
text === domain &&
|
||||||
instanceQuery.isSuccess &&
|
instanceQuery.isSuccess &&
|
||||||
instanceQuery.data &&
|
instanceQuery.data &&
|
||||||
instanceQuery.data.uri
|
instanceQuery.data.uri
|
||||||
@ -98,12 +89,12 @@ const ComponentInstance: React.FC<Props> = ({
|
|||||||
processUpdate()
|
processUpdate()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[instanceDomain, instanceQuery.isSuccess, instanceQuery.data]
|
[domain, instanceQuery.isSuccess, instanceQuery.data]
|
||||||
)
|
)
|
||||||
|
|
||||||
const requestAuth = useMemo(() => {
|
const requestAuth = useMemo(() => {
|
||||||
if (
|
if (
|
||||||
instanceDomain &&
|
domain &&
|
||||||
instanceQuery.data?.uri &&
|
instanceQuery.data?.uri &&
|
||||||
appsQuery.data?.client_id &&
|
appsQuery.data?.client_id &&
|
||||||
appsQuery.data.client_secret
|
appsQuery.data.client_secret
|
||||||
@ -111,7 +102,7 @@ const ComponentInstance: React.FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<InstanceAuth
|
<InstanceAuth
|
||||||
key={Math.random()}
|
key={Math.random()}
|
||||||
instanceDomain={instanceDomain}
|
instanceDomain={domain}
|
||||||
instance={instanceQuery.data}
|
instance={instanceQuery.data}
|
||||||
appData={{
|
appData={{
|
||||||
clientId: appsQuery.data.client_id,
|
clientId: appsQuery.data.client_id,
|
||||||
@ -121,9 +112,7 @@ const ComponentInstance: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [instanceDomain, instanceQuery.data, appsQuery.data])
|
}, [domain, instanceQuery.data, appsQuery.data])
|
||||||
|
|
||||||
const [agreed, setAgreed] = useState(false)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -160,15 +149,13 @@ const ComponentInstance: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
content={t('server.button.local')}
|
content={t('server.button')}
|
||||||
onPress={processUpdate}
|
onPress={processUpdate}
|
||||||
disabled={!instanceQuery.data?.uri}
|
disabled={!instanceQuery.data?.uri}
|
||||||
loading={instanceQuery.isFetching || appsQuery.isFetching}
|
loading={instanceQuery.isFetching || appsQuery.isFetching}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* <EULA agreed={agreed} setAgreed={setAgreed} /> */}
|
|
||||||
|
|
||||||
<View>
|
<View>
|
||||||
<Placeholder
|
<Placeholder
|
||||||
{...(instanceQuery.isFetching && {
|
{...(instanceQuery.isFetching && {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { InstanceLocal, localAddInstance } from '@utils/slices/instancesSlice'
|
import addInstance from '@utils/slices/instances/add'
|
||||||
|
import { Instance } from '@utils/slices/instancesSlice'
|
||||||
import * as AuthSession from 'expo-auth-session'
|
import * as AuthSession from 'expo-auth-session'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
@ -9,7 +10,7 @@ export interface Props {
|
|||||||
instanceDomain: string
|
instanceDomain: string
|
||||||
// Domain can be different than uri
|
// Domain can be different than uri
|
||||||
instance: Mastodon.Instance
|
instance: Mastodon.Instance
|
||||||
appData: InstanceLocal['appData']
|
appData: Instance['appData']
|
||||||
goBack?: boolean
|
goBack?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,8 +63,8 @@ const InstanceAuth = React.memo(
|
|||||||
)
|
)
|
||||||
queryClient.clear()
|
queryClient.clear()
|
||||||
dispatch(
|
dispatch(
|
||||||
localAddInstance({
|
addInstance({
|
||||||
url: instanceDomain,
|
domain: instanceDomain,
|
||||||
token: accessToken,
|
token: accessToken,
|
||||||
instance,
|
instance,
|
||||||
max_toot_chars: instance.max_toot_chars,
|
max_toot_chars: instance.max_toot_chars,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import ComponentSeparator from '@components/Separator'
|
import ComponentSeparator from '@components/Separator'
|
||||||
import { useNavigation, useScrollToTop } from '@react-navigation/native'
|
import { useNavigation, useScrollToTop } from '@react-navigation/native'
|
||||||
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
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 { findIndex } from 'lodash'
|
import { findIndex } from 'lodash'
|
||||||
@ -54,7 +54,7 @@ const Timeline: React.FC<Props> = ({
|
|||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
// Update timeline when account switched
|
// Update timeline when account switched
|
||||||
useSelector(getLocalActiveIndex)
|
useSelector(getInstanceActive)
|
||||||
|
|
||||||
const queryKeyParams = {
|
const queryKeyParams = {
|
||||||
page,
|
page,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import GracefullyImage from '@components/GracefullyImage'
|
import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
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, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
@ -58,17 +58,16 @@ const TimelineConversation: React.FC<Props> = ({
|
|||||||
queryKey,
|
queryKey,
|
||||||
highlighted = false
|
highlighted = false
|
||||||
}) => {
|
}) => {
|
||||||
const localAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.id === next?.id
|
(prev, next) => prev?.id === next?.id
|
||||||
)
|
)
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const fireMutation = useCallback(() => {
|
const fireMutation = useCallback(() => {
|
||||||
return client<Mastodon.Conversation>({
|
return apiInstance<Mastodon.Conversation>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: `conversations/${conversation.id}/read`
|
url: `conversations/${conversation.id}/read`
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
@ -135,7 +134,9 @@ const TimelineConversation: React.FC<Props> = ({
|
|||||||
statusId={conversation.last_status.id}
|
statusId={conversation.last_status.id}
|
||||||
poll={conversation.last_status.poll}
|
poll={conversation.last_status.poll}
|
||||||
reblog={false}
|
reblog={false}
|
||||||
sameAccount={conversation.last_status.id === localAccount?.id}
|
sameAccount={
|
||||||
|
conversation.last_status.id === instanceAccount?.id
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
@ -10,7 +10,7 @@ import TimelinePoll from '@components/Timeline/Shared/Poll'
|
|||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
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 { uniqBy } from 'lodash'
|
import { uniqBy } from 'lodash'
|
||||||
@ -41,8 +41,8 @@ const TimelineDefault: React.FC<Props> = ({
|
|||||||
pinned
|
pinned
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const localAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.id === next?.id
|
(prev, next) => prev?.id === next?.id
|
||||||
)
|
)
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
@ -118,7 +118,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||||||
statusId={actualStatus.id}
|
statusId={actualStatus.id}
|
||||||
poll={actualStatus.poll}
|
poll={actualStatus.poll}
|
||||||
reblog={item.reblog ? true : false}
|
reblog={item.reblog ? true : false}
|
||||||
sameAccount={actualStatus.account.id === localAccount?.id}
|
sameAccount={actualStatus.account.id === instanceAccount?.id}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{!disableDetails &&
|
{!disableDetails &&
|
||||||
@ -147,7 +147,7 @@ const TimelineDefault: React.FC<Props> = ({
|
|||||||
([actualStatus.account] as Mastodon.Account[] &
|
([actualStatus.account] as Mastodon.Account[] &
|
||||||
Mastodon.Mention[])
|
Mastodon.Mention[])
|
||||||
.concat(actualStatus.mentions)
|
.concat(actualStatus.mentions)
|
||||||
.filter(d => d.id !== localAccount?.id),
|
.filter(d => d.id !== instanceAccount?.id),
|
||||||
d => d.id
|
d => d.id
|
||||||
).map(d => d.acct)}
|
).map(d => d.acct)}
|
||||||
reblog={item.reblog ? true : false}
|
reblog={item.reblog ? true : false}
|
||||||
|
@ -10,7 +10,7 @@ import TimelinePoll from '@components/Timeline/Shared/Poll'
|
|||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
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 { uniqBy } from 'lodash'
|
import { uniqBy } from 'lodash'
|
||||||
@ -30,8 +30,8 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||||||
highlighted = false
|
highlighted = false
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const localAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.id === next?.id
|
(prev, next) => prev?.id === next?.id
|
||||||
)
|
)
|
||||||
const navigation = useNavigation<
|
const navigation = useNavigation<
|
||||||
@ -103,7 +103,7 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||||||
statusId={notification.status.id}
|
statusId={notification.status.id}
|
||||||
poll={notification.status.poll}
|
poll={notification.status.poll}
|
||||||
reblog={false}
|
reblog={false}
|
||||||
sameAccount={notification.account.id === localAccount?.id}
|
sameAccount={notification.account.id === instanceAccount?.id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{notification.status.media_attachments.length > 0 && (
|
{notification.status.media_attachments.length > 0 && (
|
||||||
@ -131,7 +131,7 @@ const TimelineNotifications: React.FC<Props> = ({
|
|||||||
([notification.status.account] as Mastodon.Account[] &
|
([notification.status.account] as Mastodon.Account[] &
|
||||||
Mastodon.Mention[])
|
Mastodon.Mention[])
|
||||||
.concat(notification.status.mentions)
|
.concat(notification.status.mentions)
|
||||||
.filter(d => d.id !== localAccount?.id),
|
.filter(d => d.id !== instanceAccount?.id),
|
||||||
d => d.id
|
d => d.id
|
||||||
).map(d => d.acct)}
|
).map(d => d.acct)}
|
||||||
reblog={false}
|
reblog={false}
|
||||||
|
@ -3,10 +3,7 @@ export default {
|
|||||||
textInput: { placeholder: "Instance' domain" },
|
textInput: { placeholder: "Instance' domain" },
|
||||||
privateInstance: 'Private instance, peeping not allowed',
|
privateInstance: 'Private instance, peeping not allowed',
|
||||||
EULA: { base: 'I have read and agreed to ', EULA: 'EULA' },
|
EULA: { base: 'I have read and agreed to ', EULA: 'EULA' },
|
||||||
button: {
|
button: 'Login',
|
||||||
local: 'Login',
|
|
||||||
remote: 'Peep'
|
|
||||||
},
|
|
||||||
information: {
|
information: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
description: { heading: 'Description', expandHint: 'description' },
|
description: { heading: 'Description', expandHint: 'description' },
|
||||||
@ -21,7 +18,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
local: {
|
|
||||||
alert: {
|
alert: {
|
||||||
title: 'Logged in to this instance',
|
title: 'Logged in to this instance',
|
||||||
message:
|
message:
|
||||||
@ -31,9 +27,5 @@ export default {
|
|||||||
continue: 'Continue'
|
continue: 'Continue'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
remote: {
|
|
||||||
succeed: 'Register peeping succeed'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,6 @@ export default {
|
|||||||
cancel: '$t(common:buttons.cancel)'
|
cancel: '$t(common:buttons.cancel)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remote: {
|
|
||||||
heading: '$t(meSettingsUpdateRemote:heading)',
|
|
||||||
description: 'External instance can only be read'
|
|
||||||
},
|
|
||||||
cache: {
|
cache: {
|
||||||
heading: 'Clear cache',
|
heading: 'Clear cache',
|
||||||
empty: 'Cache empty'
|
empty: 'Cache empty'
|
||||||
|
@ -23,8 +23,7 @@ i18n.use(initReactI18next).init({
|
|||||||
},
|
},
|
||||||
react: {
|
react: {
|
||||||
useSuspense: false
|
useSuspense: false
|
||||||
},
|
}
|
||||||
debug: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default i18n
|
export default i18n
|
||||||
|
@ -14,6 +14,7 @@ export default {
|
|||||||
meLists: require('./screens/meLists').default,
|
meLists: require('./screens/meLists').default,
|
||||||
meListsList: require('./screens/meListsList').default,
|
meListsList: require('./screens/meListsList').default,
|
||||||
meSettings: require('./screens/meSettings').default,
|
meSettings: require('./screens/meSettings').default,
|
||||||
|
meSettingsNotification: require('./screens/meSettingsNotification').default,
|
||||||
meSwitch: require('./screens/meSwitch').default,
|
meSwitch: require('./screens/meSwitch').default,
|
||||||
|
|
||||||
sharedAccount: require('./screens/sharedAccount').default,
|
sharedAccount: require('./screens/sharedAccount').default,
|
||||||
|
@ -3,10 +3,7 @@ export default {
|
|||||||
textInput: { placeholder: '输入社区服务器地址' },
|
textInput: { placeholder: '输入社区服务器地址' },
|
||||||
privateInstance: '非公开社区, 不能围观',
|
privateInstance: '非公开社区, 不能围观',
|
||||||
EULA: { base: '我阅读并同意 ', EULA: '最终用户条款' },
|
EULA: { base: '我阅读并同意 ', EULA: '最终用户条款' },
|
||||||
button: {
|
button: '登录',
|
||||||
local: '登录',
|
|
||||||
remote: '围观'
|
|
||||||
},
|
|
||||||
information: {
|
information: {
|
||||||
name: '社区名称',
|
name: '社区名称',
|
||||||
description: { heading: '社区简介', expandHint: '简介' },
|
description: { heading: '社区简介', expandHint: '简介' },
|
||||||
@ -21,7 +18,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
local: {
|
|
||||||
alert: {
|
alert: {
|
||||||
title: '此社区已登录',
|
title: '此社区已登录',
|
||||||
message: '你可以登录同个社区的另一个账号,不影响已登录的账号',
|
message: '你可以登录同个社区的另一个账号,不影响已登录的账号',
|
||||||
@ -30,9 +26,5 @@ export default {
|
|||||||
continue: '继续'
|
continue: '继续'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
remote: {
|
|
||||||
succeed: '围观登记成功'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
export default {
|
export default {
|
||||||
heading: '设置',
|
heading: '设置',
|
||||||
content: {
|
content: {
|
||||||
|
notification: {
|
||||||
|
heading: '$t(meSettingsNotification:heading)'
|
||||||
|
},
|
||||||
language: {
|
language: {
|
||||||
heading: '切换语言',
|
heading: '切换语言',
|
||||||
options: {
|
options: {
|
||||||
@ -26,10 +29,6 @@ export default {
|
|||||||
cancel: '$t(common:buttons.cancel)'
|
cancel: '$t(common:buttons.cancel)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remote: {
|
|
||||||
heading: '$t(meSettingsUpdateRemote:heading)',
|
|
||||||
description: '外站只能浏览不能玩'
|
|
||||||
},
|
|
||||||
cache: {
|
cache: {
|
||||||
heading: '清空缓存',
|
heading: '清空缓存',
|
||||||
empty: '暂无缓存'
|
empty: '暂无缓存'
|
||||||
|
24
src/i18n/zh-Hans/screens/meSettingsNotification.ts
Normal file
24
src/i18n/zh-Hans/screens/meSettingsNotification.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
export default {
|
||||||
|
heading: '通知',
|
||||||
|
content: {
|
||||||
|
global: {
|
||||||
|
heading: '启用通知',
|
||||||
|
description: 'blahblahblah'
|
||||||
|
},
|
||||||
|
follow: {
|
||||||
|
heading: '新关注者'
|
||||||
|
},
|
||||||
|
favourite: {
|
||||||
|
heading: '嘟文被喜欢'
|
||||||
|
},
|
||||||
|
reblog: {
|
||||||
|
heading: '嘟文被转嘟'
|
||||||
|
},
|
||||||
|
mention: {
|
||||||
|
heading: '提及你'
|
||||||
|
},
|
||||||
|
poll: {
|
||||||
|
heading: '投票'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,10 @@ import ComposeRoot from '@screens/Compose/Root'
|
|||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
||||||
import {
|
import {
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
getLocalMaxTootChar,
|
getInstanceMaxTootChar,
|
||||||
removeLocalDraft,
|
removeInstanceDraft,
|
||||||
updateLocalDraft
|
updateInstanceDraft
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
@ -75,7 +75,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
|
|||||||
setHasKeyboard(false)
|
setHasKeyboard(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const localAccount = useSelector(getLocalAccount, (prev, next) =>
|
const localAccount = useSelector(getInstanceAccount, (prev, next) =>
|
||||||
prev?.preferences && next?.preferences
|
prev?.preferences && next?.preferences
|
||||||
? prev?.preferences['posting:default:visibility'] ===
|
? prev?.preferences['posting:default:visibility'] ===
|
||||||
next?.preferences['posting:default:visibility']
|
next?.preferences['posting:default:visibility']
|
||||||
@ -102,7 +102,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
|
|||||||
initialReducerState
|
initialReducerState
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxTootChars = useSelector(getLocalMaxTootChar)
|
const maxTootChars = useSelector(getInstanceMaxTootChar, () => true)
|
||||||
const totalTextCount =
|
const totalTextCount =
|
||||||
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
|
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
|
||||||
composeState.text.count
|
composeState.text.count
|
||||||
@ -158,7 +158,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
|
|||||||
|
|
||||||
const saveDraft = () => {
|
const saveDraft = () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateLocalDraft({
|
updateInstanceDraft({
|
||||||
timestamp: composeState.timestamp,
|
timestamp: composeState.timestamp,
|
||||||
spoiler: composeState.spoiler.raw,
|
spoiler: composeState.spoiler.raw,
|
||||||
text: composeState.text.raw,
|
text: composeState.text.raw,
|
||||||
@ -171,7 +171,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
const removeDraft = useCallback(() => {
|
const removeDraft = useCallback(() => {
|
||||||
dispatch(removeLocalDraft(composeState.timestamp))
|
dispatch(removeInstanceDraft(composeState.timestamp))
|
||||||
}, [composeState.timestamp])
|
}, [composeState.timestamp])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const autoSave = composeState.dirty
|
const autoSave = composeState.dirty
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import ComponentSeparator from '@components/Separator'
|
import ComponentSeparator from '@components/Separator'
|
||||||
import HeaderSharedCreated from '@components/Timeline/Shared/HeaderShared/Created'
|
import HeaderSharedCreated from '@components/Timeline/Shared/HeaderShared/Created'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { getLocalDrafts, removeLocalDraft } from '@utils/slices/instancesSlice'
|
import {
|
||||||
|
getInstanceDrafts,
|
||||||
|
removeInstanceDraft
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
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, { useCallback, useContext, useState } from 'react'
|
import React, { useCallback, useContext, useState } from 'react'
|
||||||
@ -34,7 +37,7 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
|||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const { mode, theme } = useTheme()
|
const { mode, theme } = useTheme()
|
||||||
const localDrafts = useSelector(getLocalDrafts)?.filter(
|
const instanceDrafts = useSelector(getInstanceDrafts)?.filter(
|
||||||
draft => draft.timestamp !== timestamp
|
draft => draft.timestamp !== timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,7 +47,7 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
|||||||
const [checkingAttachments, setCheckingAttachments] = useState(false)
|
const [checkingAttachments, setCheckingAttachments] = useState(false)
|
||||||
|
|
||||||
const removeDraft = useCallback(ts => {
|
const removeDraft = useCallback(ts => {
|
||||||
dispatch(removeLocalDraft(ts))
|
dispatch(removeInstanceDraft(ts))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
@ -58,9 +61,8 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
|||||||
let tempUploads: ExtendedAttachment[] = []
|
let tempUploads: ExtendedAttachment[] = []
|
||||||
if (item.attachments && item.attachments.uploads.length) {
|
if (item.attachments && item.attachments.uploads.length) {
|
||||||
for (const attachment of item.attachments.uploads) {
|
for (const attachment of item.attachments.uploads) {
|
||||||
await client<Mastodon.Attachment>({
|
await apiInstance<Mastodon.Attachment>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `media/${attachment.remote?.id}`
|
url: `media/${attachment.remote?.id}`
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
@ -92,7 +94,7 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
|||||||
type: 'loadDraft',
|
type: 'loadDraft',
|
||||||
payload: tempDraft
|
payload: tempDraft
|
||||||
})
|
})
|
||||||
dispatch(removeLocalDraft(item.timestamp))
|
dispatch(removeInstanceDraft(item.timestamp))
|
||||||
navigation.goBack()
|
navigation.goBack()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -156,14 +158,14 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
|||||||
<>
|
<>
|
||||||
<PanGestureHandler enabled={true}>
|
<PanGestureHandler enabled={true}>
|
||||||
<SwipeListView
|
<SwipeListView
|
||||||
data={localDrafts}
|
data={instanceDrafts}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
renderHiddenItem={renderHiddenItem}
|
renderHiddenItem={renderHiddenItem}
|
||||||
disableRightSwipe={true}
|
disableRightSwipe={true}
|
||||||
rightOpenValue={-actionWidth}
|
rightOpenValue={-actionWidth}
|
||||||
previewRowKey={
|
previewRowKey={
|
||||||
localDrafts?.length
|
instanceDrafts?.length
|
||||||
? localDrafts[0].timestamp.toString()
|
? instanceDrafts[0].timestamp.toString()
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
// previewDuration={350}
|
// previewDuration={350}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import haptics from '@components/haptics'
|
import haptics from '@components/haptics'
|
||||||
import { HeaderCenter, HeaderLeft, HeaderRight } from '@components/Header'
|
import { HeaderCenter, HeaderLeft, HeaderRight } from '@components/Header'
|
||||||
@ -91,9 +91,8 @@ const ComposeEditAttachment: React.FC<ScreenComposeEditAttachmentProp> = ({
|
|||||||
formData.append('focus', `${focus.value.x},${focus.value.y}`)
|
formData.append('focus', `${focus.value.x},${focus.value.y}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
client<Mastodon.Attachment>({
|
apiInstance<Mastodon.Attachment>({
|
||||||
method: 'put',
|
method: 'put',
|
||||||
instance: 'local',
|
|
||||||
url: `media/${theAttachment.id}`,
|
url: `media/${theAttachment.id}`,
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Button from '@components/Button'
|
import Button from '@components/Button'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { getLocalDrafts } from '@utils/slices/instancesSlice'
|
import { getInstanceDrafts } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||||
import React, { useContext, useEffect } from 'react'
|
import React, { useContext, useEffect } from 'react'
|
||||||
@ -13,7 +13,7 @@ const ComposeDrafts: React.FC = () => {
|
|||||||
const { t } = useTranslation('sharedCompose')
|
const { t } = useTranslation('sharedCompose')
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const { composeState } = useContext(ComposeContext)
|
const { composeState } = useContext(ComposeContext)
|
||||||
const localDrafts = useSelector(getLocalDrafts)?.filter(
|
const instanceDrafts = useSelector(getInstanceDrafts)?.filter(
|
||||||
draft => draft.timestamp !== composeState.timestamp
|
draft => draft.timestamp !== composeState.timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ const ComposeDrafts: React.FC = () => {
|
|||||||
layoutAnimation()
|
layoutAnimation()
|
||||||
}, [composeState.dirty])
|
}, [composeState.dirty])
|
||||||
|
|
||||||
if (!composeState.dirty && localDrafts?.length) {
|
if (!composeState.dirty && instanceDrafts?.length) {
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={styles.base}
|
style={styles.base}
|
||||||
@ -29,7 +29,7 @@ const ComposeDrafts: React.FC = () => {
|
|||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
content={t('content.root.drafts', {
|
content={t('content.root.drafts', {
|
||||||
count: localDrafts.length
|
count: instanceDrafts.length
|
||||||
})}
|
})}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
navigation.navigate('Screen-Compose-DraftsList', {
|
navigation.navigate('Screen-Compose-DraftsList', {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import client from '@api/client'
|
|
||||||
import * as ImagePicker from 'expo-image-picker'
|
import * as ImagePicker from 'expo-image-picker'
|
||||||
import * as Crypto from 'expo-crypto'
|
import * as Crypto from 'expo-crypto'
|
||||||
import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types'
|
import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types'
|
||||||
@ -9,6 +8,7 @@ import { ComposeAction } from '../../utils/types'
|
|||||||
import { ActionSheetOptions } from '@expo/react-native-action-sheet'
|
import { ActionSheetOptions } from '@expo/react-native-action-sheet'
|
||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
|
import apiInstance from '@api/instance'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
composeDispatch: Dispatch<ComposeAction>
|
composeDispatch: Dispatch<ComposeAction>
|
||||||
@ -106,9 +106,8 @@ const addAttachment = async ({
|
|||||||
type: attachmentType
|
type: attachmentType
|
||||||
})
|
})
|
||||||
|
|
||||||
return client<Mastodon.Attachment>({
|
return apiInstance<Mastodon.Attachment>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: 'media',
|
url: 'media',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import {
|
import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice'
|
||||||
getLocalActiveIndex,
|
|
||||||
getLocalInstances
|
|
||||||
} from '@utils/slices/instancesSlice'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React, { useContext } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
@ -13,15 +10,15 @@ import ComposeTextInput from './Header/TextInput'
|
|||||||
|
|
||||||
const ComposeRootHeader: React.FC = () => {
|
const ComposeRootHeader: React.FC = () => {
|
||||||
const { composeState } = useContext(ComposeContext)
|
const { composeState } = useContext(ComposeContext)
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const localInstances = useSelector(
|
const localInstances = useSelector(
|
||||||
getLocalInstances,
|
getInstances,
|
||||||
(prev, next) => prev.length === next.length
|
(prev, next) => prev.length === next.length
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{localActiveIndex !== null &&
|
{instanceActive !== -1 &&
|
||||||
localInstances.length &&
|
localInstances.length &&
|
||||||
localInstances.length > 1 && (
|
localInstances.length > 1 && (
|
||||||
<View style={styles.postingAs}>
|
<View style={styles.postingAs}>
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { getLocalAccount, getLocalUri } from '@utils/slices/instancesSlice'
|
import {
|
||||||
|
getInstanceAccount,
|
||||||
|
getInstanceUri
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
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,17 +14,17 @@ const ComposePostingAs = React.memo(
|
|||||||
const { t } = useTranslation('sharedCompose')
|
const { t } = useTranslation('sharedCompose')
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const localAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.acct === next?.acct
|
(prev, next) => prev?.acct === next?.acct
|
||||||
)
|
)
|
||||||
const localUri = useSelector(getLocalUri)
|
const instanceUri = useSelector(getInstanceUri)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style={[styles.text, { color: theme.secondary }]}>
|
<Text style={[styles.text, { color: theme.secondary }]}>
|
||||||
{t('content.root.header.postingAs', {
|
{t('content.root.header.postingAs', {
|
||||||
acct: localAccount?.acct,
|
acct: instanceAccount?.acct,
|
||||||
domain: localUri
|
domain: instanceUri
|
||||||
})}
|
})}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { store } from '@root/store'
|
import { store } from '@root/store'
|
||||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import composeInitialState from './initialState'
|
import composeInitialState from './initialState'
|
||||||
import { ComposeState } from './types'
|
import { ComposeState } from './types'
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ const composeParseState = (
|
|||||||
}),
|
}),
|
||||||
visibility:
|
visibility:
|
||||||
params.incomingStatus.visibility ||
|
params.incomingStatus.visibility ||
|
||||||
getLocalAccount(store.getState())?.preferences[
|
getInstanceAccount(store.getState()).preferences[
|
||||||
'posting:default:visibility'
|
'posting:default:visibility'
|
||||||
] ||
|
] ||
|
||||||
'public',
|
'public',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@root/api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { ComposeState } from '@screens/Compose/utils/types'
|
import { ComposeState } from '@screens/Compose/utils/types'
|
||||||
import * as Crypto from 'expo-crypto'
|
import * as Crypto from 'expo-crypto'
|
||||||
|
|
||||||
@ -39,9 +39,8 @@ const composePost = async (
|
|||||||
|
|
||||||
formData.append('visibility', composeState.visibility)
|
formData.append('visibility', composeState.visibility)
|
||||||
|
|
||||||
return client<Mastodon.Status>({
|
return apiInstance<Mastodon.Status>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: 'statuses',
|
url: 'statuses',
|
||||||
headers: {
|
headers: {
|
||||||
'Idempotency-Key': await Crypto.digestStringAsync(
|
'Idempotency-Key': await Crypto.digestStringAsync(
|
||||||
|
@ -10,10 +10,10 @@ import { StackScreenProps } from '@react-navigation/stack'
|
|||||||
import { useTimelineQuery } from '@utils/queryHooks/timeline'
|
import { useTimelineQuery } from '@utils/queryHooks/timeline'
|
||||||
import { getPreviousTab } from '@utils/slices/contextsSlice'
|
import { getPreviousTab } from '@utils/slices/contextsSlice'
|
||||||
import {
|
import {
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
getLocalActiveIndex,
|
getInstanceActive,
|
||||||
getLocalNotification,
|
getInstanceNotification,
|
||||||
updateLocalNotification
|
updateInstanceNotification
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
@ -44,14 +44,15 @@ const ScreenTabs = React.memo(
|
|||||||
({ navigation }: ScreenTabsProp) => {
|
({ navigation }: ScreenTabsProp) => {
|
||||||
const { mode, theme } = useTheme()
|
const { mode, theme } = useTheme()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const localAccount = useSelector(
|
const localAccount = useSelector(
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.avatarStatic === next?.avatarStatic
|
(prev, next) => prev?.avatarStatic === next?.avatarStatic
|
||||||
)
|
)
|
||||||
|
|
||||||
const screenOptions = useCallback(
|
const screenOptions = useCallback(
|
||||||
({ route }): BottomTabNavigationOptions => ({
|
({ route }): BottomTabNavigationOptions => ({
|
||||||
|
tabBarVisible: instanceActive !== -1,
|
||||||
tabBarIcon: ({
|
tabBarIcon: ({
|
||||||
focused,
|
focused,
|
||||||
color,
|
color,
|
||||||
@ -71,7 +72,7 @@ const ScreenTabs = React.memo(
|
|||||||
case 'Tab-Notifications':
|
case 'Tab-Notifications':
|
||||||
return <Icon name='Bell' size={size} color={color} />
|
return <Icon name='Bell' size={size} color={color} />
|
||||||
case 'Tab-Me':
|
case 'Tab-Me':
|
||||||
return localActiveIndex !== null ? (
|
return instanceActive !== -1 ? (
|
||||||
<FastImage
|
<FastImage
|
||||||
source={{ uri: localAccount?.avatarStatic }}
|
source={{ uri: localAccount?.avatarStatic }}
|
||||||
style={{
|
style={{
|
||||||
@ -94,61 +95,39 @@ const ScreenTabs = React.memo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
[localActiveIndex, localAccount?.avatarStatic]
|
[instanceActive, localAccount?.avatarStatic]
|
||||||
)
|
)
|
||||||
const tabBarOptions = useMemo(
|
const tabBarOptions = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
activeTintColor: theme.primary,
|
activeTintColor: theme.primary,
|
||||||
inactiveTintColor:
|
inactiveTintColor: theme.secondary,
|
||||||
localActiveIndex !== null ? theme.secondary : theme.disabled,
|
|
||||||
showLabel: false,
|
showLabel: false,
|
||||||
...(Platform.OS === 'android' && { keyboardHidesTabBar: true })
|
...(Platform.OS === 'android' && { keyboardHidesTabBar: true })
|
||||||
}),
|
}),
|
||||||
[mode, localActiveIndex]
|
[mode]
|
||||||
)
|
|
||||||
const localListeners = useCallback(
|
|
||||||
() => ({
|
|
||||||
tabPress: (e: any) => {
|
|
||||||
if (!(localActiveIndex !== null)) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
[localActiveIndex]
|
|
||||||
)
|
)
|
||||||
const composeListeners = useMemo(
|
const composeListeners = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
tabPress: (e: any) => {
|
tabPress: (e: any) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (localActiveIndex !== null) {
|
|
||||||
haptics('Light')
|
haptics('Light')
|
||||||
navigation.navigate('Screen-Compose')
|
navigation.navigate('Screen-Compose')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
[localActiveIndex]
|
[]
|
||||||
)
|
)
|
||||||
const composeComponent = useCallback(() => null, [])
|
const composeComponent = useCallback(() => null, [])
|
||||||
const notificationsListeners = useCallback(
|
|
||||||
() => ({
|
|
||||||
tabPress: (e: any) => {
|
|
||||||
if (!(localActiveIndex !== null)) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
[localActiveIndex]
|
|
||||||
)
|
|
||||||
|
|
||||||
// On launch check if there is any unread noficiations
|
// On launch check if there is any unread noficiations
|
||||||
useTimelineQuery({
|
useTimelineQuery({
|
||||||
page: 'Notifications',
|
page: 'Notifications',
|
||||||
options: {
|
options: {
|
||||||
|
enabled: instanceActive !== -1,
|
||||||
notifyOnChangeProps: [],
|
notifyOnChangeProps: [],
|
||||||
select: data => {
|
select: data => {
|
||||||
if (data.pages[0].body.length) {
|
if (data.pages[0].body.length) {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateLocalNotification({
|
updateInstanceNotification({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
latestTime: data.pages[0].body[0].created_at
|
latestTime: data.pages[0].body[0].created_at
|
||||||
})
|
})
|
||||||
@ -160,27 +139,21 @@ const ScreenTabs = React.memo(
|
|||||||
})
|
})
|
||||||
useWebsocket({ stream: 'user', event: 'notification' })
|
useWebsocket({ stream: 'user', event: 'notification' })
|
||||||
const localNotification = useSelector(
|
const localNotification = useSelector(
|
||||||
getLocalNotification,
|
getInstanceNotification,
|
||||||
(prev, next) =>
|
(prev, next) =>
|
||||||
prev?.readTime === next?.readTime &&
|
prev?.readTime === next?.readTime &&
|
||||||
prev?.latestTime === next?.latestTime
|
prev?.latestTime === next?.latestTime
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const previousTab = useSelector(getPreviousTab, () => true)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tab.Navigator
|
<Tab.Navigator
|
||||||
initialRouteName={
|
initialRouteName={instanceActive !== -1 ? previousTab : 'Tab-Me'}
|
||||||
localActiveIndex !== null
|
|
||||||
? useSelector(getPreviousTab, () => true)
|
|
||||||
: 'Tab-Me'
|
|
||||||
}
|
|
||||||
screenOptions={screenOptions}
|
screenOptions={screenOptions}
|
||||||
tabBarOptions={tabBarOptions}
|
tabBarOptions={tabBarOptions}
|
||||||
>
|
>
|
||||||
<Tab.Screen
|
<Tab.Screen name='Tab-Local' component={TabLocal} />
|
||||||
name='Tab-Local'
|
|
||||||
component={TabLocal}
|
|
||||||
listeners={localListeners}
|
|
||||||
/>
|
|
||||||
<Tab.Screen name='Tab-Public' component={TabPublic} />
|
<Tab.Screen name='Tab-Public' component={TabPublic} />
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name='Tab-Compose'
|
name='Tab-Compose'
|
||||||
@ -190,7 +163,6 @@ const ScreenTabs = React.memo(
|
|||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name='Tab-Notifications'
|
name='Tab-Notifications'
|
||||||
component={TabNotifications}
|
component={TabNotifications}
|
||||||
listeners={notificationsListeners}
|
|
||||||
options={{
|
options={{
|
||||||
tabBarBadge: localNotification?.latestTime
|
tabBarBadge: localNotification?.latestTime
|
||||||
? !localNotification.readTime ||
|
? !localNotification.readTime ||
|
||||||
|
@ -3,7 +3,7 @@ import { HeaderCenter, HeaderRight } from '@components/Header'
|
|||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs'
|
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs'
|
||||||
import { ScreenTabsParamList } from '@screens/Tabs'
|
import { ScreenTabsParamList } from '@screens/Tabs'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
@ -21,7 +21,7 @@ const Stack = createNativeStackNavigator<Nav.TabLocalStackParamList>()
|
|||||||
const TabLocal = React.memo(
|
const TabLocal = React.memo(
|
||||||
({ navigation }: TabLocalProp) => {
|
({ navigation }: TabLocalProp) => {
|
||||||
const { t } = useTranslation('local')
|
const { t } = useTranslation('local')
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
|
|
||||||
const screenOptions = useMemo(
|
const screenOptions = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@ -49,7 +49,7 @@ const TabLocal = React.memo(
|
|||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
const children = useCallback(
|
const children = useCallback(
|
||||||
() => (localActiveIndex !== null ? <Timeline page='Following' /> : null),
|
() => (instanceActive !== -1 ? <Timeline page='Following' /> : null),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import React from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
|
import ScreenMeSettingsNotification from './Me/Notification'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<Nav.TabMeStackParamList>()
|
const Stack = createNativeStackNavigator<Nav.TabMeStackParamList>()
|
||||||
|
|
||||||
@ -114,6 +115,19 @@ const TabMe = React.memo(
|
|||||||
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Tab-Me-Settings-Notification'
|
||||||
|
component={ScreenMeSettingsNotification}
|
||||||
|
options={({ navigation }: any) => ({
|
||||||
|
headerTitle: t('meSettingsNotification:heading'),
|
||||||
|
...(Platform.OS === 'android' && {
|
||||||
|
headerCenter: () => (
|
||||||
|
<HeaderCenter content={t('meSettingsNotification:heading')} />
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='Tab-Me-Switch'
|
name='Tab-Me-Switch'
|
||||||
component={ScreenMeSwitch}
|
component={ScreenMeSwitch}
|
||||||
|
37
src/screens/Tabs/Me/Notification.tsx
Normal file
37
src/screens/Tabs/Me/Notification.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
|
import { usePushQuery } from '@utils/queryHooks/push'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { ScrollView } from 'react-native-gesture-handler'
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
|
|
||||||
|
const ScreenMeSettingsNotification: React.FC = () => {
|
||||||
|
const { t } = useTranslation('meSettingsNotification')
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
|
const { data, isLoading } = usePushQuery({})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView>
|
||||||
|
<MenuContainer>
|
||||||
|
<MenuRow
|
||||||
|
title={t('content.global.heading')}
|
||||||
|
description={t('content.global.description')}
|
||||||
|
// switchValue={notification.enabled}
|
||||||
|
// switchOnValueChange={() => dispatch(updateNotification(true))}
|
||||||
|
/>
|
||||||
|
</MenuContainer>
|
||||||
|
<MenuContainer>
|
||||||
|
<MenuRow
|
||||||
|
title={t('content.follow.heading')}
|
||||||
|
loading={isLoading}
|
||||||
|
// switchDisabled={!notification.enabled}
|
||||||
|
// switchValue={notification.enabled ? data?.alerts.follow : false}
|
||||||
|
// switchOnValueChange={() => dispatch(updateNotification(true))}
|
||||||
|
/>
|
||||||
|
</MenuContainer>
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ScreenMeSettingsNotification
|
@ -8,7 +8,7 @@ import AccountNav from '@screens/Tabs/Shared/Account/Nav'
|
|||||||
import AccountContext from '@screens/Tabs/Shared/Account/utils/createContext'
|
import AccountContext from '@screens/Tabs/Shared/Account/utils/createContext'
|
||||||
import accountInitialState from '@screens/Tabs/Shared/Account/utils/initialState'
|
import accountInitialState from '@screens/Tabs/Shared/Account/utils/initialState'
|
||||||
import accountReducer from '@screens/Tabs/Shared/Account/utils/reducer'
|
import accountReducer from '@screens/Tabs/Shared/Account/utils/reducer'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import React, { useReducer, useRef, useState } from 'react'
|
import React, { useReducer, useRef, useState } from 'react'
|
||||||
import Animated, {
|
import Animated, {
|
||||||
useAnimatedScrollHandler,
|
useAnimatedScrollHandler,
|
||||||
@ -17,7 +17,7 @@ import Animated, {
|
|||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const ScreenMeRoot: React.FC = () => {
|
const ScreenMeRoot: React.FC = () => {
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
|
|
||||||
const scrollRef = useRef<Animated.ScrollView>(null)
|
const scrollRef = useRef<Animated.ScrollView>(null)
|
||||||
useScrollToTop(scrollRef)
|
useScrollToTop(scrollRef)
|
||||||
@ -36,7 +36,7 @@ const ScreenMeRoot: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AccountContext.Provider value={{ accountState, accountDispatch }}>
|
<AccountContext.Provider value={{ accountState, accountDispatch }}>
|
||||||
{localActiveIndex !== null && data ? (
|
{instanceActive !== -1 && data ? (
|
||||||
<AccountNav scrollY={scrollY} account={data} />
|
<AccountNav scrollY={scrollY} account={data} />
|
||||||
) : null}
|
) : null}
|
||||||
<Animated.ScrollView
|
<Animated.ScrollView
|
||||||
@ -45,14 +45,14 @@ const ScreenMeRoot: React.FC = () => {
|
|||||||
onScroll={onScroll}
|
onScroll={onScroll}
|
||||||
scrollEventThrottle={16}
|
scrollEventThrottle={16}
|
||||||
>
|
>
|
||||||
{localActiveIndex !== null ? (
|
{instanceActive !== -1 ? (
|
||||||
<MyInfo setData={setData} />
|
<MyInfo setData={setData} />
|
||||||
) : (
|
) : (
|
||||||
<ComponentInstance />
|
<ComponentInstance />
|
||||||
)}
|
)}
|
||||||
{localActiveIndex !== null ? <Collections /> : null}
|
{instanceActive !== -1 ? <Collections /> : null}
|
||||||
<Settings />
|
<Settings />
|
||||||
{localActiveIndex !== null ? <Logout /> : null}
|
{instanceActive !== -1 ? <Logout /> : null}
|
||||||
</Animated.ScrollView>
|
</Animated.ScrollView>
|
||||||
</AccountContext.Provider>
|
</AccountContext.Provider>
|
||||||
)
|
)
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
import Button from '@components/Button'
|
import Button from '@components/Button'
|
||||||
import haptics from '@root/components/haptics'
|
import haptics from '@root/components/haptics'
|
||||||
import { localRemoveInstance } from '@utils/slices/instancesSlice'
|
import removeInstance from '@utils/slices/instances/remove'
|
||||||
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Alert } from 'react-native'
|
import { Alert } from 'react-native'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { useDispatch } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
const Logout: React.FC = () => {
|
const Logout: React.FC = () => {
|
||||||
const { t } = useTranslation('meRoot')
|
const { t } = useTranslation('meRoot')
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
@ -33,7 +35,7 @@ const Logout: React.FC = () => {
|
|||||||
onPress: () => {
|
onPress: () => {
|
||||||
haptics('Success')
|
haptics('Success')
|
||||||
queryClient.clear()
|
queryClient.clear()
|
||||||
dispatch(localRemoveInstance())
|
dispatch(removeInstance(instanceActive))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import AccountHeader from '@screens/Tabs/Shared/Account/Header'
|
import AccountHeader from '@screens/Tabs/Shared/Account/Header'
|
||||||
import AccountInformation from '@screens/Tabs/Shared/Account/Information'
|
import AccountInformation from '@screens/Tabs/Shared/Account/Information'
|
||||||
import { useAccountQuery } from '@utils/queryHooks/account'
|
import { useAccountQuery } from '@utils/queryHooks/account'
|
||||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
@ -10,11 +10,11 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MyInfo: React.FC<Props> = ({ setData }) => {
|
const MyInfo: React.FC<Props> = ({ setData }) => {
|
||||||
const localAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.id === next?.id
|
(prev, next) => prev?.id === next?.id
|
||||||
)
|
)
|
||||||
const { data } = useAccountQuery({ id: localAccount!.id })
|
const { data } = useAccountQuery({ id: instanceAccount!.id })
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { getLocalUrl } from '@utils/slices/instancesSlice'
|
|
||||||
import * as WebBrowser from 'expo-web-browser'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useSelector } from 'react-redux'
|
|
||||||
|
|
||||||
const Settings: React.FC = () => {
|
const Settings: React.FC = () => {
|
||||||
const { t } = useTranslation('meRoot')
|
const { t } = useTranslation('meRoot')
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
|
|
||||||
const localUrl = useSelector(getLocalUrl)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuContainer>
|
<MenuContainer>
|
||||||
{/* <MenuRow
|
{/* <MenuRow
|
||||||
|
@ -2,7 +2,9 @@ import analytics from '@components/analytics'
|
|||||||
import haptics from '@components/haptics'
|
import haptics from '@components/haptics'
|
||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import i18n from '@root/i18n/i18n'
|
import i18n from '@root/i18n/i18n'
|
||||||
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import {
|
import {
|
||||||
changeBrowser,
|
changeBrowser,
|
||||||
changeLanguage,
|
changeLanguage,
|
||||||
@ -17,17 +19,28 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
const SettingsApp: React.FC = () => {
|
const SettingsApp: React.FC = () => {
|
||||||
|
const navigation = useNavigation()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const { showActionSheetWithOptions } = useActionSheet()
|
const { showActionSheetWithOptions } = useActionSheet()
|
||||||
const { setTheme } = useTheme()
|
const { setTheme } = useTheme()
|
||||||
const { t } = useTranslation('meSettings')
|
const { t } = useTranslation('meSettings')
|
||||||
|
|
||||||
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const settingsLanguage = useSelector(getSettingsLanguage)
|
const settingsLanguage = useSelector(getSettingsLanguage)
|
||||||
const settingsTheme = useSelector(getSettingsTheme)
|
const settingsTheme = useSelector(getSettingsTheme)
|
||||||
const settingsBrowser = useSelector(getSettingsBrowser)
|
const settingsBrowser = useSelector(getSettingsBrowser)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuContainer>
|
<MenuContainer>
|
||||||
|
{instanceActive !== -1 ? (
|
||||||
|
<MenuRow
|
||||||
|
title={t('content.notification.heading')}
|
||||||
|
iconBack='ChevronRight'
|
||||||
|
onPress={() => {
|
||||||
|
navigation.navigate('Tab-Me-Settings-Notification')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
<MenuRow
|
<MenuRow
|
||||||
title={t('content.language.heading')}
|
title={t('content.language.heading')}
|
||||||
content={t(`content.language.options.${settingsLanguage}`)}
|
content={t(`content.language.options.${settingsLanguage}`)}
|
||||||
|
@ -2,39 +2,36 @@ import Button from '@components/Button'
|
|||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||||
import { persistor } from '@root/store'
|
import { persistor } from '@root/store'
|
||||||
import {
|
import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice'
|
||||||
getLocalActiveIndex,
|
|
||||||
getLocalInstances
|
|
||||||
} from '@utils/slices/instancesSlice'
|
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const SettingsDev: React.FC = () => {
|
const SettingsDev: React.FC = () => {
|
||||||
const { showActionSheetWithOptions } = useActionSheet()
|
const { showActionSheetWithOptions } = useActionSheet()
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const localInstances = useSelector(getLocalInstances)
|
const instances = useSelector(getInstances)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuContainer>
|
<MenuContainer>
|
||||||
<MenuRow
|
<MenuRow
|
||||||
title={'Local active index'}
|
title={'Local active index'}
|
||||||
content={typeof localActiveIndex + ' - ' + localActiveIndex}
|
content={typeof instanceActive + ' - ' + instanceActive}
|
||||||
onPress={() => {}}
|
onPress={() => {}}
|
||||||
/>
|
/>
|
||||||
<MenuRow
|
<MenuRow
|
||||||
title={'Saved local instances'}
|
title={'Saved local instances'}
|
||||||
content={localInstances.length.toString()}
|
content={instances.length.toString()}
|
||||||
iconBack='ChevronRight'
|
iconBack='ChevronRight'
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
showActionSheetWithOptions(
|
showActionSheetWithOptions(
|
||||||
{
|
{
|
||||||
options: localInstances
|
options: instances
|
||||||
.map(instance => {
|
.map(instance => {
|
||||||
return instance.url + ': ' + instance.account.id
|
return instance.url + ': ' + instance.account.id
|
||||||
})
|
})
|
||||||
.concat(['Cancel']),
|
.concat(['Cancel']),
|
||||||
cancelButtonIndex: localInstances.length
|
cancelButtonIndex: instances.length
|
||||||
},
|
},
|
||||||
buttonIndex => {}
|
buttonIndex => {}
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,6 @@ import Icon from '@components/Icon'
|
|||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { useSearchQuery } from '@utils/queryHooks/search'
|
import { useSearchQuery } from '@utils/queryHooks/search'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
|
||||||
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 Updates from 'expo-updates'
|
import * as Updates from 'expo-updates'
|
||||||
@ -13,16 +12,17 @@ import * as WebBrowser from 'expo-web-browser'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
|
|
||||||
const SettingsTooot: React.FC = () => {
|
const SettingsTooot: React.FC = () => {
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const { t } = useTranslation('meSettings')
|
const { t } = useTranslation('meSettings')
|
||||||
|
|
||||||
const { isLoading, data } = useSearchQuery({
|
const { isLoading, data } = useSearchQuery({
|
||||||
term: '@tooot@xmflsct.com',
|
term: '@tooot@xmflsct.com',
|
||||||
options: { enabled: localActiveIndex !== null }
|
options: { enabled: instanceActive !== -1 }
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -4,10 +4,10 @@ import haptics from '@components/haptics'
|
|||||||
import ComponentInstance from '@components/Instance'
|
import ComponentInstance from '@components/Instance'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import {
|
import {
|
||||||
getLocalActiveIndex,
|
getInstanceActive,
|
||||||
getLocalInstances,
|
getInstances,
|
||||||
InstanceLocal,
|
Instance,
|
||||||
updateLocalActiveIndex
|
updateInstanceActive
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
@ -25,7 +25,7 @@ import { useQueryClient } from 'react-query'
|
|||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
instance: InstanceLocal
|
instance: Instance
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ const AccountButton: React.FC<Props> = ({ instance, disabled = false }) => {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
haptics('Light')
|
haptics('Light')
|
||||||
analytics('switch_existing_press')
|
analytics('switch_existing_press')
|
||||||
dispatch(updateLocalActiveIndex(instance))
|
dispatch(updateInstanceActive(instance))
|
||||||
queryClient.clear()
|
queryClient.clear()
|
||||||
navigation.goBack()
|
navigation.goBack()
|
||||||
}}
|
}}
|
||||||
@ -56,8 +56,8 @@ const AccountButton: React.FC<Props> = ({ instance, disabled = false }) => {
|
|||||||
const ScreenMeSwitchRoot: React.FC = () => {
|
const ScreenMeSwitchRoot: React.FC = () => {
|
||||||
const { t } = useTranslation('meSwitch')
|
const { t } = useTranslation('meSwitch')
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const localInstances = useSelector(getLocalInstances)
|
const instances = useSelector(getInstances)
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
@ -72,8 +72,8 @@ const ScreenMeSwitchRoot: React.FC = () => {
|
|||||||
{t('content.existing')}
|
{t('content.existing')}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={styles.accountButtons}>
|
<View style={styles.accountButtons}>
|
||||||
{localInstances.length
|
{instances.length
|
||||||
? localInstances
|
? instances
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) =>
|
.sort((a, b) =>
|
||||||
`${a.uri}${a.account.acct}`.localeCompare(
|
`${a.uri}${a.account.acct}`.localeCompare(
|
||||||
@ -81,7 +81,7 @@ const ScreenMeSwitchRoot: React.FC = () => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.map((instance, index) => {
|
.map((instance, index) => {
|
||||||
const localAccount = localInstances[localActiveIndex!]
|
const localAccount = instances[instanceActive!]
|
||||||
return (
|
return (
|
||||||
<AccountButton
|
<AccountButton
|
||||||
key={index}
|
key={index}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { HeaderCenter } from '@components/Header'
|
import { HeaderCenter } from '@components/Header'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import sharedScreens from '@screens/Tabs/Shared/sharedScreens'
|
import sharedScreens from '@screens/Tabs/Shared/sharedScreens'
|
||||||
import { updateLocalNotification } from '@utils/slices/instancesSlice'
|
import { updateInstanceNotification } from '@utils/slices/instancesSlice'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform, ViewToken } from 'react-native'
|
import { Platform, ViewToken } from 'react-native'
|
||||||
@ -46,7 +46,7 @@ const TabNotifications = React.memo(
|
|||||||
viewableItems[0].index === 0
|
viewableItems[0].index === 0
|
||||||
) {
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateLocalNotification({
|
updateInstanceNotification({
|
||||||
readTime: viewableItems[0].item.created_at
|
readTime: viewableItems[0].item.created_at
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,7 @@ import Timeline from '@components/Timeline'
|
|||||||
import SegmentedControl from '@react-native-community/segmented-control'
|
import SegmentedControl from '@react-native-community/segmented-control'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import sharedScreens from '@screens/Tabs/Shared/sharedScreens'
|
import sharedScreens from '@screens/Tabs/Shared/sharedScreens'
|
||||||
import { getLocalActiveIndex } from '@utils/slices/instancesSlice'
|
import { getInstanceActive } from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useMemo, useState } from 'react'
|
import React, { useCallback, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -21,7 +21,7 @@ const TabPublic = React.memo(
|
|||||||
const { t, i18n } = useTranslation()
|
const { t, i18n } = useTranslation()
|
||||||
const { mode } = useTheme()
|
const { mode } = useTheme()
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const localActiveIndex = useSelector(getLocalActiveIndex)
|
const instanceActive = useSelector(getInstanceActive)
|
||||||
|
|
||||||
const [segment, setSegment] = useState(0)
|
const [segment, setSegment] = useState(0)
|
||||||
const pages: { title: string; page: App.Pages }[] = [
|
const pages: { title: string; page: App.Pages }[] = [
|
||||||
@ -74,9 +74,9 @@ const TabPublic = React.memo(
|
|||||||
key: App.Pages
|
key: App.Pages
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
return localActiveIndex !== null && <Timeline page={route.key} />
|
return instanceActive !== -1 && <Timeline page={route.key} />
|
||||||
},
|
},
|
||||||
[localActiveIndex]
|
[instanceActive]
|
||||||
)
|
)
|
||||||
const children = useCallback(
|
const children = useCallback(
|
||||||
() => (
|
() => (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getLocalAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
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, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
@ -23,7 +23,7 @@ export interface Props {
|
|||||||
const AccountInformation: React.FC<Props> = ({ account, myInfo = false }) => {
|
const AccountInformation: React.FC<Props> = ({ account, myInfo = false }) => {
|
||||||
const ownAccount =
|
const ownAccount =
|
||||||
account?.id ===
|
account?.id ===
|
||||||
useSelector(getLocalAccount, (prev, next) => prev?.id === next?.id)?.id
|
useSelector(getInstanceAccount, (prev, next) => prev?.id === next?.id)?.id
|
||||||
const { mode, theme } = useTheme()
|
const { mode, theme } = useTheme()
|
||||||
|
|
||||||
const animation = useCallback(
|
const animation = useCallback(
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { getLocalAccount, getLocalUri } from '@utils/slices/instancesSlice'
|
import {
|
||||||
|
getInstanceAccount,
|
||||||
|
getInstanceUri
|
||||||
|
} from '@utils/slices/instancesSlice'
|
||||||
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, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
@ -14,11 +17,11 @@ export interface Props {
|
|||||||
|
|
||||||
const AccountInformationAccount: React.FC<Props> = ({ account, myInfo }) => {
|
const AccountInformationAccount: React.FC<Props> = ({ account, myInfo }) => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const localAccount = useSelector(
|
const instanceAccount = useSelector(
|
||||||
getLocalAccount,
|
getInstanceAccount,
|
||||||
(prev, next) => prev?.acct === next?.acct
|
(prev, next) => prev?.acct === next?.acct
|
||||||
)
|
)
|
||||||
const localUri = useSelector(getLocalUri)
|
const instanceUri = useSelector(getInstanceUri)
|
||||||
|
|
||||||
const movedStyle = useMemo(
|
const movedStyle = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -45,7 +48,7 @@ const AccountInformationAccount: React.FC<Props> = ({ account, myInfo }) => {
|
|||||||
}
|
}
|
||||||
}, [account?.moved])
|
}, [account?.moved])
|
||||||
|
|
||||||
if (account || (myInfo && localAccount !== undefined)) {
|
if (account || (myInfo && instanceAccount)) {
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[styles.base, { flexDirection: 'row', alignItems: 'center' }]}
|
style={[styles.base, { flexDirection: 'row', alignItems: 'center' }]}
|
||||||
@ -60,8 +63,8 @@ const AccountInformationAccount: React.FC<Props> = ({ account, myInfo }) => {
|
|||||||
]}
|
]}
|
||||||
selectable
|
selectable
|
||||||
>
|
>
|
||||||
@{myInfo ? localAccount?.acct : account?.acct}
|
@{myInfo ? instanceAccount.acct : account?.acct}
|
||||||
{myInfo ? `@${localUri}` : null}
|
{myInfo ? `@${instanceUri}` : null}
|
||||||
</Text>
|
</Text>
|
||||||
{movedContent}
|
{movedContent}
|
||||||
{account?.locked ? (
|
{account?.locked ? (
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import NetInfo from '@react-native-community/netinfo'
|
import NetInfo from '@react-native-community/netinfo'
|
||||||
import { store } from '@root/store'
|
import { store } from '@root/store'
|
||||||
|
import removeInstance from '@utils/slices/instances/remove'
|
||||||
import {
|
import {
|
||||||
localRemoveInstance,
|
getInstanceActive,
|
||||||
updateLocalAccount
|
updateInstanceAccount
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
import log from './log'
|
import log from './log'
|
||||||
|
|
||||||
@ -13,29 +14,28 @@ const netInfo = async (): Promise<{
|
|||||||
}> => {
|
}> => {
|
||||||
log('log', 'netInfo', 'initializing')
|
log('log', 'netInfo', 'initializing')
|
||||||
const netInfo = await NetInfo.fetch()
|
const netInfo = await NetInfo.fetch()
|
||||||
const activeIndex = store.getState().instances.local?.activeIndex
|
const activeIndex = getInstanceActive(store.getState())
|
||||||
|
|
||||||
if (netInfo.isConnected) {
|
if (netInfo.isConnected) {
|
||||||
log('log', 'netInfo', 'network connected')
|
log('log', 'netInfo', 'network connected')
|
||||||
if (activeIndex !== null) {
|
if (activeIndex !== -1) {
|
||||||
log('log', 'netInfo', 'checking locally stored credentials')
|
log('log', 'netInfo', 'checking locally stored credentials')
|
||||||
return client<Mastodon.Account>({
|
return apiInstance<Mastodon.Account>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/verify_credentials`
|
url: `accounts/verify_credentials`
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
log('log', 'netInfo', 'local credential check passed')
|
log('log', 'netInfo', 'local credential check passed')
|
||||||
if (
|
if (
|
||||||
res.body.id !==
|
res.body.id !==
|
||||||
store.getState().instances.local?.instances[activeIndex].account.id
|
store.getState().instances.instances[activeIndex].account.id
|
||||||
) {
|
) {
|
||||||
log('error', 'netInfo', 'local id does not match remote id')
|
log('error', 'netInfo', 'local id does not match remote id')
|
||||||
store.dispatch(localRemoveInstance(activeIndex))
|
store.dispatch(removeInstance(activeIndex))
|
||||||
return Promise.resolve({ connected: true, corruputed: '' })
|
return Promise.resolve({ connected: true, corruputed: '' })
|
||||||
} else {
|
} else {
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
updateLocalAccount({
|
updateInstanceAccount({
|
||||||
acct: res.body.acct,
|
acct: res.body.acct,
|
||||||
avatarStatic: res.body.avatar_static
|
avatarStatic: res.body.avatar_static
|
||||||
})
|
})
|
||||||
@ -50,7 +50,7 @@ const netInfo = async (): Promise<{
|
|||||||
typeof error.status === 'number' &&
|
typeof error.status === 'number' &&
|
||||||
error.status === 401
|
error.status === 401
|
||||||
) {
|
) {
|
||||||
store.dispatch(localRemoveInstance(activeIndex))
|
store.dispatch(removeInstance(activeIndex))
|
||||||
}
|
}
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
connected: true,
|
connected: true,
|
||||||
|
39
src/store.ts
39
src/store.ts
@ -6,9 +6,9 @@ import {
|
|||||||
getDefaultMiddleware
|
getDefaultMiddleware
|
||||||
} from '@reduxjs/toolkit'
|
} from '@reduxjs/toolkit'
|
||||||
import contextsSlice from '@utils/slices/contextsSlice'
|
import contextsSlice from '@utils/slices/contextsSlice'
|
||||||
import instancesSlice, { InstancesState } from '@utils/slices/instancesSlice'
|
import instancesSlice from '@utils/slices/instancesSlice'
|
||||||
import settingsSlice from '@utils/slices/settingsSlice'
|
import settingsSlice from '@utils/slices/settingsSlice'
|
||||||
import { createMigrate, persistReducer, persistStore } from 'redux-persist'
|
import { persistReducer, persistStore } from 'redux-persist'
|
||||||
|
|
||||||
const secureStorage = createSecureStore()
|
const secureStorage = createSecureStore()
|
||||||
|
|
||||||
@ -20,43 +20,10 @@ const contextsPersistConfig = {
|
|||||||
storage: AsyncStorage
|
storage: AsyncStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
const instancesMigration = {
|
|
||||||
2: (state: InstancesState) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
local: {
|
|
||||||
...state.local,
|
|
||||||
instances: state.local.instances.map(instance => {
|
|
||||||
instance.max_toot_chars = 500
|
|
||||||
instance.drafts = []
|
|
||||||
return instance
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
3: (state: InstancesState) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
local: {
|
|
||||||
...state.local,
|
|
||||||
instances: state.local.instances.map(instance => {
|
|
||||||
if (!instance.urls) {
|
|
||||||
instance.urls = {
|
|
||||||
streaming_api: `wss://${instance.url}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instance
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const instancesPersistConfig = {
|
const instancesPersistConfig = {
|
||||||
key: 'instances',
|
key: 'instances',
|
||||||
prefix,
|
prefix,
|
||||||
version: 3,
|
storage: secureStorage
|
||||||
storage: secureStorage,
|
|
||||||
migrate: createMigrate(instancesMigration, { debug: true })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsPersistConfig = {
|
const settingsPersistConfig = {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { useQuery, UseQueryOptions } from 'react-query'
|
import { useQuery, UseQueryOptions } from 'react-query'
|
||||||
|
|
||||||
@ -7,9 +7,8 @@ export type QueryKey = ['Account', { id: Mastodon.Account['id'] }]
|
|||||||
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
||||||
const { id } = queryKey[1]
|
const { id } = queryKey[1]
|
||||||
|
|
||||||
return client<Mastodon.Account>({
|
return apiInstance<Mastodon.Account>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${id}`
|
url: `accounts/${id}`
|
||||||
}).then(res => res.body)
|
}).then(res => res.body)
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import client from '@api/client'
|
|
||||||
import { InstancesState } from '@utils/slices/instancesSlice'
|
|
||||||
import { AxiosError } from 'axios'
|
|
||||||
import { useQuery, UseQueryOptions } from 'react-query'
|
|
||||||
|
|
||||||
export type QueryKey = [
|
|
||||||
'AccountCheck',
|
|
||||||
{
|
|
||||||
id: Mastodon.Account['id']
|
|
||||||
index: NonNullable<InstancesState['local']['activeIndex']>
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const queryFunction = async ({ queryKey }: { queryKey: QueryKey }) => {
|
|
||||||
const { id, index } = queryKey[1]
|
|
||||||
|
|
||||||
return client<Mastodon.Account>({
|
|
||||||
method: 'get',
|
|
||||||
instance: 'local',
|
|
||||||
localIndex: index,
|
|
||||||
url: `accounts/${id}`
|
|
||||||
}).then(res => res.body)
|
|
||||||
}
|
|
||||||
|
|
||||||
const useAccountCheckQuery = <TData = Mastodon.Account>({
|
|
||||||
options,
|
|
||||||
...queryKeyParams
|
|
||||||
}: QueryKey[1] & {
|
|
||||||
options?: UseQueryOptions<Mastodon.Account, AxiosError, TData>
|
|
||||||
}) => {
|
|
||||||
const queryKey: QueryKey = ['AccountCheck', { ...queryKeyParams }]
|
|
||||||
return useQuery(queryKey, queryFunction, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { useAccountCheckQuery }
|
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import {
|
import {
|
||||||
useMutation,
|
useMutation,
|
||||||
@ -12,9 +12,8 @@ type QueryKeyAnnouncement = ['Announcements', { showAll?: boolean }]
|
|||||||
const queryFunction = ({ queryKey }: { queryKey: QueryKeyAnnouncement }) => {
|
const queryFunction = ({ queryKey }: { queryKey: QueryKeyAnnouncement }) => {
|
||||||
const { showAll } = queryKey[1]
|
const { showAll } = queryKey[1]
|
||||||
|
|
||||||
return client<Mastodon.Announcement[]>({
|
return apiInstance<Mastodon.Announcement[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `announcements`,
|
url: `announcements`,
|
||||||
...(showAll && {
|
...(showAll && {
|
||||||
params: {
|
params: {
|
||||||
@ -52,15 +51,13 @@ const mutationFunction = async ({
|
|||||||
}: MutationVarsAnnouncement) => {
|
}: MutationVarsAnnouncement) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'reaction':
|
case 'reaction':
|
||||||
return client<{}>({
|
return apiInstance<{}>({
|
||||||
method: me ? 'delete' : 'put',
|
method: me ? 'delete' : 'put',
|
||||||
instance: 'local',
|
|
||||||
url: `announcements/${id}/reactions/${name}`
|
url: `announcements/${id}/reactions/${name}`
|
||||||
})
|
})
|
||||||
case 'dismiss':
|
case 'dismiss':
|
||||||
return client<{}>({
|
return apiInstance<{}>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: `announcements/${id}/dismiss`
|
url: `announcements/${id}/dismiss`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import client from '@api/client'
|
import apiGeneral from '@api/general'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import * as AuthSession from 'expo-auth-session'
|
import * as AuthSession from 'expo-auth-session'
|
||||||
import { useQuery, UseQueryOptions } from 'react-query'
|
import { useQuery, UseQueryOptions } from 'react-query'
|
||||||
|
|
||||||
export type QueryKey = ['Apps', { instanceDomain?: string }]
|
export type QueryKey = ['Apps', { domain?: string }]
|
||||||
|
|
||||||
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
||||||
const redirectUri = AuthSession.makeRedirectUri({
|
const redirectUri = AuthSession.makeRedirectUri({
|
||||||
@ -11,7 +11,7 @@ const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
|||||||
useProxy: false
|
useProxy: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const { instanceDomain } = queryKey[1]
|
const { domain } = queryKey[1]
|
||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('client_name', 'tooot')
|
formData.append('client_name', 'tooot')
|
||||||
@ -19,11 +19,10 @@ const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
|||||||
formData.append('redirect_uris', redirectUri)
|
formData.append('redirect_uris', redirectUri)
|
||||||
formData.append('scopes', 'read write follow push')
|
formData.append('scopes', 'read write follow push')
|
||||||
|
|
||||||
return client<Mastodon.Apps>({
|
return apiGeneral<Mastodon.Apps>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'remote',
|
domain: domain || '',
|
||||||
instanceDomain,
|
url: `api/v1/apps`,
|
||||||
url: `apps`,
|
|
||||||
body: formData
|
body: formData
|
||||||
}).then(res => res.body)
|
}).then(res => res.body)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { useQuery, UseQueryOptions } from 'react-query'
|
import { useQuery, UseQueryOptions } from 'react-query'
|
||||||
|
|
||||||
type QueryKey = ['Emojis']
|
type QueryKey = ['Emojis']
|
||||||
|
|
||||||
const queryFunction = () => {
|
const queryFunction = () => {
|
||||||
return client<Mastodon.Emoji[]>({
|
return apiInstance<Mastodon.Emoji[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: 'custom_emojis'
|
url: 'custom_emojis'
|
||||||
}).then(res => res.body)
|
}).then(res => res.body)
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import client from '@api/client'
|
import apiGeneral from '@api/general'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { useQuery, UseQueryOptions } from 'react-query'
|
import { useQuery, UseQueryOptions } from 'react-query'
|
||||||
|
|
||||||
export type QueryKey = ['Instance', { instanceDomain?: string }]
|
export type QueryKey = ['Instance', { domain?: string }]
|
||||||
|
|
||||||
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
const queryFunction = async ({ queryKey }: { queryKey: QueryKey }) => {
|
||||||
const { instanceDomain } = queryKey[1]
|
const { domain } = queryKey[1]
|
||||||
|
if (!domain) {
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
|
||||||
return client<Mastodon.Instance>({
|
const res = await apiGeneral<Mastodon.Instance>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'remote',
|
domain: domain,
|
||||||
instanceDomain,
|
url: `api/v1/instance`
|
||||||
url: `instance`
|
})
|
||||||
}).then(res => res.body)
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
const useInstanceQuery = <
|
const useInstanceQuery = <
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { useQuery, UseQueryOptions } from 'react-query'
|
import { useQuery, UseQueryOptions } from 'react-query'
|
||||||
|
|
||||||
export type QueryKey = ['Lists']
|
export type QueryKey = ['Lists']
|
||||||
|
|
||||||
const queryFunction = () => {
|
const queryFunction = () => {
|
||||||
return client<Mastodon.List[]>({
|
return apiInstance<Mastodon.List[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: 'lists'
|
url: 'lists'
|
||||||
}).then(res => res.body)
|
}).then(res => res.body)
|
||||||
}
|
}
|
||||||
|
24
src/utils/queryHooks/push.ts
Normal file
24
src/utils/queryHooks/push.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import apiInstance from '@api/instance'
|
||||||
|
import { AxiosError } from 'axios'
|
||||||
|
import { useQuery, UseQueryOptions } from 'react-query'
|
||||||
|
|
||||||
|
export type QueryKey = ['Push']
|
||||||
|
|
||||||
|
const queryFunction = async () => {
|
||||||
|
const res = await apiInstance<Mastodon.PushSubscription>({
|
||||||
|
method: 'get',
|
||||||
|
url: 'push/subscription'
|
||||||
|
})
|
||||||
|
return res.body
|
||||||
|
}
|
||||||
|
|
||||||
|
const usePushQuery = <TData = Mastodon.PushSubscription>({
|
||||||
|
options
|
||||||
|
}: {
|
||||||
|
options?: UseQueryOptions<Mastodon.PushSubscription, AxiosError, TData>
|
||||||
|
}) => {
|
||||||
|
const queryKey: QueryKey = ['Push']
|
||||||
|
return useQuery(queryKey, queryFunction, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { usePushQuery }
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import {
|
import {
|
||||||
useMutation,
|
useMutation,
|
||||||
@ -15,9 +15,8 @@ export type QueryKeyRelationship = [
|
|||||||
const queryFunction = ({ queryKey }: { queryKey: QueryKeyRelationship }) => {
|
const queryFunction = ({ queryKey }: { queryKey: QueryKeyRelationship }) => {
|
||||||
const { id } = queryKey[1]
|
const { id } = queryKey[1]
|
||||||
|
|
||||||
return client<Mastodon.Relationship[]>({
|
return apiInstance<Mastodon.Relationship[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/relationships`,
|
url: `accounts/relationships`,
|
||||||
params: {
|
params: {
|
||||||
'id[]': id
|
'id[]': id
|
||||||
@ -57,15 +56,13 @@ type MutationVarsRelationship =
|
|||||||
const mutationFunction = async (params: MutationVarsRelationship) => {
|
const mutationFunction = async (params: MutationVarsRelationship) => {
|
||||||
switch (params.type) {
|
switch (params.type) {
|
||||||
case 'incoming':
|
case 'incoming':
|
||||||
return client<Mastodon.Relationship>({
|
return apiInstance<Mastodon.Relationship>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: `follow_requests/${params.id}/${params.payload.action}`
|
url: `follow_requests/${params.id}/${params.payload.action}`
|
||||||
}).then(res => res.body)
|
}).then(res => res.body)
|
||||||
case 'outgoing':
|
case 'outgoing':
|
||||||
return client<Mastodon.Relationship>({
|
return apiInstance<Mastodon.Relationship>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${params.id}/${params.payload.state ? 'un' : ''}${
|
url: `accounts/${params.id}/${params.payload.state ? 'un' : ''}${
|
||||||
params.payload.action
|
params.payload.action
|
||||||
}`
|
}`
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { useInfiniteQuery, UseInfiniteQueryOptions } from 'react-query'
|
import { useInfiniteQuery, UseInfiniteQueryOptions } from 'react-query'
|
||||||
|
|
||||||
@ -17,9 +17,8 @@ const queryFunction = ({
|
|||||||
const { type, id } = queryKey[1]
|
const { type, id } = queryKey[1]
|
||||||
let params: { [key: string]: string } = { ...pageParam }
|
let params: { [key: string]: string } = { ...pageParam }
|
||||||
|
|
||||||
return client<Mastodon.Account[]>({
|
return apiInstance<Mastodon.Account[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${id}/${type}`,
|
url: `accounts/${id}/${type}`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { useQuery, UseQueryOptions } from 'react-query'
|
import { useQuery, UseQueryOptions } from 'react-query'
|
||||||
|
|
||||||
@ -19,10 +19,9 @@ type SearchResult = {
|
|||||||
|
|
||||||
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
const queryFunction = ({ queryKey }: { queryKey: QueryKey }) => {
|
||||||
const { type, term, limit = 20 } = queryKey[1]
|
const { type, term, limit = 20 } = queryKey[1]
|
||||||
return client<SearchResult>({
|
return apiInstance<SearchResult>({
|
||||||
version: 'v2',
|
version: 'v2',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: 'search',
|
url: 'search',
|
||||||
params: { ...(type && { type }), ...(term && { q: term }), limit }
|
params: { ...(type && { type }), ...(term && { q: term }), limit }
|
||||||
}).then(res => res.body)
|
}).then(res => res.body)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import client from '@api/client'
|
import apiInstance from '@api/instance'
|
||||||
import haptics from '@components/haptics'
|
import haptics from '@components/haptics'
|
||||||
import { AxiosError } from 'axios'
|
import { AxiosError } from 'axios'
|
||||||
import { uniqBy } from 'lodash'
|
import { uniqBy } from 'lodash'
|
||||||
@ -35,17 +35,15 @@ const queryFunction = ({
|
|||||||
|
|
||||||
switch (page) {
|
switch (page) {
|
||||||
case 'Following':
|
case 'Following':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: 'timelines/home',
|
url: 'timelines/home',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Local':
|
case 'Local':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: 'timelines/public',
|
url: 'timelines/public',
|
||||||
params: {
|
params: {
|
||||||
...params,
|
...params,
|
||||||
@ -54,26 +52,23 @@ const queryFunction = ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
case 'LocalPublic':
|
case 'LocalPublic':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: 'timelines/public',
|
url: 'timelines/public',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Notifications':
|
case 'Notifications':
|
||||||
return client<Mastodon.Notification[]>({
|
return apiInstance<Mastodon.Notification[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: 'notifications',
|
url: 'notifications',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Account_Default':
|
case 'Account_Default':
|
||||||
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
if (pageParam && pageParam.hasOwnProperty('max_id')) {
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${account}/statuses`,
|
url: `accounts/${account}/statuses`,
|
||||||
params: {
|
params: {
|
||||||
exclude_replies: 'true',
|
exclude_replies: 'true',
|
||||||
@ -81,9 +76,8 @@ const queryFunction = ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return client<(Mastodon.Status & { isPinned: boolean })[]>({
|
return apiInstance<(Mastodon.Status & { isPinned: boolean })[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${account}/statuses`,
|
url: `accounts/${account}/statuses`,
|
||||||
params: {
|
params: {
|
||||||
pinned: 'true'
|
pinned: 'true'
|
||||||
@ -91,9 +85,8 @@ const queryFunction = ({
|
|||||||
}).then(async res1 => {
|
}).then(async res1 => {
|
||||||
let pinned: Mastodon.Status['id'][] = []
|
let pinned: Mastodon.Status['id'][] = []
|
||||||
res1.body.forEach(status => pinned.push(status.id))
|
res1.body.forEach(status => pinned.push(status.id))
|
||||||
const res2 = await client<Mastodon.Status[]>({
|
const res2 = await apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${account}/statuses`,
|
url: `accounts/${account}/statuses`,
|
||||||
params: {
|
params: {
|
||||||
exclude_replies: 'true'
|
exclude_replies: 'true'
|
||||||
@ -108,17 +101,15 @@ const queryFunction = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'Account_All':
|
case 'Account_All':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${account}/statuses`,
|
url: `accounts/${account}/statuses`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Account_Attachments':
|
case 'Account_Attachments':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${account}/statuses`,
|
url: `accounts/${account}/statuses`,
|
||||||
params: {
|
params: {
|
||||||
only_media: 'true',
|
only_media: 'true',
|
||||||
@ -127,57 +118,50 @@ const queryFunction = ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
case 'Hashtag':
|
case 'Hashtag':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `timelines/tag/${hashtag}`,
|
url: `timelines/tag/${hashtag}`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Conversations':
|
case 'Conversations':
|
||||||
return client<Mastodon.Conversation[]>({
|
return apiInstance<Mastodon.Conversation[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `conversations`,
|
url: `conversations`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Bookmarks':
|
case 'Bookmarks':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `bookmarks`,
|
url: `bookmarks`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Favourites':
|
case 'Favourites':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `favourites`,
|
url: `favourites`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'List':
|
case 'List':
|
||||||
return client<Mastodon.Status[]>({
|
return apiInstance<Mastodon.Status[]>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `timelines/list/${list}`,
|
url: `timelines/list/${list}`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|
||||||
case 'Toot':
|
case 'Toot':
|
||||||
return client<Mastodon.Status>({
|
return apiInstance<Mastodon.Status>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `statuses/${toot}`
|
url: `statuses/${toot}`
|
||||||
}).then(async res1 => {
|
}).then(async res1 => {
|
||||||
const res2 = await client<{
|
const res2 = await apiInstance<{
|
||||||
ancestors: Mastodon.Status[]
|
ancestors: Mastodon.Status[]
|
||||||
descendants: Mastodon.Status[]
|
descendants: Mastodon.Status[]
|
||||||
}>({
|
}>({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
|
||||||
url: `statuses/${toot}/context`
|
url: `statuses/${toot}/context`
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
@ -296,9 +280,8 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return client<Mastodon.Poll>({
|
return apiInstance<Mastodon.Poll>({
|
||||||
method: params.payload.type === 'vote' ? 'post' : 'get',
|
method: params.payload.type === 'vote' ? 'post' : 'get',
|
||||||
instance: 'local',
|
|
||||||
url:
|
url:
|
||||||
params.payload.type === 'vote'
|
params.payload.type === 'vote'
|
||||||
? `polls/${params.payload.id}/votes`
|
? `polls/${params.payload.id}/votes`
|
||||||
@ -306,9 +289,8 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
|
|||||||
...(params.payload.type === 'vote' && { body: formData })
|
...(params.payload.type === 'vote' && { body: formData })
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
return client<Mastodon.Status>({
|
return apiInstance<Mastodon.Status>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: `statuses/${params.id}/${
|
url: `statuses/${params.id}/${
|
||||||
params.payload.currentValue ? 'un' : ''
|
params.payload.currentValue ? 'un' : ''
|
||||||
}${MapPropertyToUrl[params.payload.property]}`
|
}${MapPropertyToUrl[params.payload.property]}`
|
||||||
@ -318,15 +300,13 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
|
|||||||
switch (params.payload.property) {
|
switch (params.payload.property) {
|
||||||
case 'block':
|
case 'block':
|
||||||
case 'mute':
|
case 'mute':
|
||||||
return client<Mastodon.Account>({
|
return apiInstance<Mastodon.Account>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: `accounts/${params.id}/${params.payload.property}`
|
url: `accounts/${params.id}/${params.payload.property}`
|
||||||
})
|
})
|
||||||
case 'reports':
|
case 'reports':
|
||||||
return client<Mastodon.Account>({
|
return apiInstance<Mastodon.Account>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: `reports`,
|
url: `reports`,
|
||||||
params: {
|
params: {
|
||||||
account_id: params.id
|
account_id: params.id
|
||||||
@ -334,15 +314,13 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
case 'deleteItem':
|
case 'deleteItem':
|
||||||
return client<Mastodon.Conversation>({
|
return apiInstance<Mastodon.Conversation>({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
instance: 'local',
|
|
||||||
url: `${params.source}/${params.id}`
|
url: `${params.source}/${params.id}`
|
||||||
})
|
})
|
||||||
case 'domainBlock':
|
case 'domainBlock':
|
||||||
return client<any>({
|
return apiInstance<any>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
instance: 'local',
|
|
||||||
url: `domain_blocks`,
|
url: `domain_blocks`,
|
||||||
params: {
|
params: {
|
||||||
domain: params.domain
|
domain: params.domain
|
||||||
|
@ -32,11 +32,11 @@ export const contextsInitialState = {
|
|||||||
current: 0,
|
current: 0,
|
||||||
hidden: false
|
hidden: false
|
||||||
},
|
},
|
||||||
previousTab: 'Tab-Local'
|
previousTab: 'Tab-Me'
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextsSlice = createSlice({
|
const contextsSlice = createSlice({
|
||||||
name: 'settings',
|
name: 'contexts',
|
||||||
initialState: contextsInitialState as ContextsState,
|
initialState: contextsInitialState as ContextsState,
|
||||||
reducers: {
|
reducers: {
|
||||||
updateStoreReview: (state, action: PayloadAction<1>) => {
|
updateStoreReview: (state, action: PayloadAction<1>) => {
|
||||||
|
87
src/utils/slices/instances/add.ts
Normal file
87
src/utils/slices/instances/add.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import apiGeneral from '@api/general'
|
||||||
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
import { RootState } from '@root/store'
|
||||||
|
import { Instance } from '../instancesSlice'
|
||||||
|
|
||||||
|
const addInstance = createAsyncThunk(
|
||||||
|
'instances/add',
|
||||||
|
async ({
|
||||||
|
domain,
|
||||||
|
token,
|
||||||
|
instance,
|
||||||
|
max_toot_chars = 500,
|
||||||
|
appData
|
||||||
|
}: {
|
||||||
|
domain: Instance['url']
|
||||||
|
token: Instance['token']
|
||||||
|
instance: Mastodon.Instance
|
||||||
|
max_toot_chars?: number
|
||||||
|
appData: Instance['appData']
|
||||||
|
}): Promise<{ type: 'add' | 'overwrite'; data: Instance }> => {
|
||||||
|
const { store } = require('@root/store')
|
||||||
|
const instances = (store.getState() as RootState).instances.instances
|
||||||
|
|
||||||
|
const {
|
||||||
|
body: { id, acct, avatar_static }
|
||||||
|
} = await apiGeneral<Mastodon.Account>({
|
||||||
|
method: 'get',
|
||||||
|
domain,
|
||||||
|
url: `api/v1/accounts/verify_credentials`,
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
|
||||||
|
let type: 'add' | 'overwrite'
|
||||||
|
type = 'add'
|
||||||
|
if (
|
||||||
|
instances.length &&
|
||||||
|
instances.filter(instance => {
|
||||||
|
if (instance.url === domain && instance.account.id === id) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}).length
|
||||||
|
) {
|
||||||
|
type = 'overwrite'
|
||||||
|
} else {
|
||||||
|
type = 'add'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { body: preferences } = await apiGeneral<Mastodon.Preferences>({
|
||||||
|
method: 'get',
|
||||||
|
domain,
|
||||||
|
url: `api/v1/preferences`,
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
type,
|
||||||
|
data: {
|
||||||
|
active: true,
|
||||||
|
appData,
|
||||||
|
url: domain,
|
||||||
|
token,
|
||||||
|
uri: instance.uri,
|
||||||
|
urls: instance.urls,
|
||||||
|
max_toot_chars,
|
||||||
|
account: {
|
||||||
|
id,
|
||||||
|
acct,
|
||||||
|
avatarStatic: avatar_static,
|
||||||
|
preferences
|
||||||
|
},
|
||||||
|
notification: {
|
||||||
|
readTime: undefined,
|
||||||
|
latestTime: undefined
|
||||||
|
},
|
||||||
|
push: {
|
||||||
|
loading: false,
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
drafts: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default addInstance
|
21
src/utils/slices/instances/push/disable.ts
Normal file
21
src/utils/slices/instances/push/disable.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import apiGeneral from '@api/general'
|
||||||
|
import * as Notifications from 'expo-notifications'
|
||||||
|
|
||||||
|
const serverUnregister = async () => {
|
||||||
|
const deviceToken = (await Notifications.getDevicePushTokenAsync()).data
|
||||||
|
|
||||||
|
return apiGeneral<{ endpoint: string; publicKey: string; auth: string }>({
|
||||||
|
method: 'post',
|
||||||
|
domain: 'testpush.home.xmflsct.com',
|
||||||
|
url: 'unregister',
|
||||||
|
body: { deviceToken }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushDisable = async () => {
|
||||||
|
await serverUnregister()
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default pushDisable
|
61
src/utils/slices/instances/push/enable.ts
Normal file
61
src/utils/slices/instances/push/enable.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import apiGeneral from '@api/general'
|
||||||
|
import apiInstance from '@api/instance'
|
||||||
|
import * as Notifications from 'expo-notifications'
|
||||||
|
import { Platform } from 'react-native'
|
||||||
|
|
||||||
|
const serverRegister = async () => {
|
||||||
|
const deviceToken = (await Notifications.getDevicePushTokenAsync()).data
|
||||||
|
const expoToken = (
|
||||||
|
await Notifications.getExpoPushTokenAsync({
|
||||||
|
experienceId: '@xmflsct/tooot'
|
||||||
|
})
|
||||||
|
).data
|
||||||
|
|
||||||
|
return apiGeneral<{ endpoint: string; publicKey: string; auth: string }>({
|
||||||
|
method: 'post',
|
||||||
|
domain: 'testpush.home.xmflsct.com',
|
||||||
|
url: 'register',
|
||||||
|
body: { deviceToken, expoToken }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushEnable = async (): Promise<Mastodon.PushSubscription> => {
|
||||||
|
const { status: existingStatus } = await Notifications.getPermissionsAsync()
|
||||||
|
let finalStatus = existingStatus
|
||||||
|
if (existingStatus !== 'granted') {
|
||||||
|
const { status } = await Notifications.requestPermissionsAsync()
|
||||||
|
finalStatus = status
|
||||||
|
}
|
||||||
|
if (finalStatus !== 'granted') {
|
||||||
|
alert('Failed to get push token for push notification!')
|
||||||
|
return Promise.reject()
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverRes = (await serverRegister()).body
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append(
|
||||||
|
'subscription[endpoint]',
|
||||||
|
'https://testpush.home.xmflsct.com/test1'
|
||||||
|
)
|
||||||
|
formData.append('subscription[keys][p256dh]', serverRes.publicKey)
|
||||||
|
formData.append('subscription[keys][auth]', serverRes.auth)
|
||||||
|
|
||||||
|
const res = await apiInstance<Mastodon.PushSubscription>({
|
||||||
|
method: 'post',
|
||||||
|
url: 'push/subscription',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
return res.body
|
||||||
|
|
||||||
|
// if (Platform.OS === 'android') {
|
||||||
|
// Notifications.setNotificationChannelAsync('default', {
|
||||||
|
// name: 'default',
|
||||||
|
// importance: Notifications.AndroidImportance.MAX,
|
||||||
|
// vibrationPattern: [0, 250, 250, 250],
|
||||||
|
// lightColor: '#FF231F7C'
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default pushEnable
|
40
src/utils/slices/instances/remove.ts
Normal file
40
src/utils/slices/instances/remove.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
import { RootState } from '@root/store'
|
||||||
|
import * as AuthSession from 'expo-auth-session'
|
||||||
|
|
||||||
|
const removeInstance = createAsyncThunk(
|
||||||
|
'instances/remove',
|
||||||
|
async (index: number): Promise<number> => {
|
||||||
|
const { store } = require('@root/store')
|
||||||
|
const instances = (store.getState() as RootState).instances.instances
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
const currentInstance = instances[index]
|
||||||
|
|
||||||
|
let revoked = undefined
|
||||||
|
try {
|
||||||
|
revoked = await AuthSession.revokeAsync(
|
||||||
|
{
|
||||||
|
clientId: currentInstance.appData.clientId,
|
||||||
|
clientSecret: currentInstance.appData.clientSecret,
|
||||||
|
token: currentInstance.token,
|
||||||
|
scopes: ['read', 'write', 'follow', 'push']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
revocationEndpoint: `https://${currentInstance.url}/oauth/revoke`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
console.warn('Revoking error')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!revoked) {
|
||||||
|
console.warn('Revoking error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(index)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default removeInstance
|
12
src/utils/slices/instances/updateAccountPreferences.ts
Normal file
12
src/utils/slices/instances/updateAccountPreferences.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import apiInstance from '@api/instance'
|
||||||
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
export const updateAccountPreferences = createAsyncThunk(
|
||||||
|
'instances/updateAccountPreferences',
|
||||||
|
async (): Promise<Mastodon.Preferences> => {
|
||||||
|
return apiInstance<Mastodon.Preferences>({
|
||||||
|
method: 'get',
|
||||||
|
url: `preferences`
|
||||||
|
}).then(res => res.body)
|
||||||
|
}
|
||||||
|
)
|
17
src/utils/slices/instances/updatePush.ts
Normal file
17
src/utils/slices/instances/updatePush.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
|
import { Instance } from '../instancesSlice'
|
||||||
|
import pushDisable from './push/disable'
|
||||||
|
import pushEnable from './push/enable'
|
||||||
|
|
||||||
|
export const updatePush = createAsyncThunk(
|
||||||
|
'instances/updatePush',
|
||||||
|
async (
|
||||||
|
enable: boolean
|
||||||
|
): Promise<Instance['push']['subscription'] | boolean> => {
|
||||||
|
if (enable) {
|
||||||
|
return pushEnable()
|
||||||
|
} else {
|
||||||
|
return pushDisable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
@ -1,13 +1,15 @@
|
|||||||
import client from '@api/client'
|
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
import { ComposeStateDraft } from '@screens/Compose/utils/types'
|
||||||
import * as AuthSession from 'expo-auth-session'
|
|
||||||
import * as Localization from 'expo-localization'
|
|
||||||
import { findIndex } from 'lodash'
|
import { findIndex } from 'lodash'
|
||||||
|
import addInstance from './instances/add'
|
||||||
|
import removeInstance from './instances/remove'
|
||||||
|
import { updateAccountPreferences } from './instances/updateAccountPreferences'
|
||||||
|
import { updatePush } from './instances/updatePush'
|
||||||
|
|
||||||
export type InstanceLocal = {
|
export type Instance = {
|
||||||
|
active: boolean
|
||||||
appData: {
|
appData: {
|
||||||
clientId: string
|
clientId: string
|
||||||
clientSecret: string
|
clientSecret: string
|
||||||
@ -27,245 +29,105 @@ export type InstanceLocal = {
|
|||||||
readTime?: Mastodon.Notification['created_at']
|
readTime?: Mastodon.Notification['created_at']
|
||||||
latestTime?: Mastodon.Notification['created_at']
|
latestTime?: Mastodon.Notification['created_at']
|
||||||
}
|
}
|
||||||
|
push: {
|
||||||
|
loading: boolean
|
||||||
|
enabled: boolean
|
||||||
|
subscription?: Mastodon.PushSubscription
|
||||||
|
}
|
||||||
drafts: ComposeStateDraft[]
|
drafts: ComposeStateDraft[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InstancesState = {
|
export type InstancesState = {
|
||||||
local: {
|
instances: Instance[]
|
||||||
activeIndex: number | null
|
|
||||||
instances: InstanceLocal[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
remote: {
|
|
||||||
url: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const updateLocalAccountPreferences = createAsyncThunk(
|
|
||||||
'instances/updateLocalAccountPreferences',
|
|
||||||
async (): Promise<Mastodon.Preferences> => {
|
|
||||||
const res = await client<Mastodon.Preferences>({
|
|
||||||
method: 'get',
|
|
||||||
instance: 'local',
|
|
||||||
url: `preferences`
|
|
||||||
})
|
|
||||||
|
|
||||||
return Promise.resolve(res.body)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export const localAddInstance = createAsyncThunk(
|
|
||||||
'instances/localAddInstance',
|
|
||||||
async ({
|
|
||||||
url,
|
|
||||||
token,
|
|
||||||
instance,
|
|
||||||
max_toot_chars = 500,
|
|
||||||
appData
|
|
||||||
}: {
|
|
||||||
url: InstanceLocal['url']
|
|
||||||
token: InstanceLocal['token']
|
|
||||||
instance: Mastodon.Instance
|
|
||||||
max_toot_chars?: number
|
|
||||||
appData: InstanceLocal['appData']
|
|
||||||
}): Promise<{ type: 'add' | 'overwrite'; data: InstanceLocal }> => {
|
|
||||||
const { store } = require('@root/store')
|
|
||||||
const instanceLocal: InstancesState['local'] = store.getState().instances
|
|
||||||
.local
|
|
||||||
|
|
||||||
const {
|
|
||||||
body: { id, acct, avatar_static }
|
|
||||||
} = await client<Mastodon.Account>({
|
|
||||||
method: 'get',
|
|
||||||
instance: 'remote',
|
|
||||||
instanceDomain: url,
|
|
||||||
url: `accounts/verify_credentials`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` }
|
|
||||||
})
|
|
||||||
|
|
||||||
let type: 'add' | 'overwrite'
|
|
||||||
if (
|
|
||||||
instanceLocal.instances.filter(instance => {
|
|
||||||
if (instance) {
|
|
||||||
if (instance.url === url && instance.account.id === id) {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}).length
|
|
||||||
) {
|
|
||||||
type = 'overwrite'
|
|
||||||
} else {
|
|
||||||
type = 'add'
|
|
||||||
}
|
|
||||||
|
|
||||||
const { body: preferences } = await client<Mastodon.Preferences>({
|
|
||||||
method: 'get',
|
|
||||||
instance: 'remote',
|
|
||||||
instanceDomain: url,
|
|
||||||
url: `preferences`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` }
|
|
||||||
})
|
|
||||||
|
|
||||||
return Promise.resolve({
|
|
||||||
type,
|
|
||||||
data: {
|
|
||||||
appData,
|
|
||||||
url,
|
|
||||||
token,
|
|
||||||
uri: instance.uri,
|
|
||||||
urls: instance.urls,
|
|
||||||
max_toot_chars,
|
|
||||||
account: {
|
|
||||||
id,
|
|
||||||
acct,
|
|
||||||
avatarStatic: avatar_static,
|
|
||||||
preferences
|
|
||||||
},
|
|
||||||
notification: {
|
|
||||||
readTime: undefined,
|
|
||||||
latestTime: undefined
|
|
||||||
},
|
|
||||||
drafts: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
export const localRemoveInstance = createAsyncThunk(
|
|
||||||
'instances/localRemoveInstance',
|
|
||||||
async (index?: InstancesState['local']['activeIndex']): Promise<number> => {
|
|
||||||
const { store } = require('@root/store')
|
|
||||||
const instanceLocal: InstancesState['local'] = store.getState().instances
|
|
||||||
.local
|
|
||||||
|
|
||||||
if (index) {
|
|
||||||
return Promise.resolve(index)
|
|
||||||
} else {
|
|
||||||
if (instanceLocal.activeIndex !== null) {
|
|
||||||
const currentInstance =
|
|
||||||
instanceLocal.instances[instanceLocal.activeIndex]
|
|
||||||
|
|
||||||
let revoked = undefined
|
|
||||||
try {
|
|
||||||
revoked = await AuthSession.revokeAsync(
|
|
||||||
{
|
|
||||||
clientId: currentInstance.appData.clientId,
|
|
||||||
clientSecret: currentInstance.appData.clientSecret,
|
|
||||||
token: currentInstance.token,
|
|
||||||
scopes: ['read', 'write', 'follow', 'push']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
revocationEndpoint: `https://${currentInstance.url}/oauth/revoke`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
if (!revoked) {
|
|
||||||
console.warn('Revoking error')
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve(instanceLocal.activeIndex)
|
|
||||||
} else {
|
|
||||||
throw new Error('Active index invalid, cannot remove instance')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export const instancesInitialState: InstancesState = {
|
export const instancesInitialState: InstancesState = {
|
||||||
local: {
|
|
||||||
activeIndex: null,
|
|
||||||
instances: []
|
instances: []
|
||||||
},
|
|
||||||
remote: {
|
|
||||||
url: Localization.locale.includes('zh') ? 'm.cmx.im' : 'mastodon.social'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const findInstanceActive = (state: Instance[]) =>
|
||||||
|
state.findIndex(instance => instance.active)
|
||||||
|
|
||||||
const instancesSlice = createSlice({
|
const instancesSlice = createSlice({
|
||||||
name: 'instances',
|
name: 'instances',
|
||||||
initialState: instancesInitialState,
|
initialState: instancesInitialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
updateLocalActiveIndex: (state, action: PayloadAction<InstanceLocal>) => {
|
updateInstanceActive: ({ instances }, action: PayloadAction<Instance>) => {
|
||||||
state.local.activeIndex = state.local.instances.findIndex(
|
instances = instances.map(instance => {
|
||||||
instance =>
|
instance.active =
|
||||||
instance.url === action.payload.url &&
|
instance.url === action.payload.url &&
|
||||||
instance.token === action.payload.token &&
|
instance.token === action.payload.token &&
|
||||||
instance.account.id === action.payload.account.id
|
instance.account.id === action.payload.account.id
|
||||||
)
|
return instance
|
||||||
|
})
|
||||||
},
|
},
|
||||||
updateLocalAccount: (
|
updateInstanceAccount: (
|
||||||
state,
|
{ instances },
|
||||||
action: PayloadAction<
|
action: PayloadAction<Pick<Instance['account'], 'acct' & 'avatarStatic'>>
|
||||||
Pick<InstanceLocal['account'], 'acct' & 'avatarStatic'>
|
|
||||||
>
|
|
||||||
) => {
|
) => {
|
||||||
if (state.local.activeIndex !== null) {
|
const activeIndex = findInstanceActive(instances)
|
||||||
state.local.instances[state.local.activeIndex].account = {
|
instances[activeIndex].account = {
|
||||||
...state.local.instances[state.local.activeIndex].account,
|
...instances[activeIndex].account,
|
||||||
...action.payload
|
...action.payload
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
updateLocalNotification: (
|
updateInstanceNotification: (
|
||||||
state,
|
{ instances },
|
||||||
action: PayloadAction<Partial<InstanceLocal['notification']>>
|
action: PayloadAction<Partial<Instance['notification']>>
|
||||||
) => {
|
) => {
|
||||||
if (state.local.activeIndex !== null) {
|
const activeIndex = findInstanceActive(instances)
|
||||||
state.local.instances[state.local.activeIndex].notification = {
|
instances[activeIndex].notification = {
|
||||||
...state.local.instances[state.local.activeIndex].notification,
|
...instances[activeIndex].notification,
|
||||||
...action.payload
|
...action.payload
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
updateLocalDraft: (state, action: PayloadAction<ComposeStateDraft>) => {
|
updateInstanceDraft: (
|
||||||
if (state.local.activeIndex !== null) {
|
{ instances },
|
||||||
const draftIndex = findIndex(
|
action: PayloadAction<ComposeStateDraft>
|
||||||
state.local.instances[state.local.activeIndex].drafts,
|
) => {
|
||||||
['timestamp', action.payload.timestamp]
|
const activeIndex = findInstanceActive(instances)
|
||||||
)
|
const draftIndex = findIndex(instances[activeIndex].drafts, [
|
||||||
|
'timestamp',
|
||||||
|
action.payload.timestamp
|
||||||
|
])
|
||||||
if (draftIndex === -1) {
|
if (draftIndex === -1) {
|
||||||
state.local.instances[state.local.activeIndex].drafts.unshift(
|
instances[activeIndex].drafts.unshift(action.payload)
|
||||||
action.payload
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
state.local.instances[state.local.activeIndex].drafts[draftIndex] =
|
instances[activeIndex].drafts[draftIndex] = action.payload
|
||||||
action.payload
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeLocalDraft: (
|
removeInstanceDraft: (
|
||||||
state,
|
{ instances },
|
||||||
action: PayloadAction<ComposeStateDraft['timestamp']>
|
action: PayloadAction<ComposeStateDraft['timestamp']>
|
||||||
) => {
|
) => {
|
||||||
if (state.local.activeIndex !== null) {
|
const activeIndex = findInstanceActive(instances)
|
||||||
state.local.instances[
|
instances[activeIndex].drafts = instances[activeIndex].drafts?.filter(
|
||||||
state.local.activeIndex
|
draft => draft.timestamp !== action.payload
|
||||||
].drafts = state.local.instances[
|
)
|
||||||
state.local.activeIndex
|
|
||||||
].drafts?.filter(draft => draft.timestamp !== action.payload)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
extraReducers: builder => {
|
extraReducers: builder => {
|
||||||
builder
|
builder
|
||||||
.addCase(localAddInstance.fulfilled, (state, action) => {
|
.addCase(addInstance.fulfilled, (state, action) => {
|
||||||
switch (action.payload.type) {
|
switch (action.payload.type) {
|
||||||
case 'add':
|
case 'add':
|
||||||
state.local.instances.push(action.payload.data)
|
state.instances.length &&
|
||||||
state.local.activeIndex = state.local.instances.length - 1
|
(state.instances = state.instances.map(instance => {
|
||||||
|
instance.active = false
|
||||||
|
return instance
|
||||||
|
}))
|
||||||
|
state.instances.push(action.payload.data)
|
||||||
break
|
break
|
||||||
case 'overwrite':
|
case 'overwrite':
|
||||||
state.local.instances = state.local.instances.map(instance => {
|
console.log('overwriting')
|
||||||
|
state.instances = state.instances.map(instance => {
|
||||||
if (
|
if (
|
||||||
instance.url === action.payload.data.url &&
|
instance.url === action.payload.data.url &&
|
||||||
instance.account.id === action.payload.data.account.id
|
instance.account.id === action.payload.data.account.id
|
||||||
) {
|
) {
|
||||||
return action.payload.data
|
return action.payload.data
|
||||||
} else {
|
} else {
|
||||||
|
instance.active = false
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -273,76 +135,99 @@ const instancesSlice = createSlice({
|
|||||||
|
|
||||||
analytics('login')
|
analytics('login')
|
||||||
})
|
})
|
||||||
.addCase(localAddInstance.rejected, (state, action) => {
|
.addCase(addInstance.rejected, (state, action) => {
|
||||||
console.error(state.local)
|
console.error(state.instances)
|
||||||
console.error(action.error)
|
console.error(action.error)
|
||||||
})
|
})
|
||||||
|
|
||||||
.addCase(localRemoveInstance.fulfilled, (state, action) => {
|
.addCase(removeInstance.fulfilled, (state, action) => {
|
||||||
state.local.instances.splice(action.payload, 1)
|
state.instances.splice(action.payload, 1)
|
||||||
state.local.activeIndex = state.local.instances.length
|
state.instances.length &&
|
||||||
? state.local.instances.length - 1
|
(state.instances[state.instances.length - 1].active = true)
|
||||||
: null
|
|
||||||
|
|
||||||
analytics('logout')
|
analytics('logout')
|
||||||
})
|
})
|
||||||
.addCase(localRemoveInstance.rejected, (state, action) => {
|
.addCase(removeInstance.rejected, (state, action) => {
|
||||||
console.error(state.local)
|
console.error(state)
|
||||||
console.error(action.error)
|
console.error(action.error)
|
||||||
})
|
})
|
||||||
|
|
||||||
.addCase(updateLocalAccountPreferences.fulfilled, (state, action) => {
|
.addCase(updateAccountPreferences.fulfilled, (state, action) => {
|
||||||
state.local.instances[state.local.activeIndex!].account.preferences =
|
const activeIndex = findInstanceActive(state.instances)
|
||||||
action.payload
|
state.instances[activeIndex].account.preferences = action.payload
|
||||||
})
|
})
|
||||||
.addCase(updateLocalAccountPreferences.rejected, (_, action) => {
|
.addCase(updateAccountPreferences.rejected, (_, action) => {
|
||||||
console.error(action.error)
|
console.error(action.error)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.addCase(updatePush.fulfilled, (state, action) => {
|
||||||
|
const activeIndex = findInstanceActive(state.instances)
|
||||||
|
if (typeof action.payload === 'boolean') {
|
||||||
|
state.instances[activeIndex].push.enabled = action.payload
|
||||||
|
} else {
|
||||||
|
state.instances[activeIndex].push.enabled = true
|
||||||
|
state.instances[activeIndex].push.subscription = action.payload
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getLocalActiveIndex = ({ instances: { local } }: RootState) =>
|
export const getInstanceActive = ({ instances: { instances } }: RootState) =>
|
||||||
local.activeIndex
|
findInstanceActive(instances)
|
||||||
export const getLocalInstances = ({ instances: { local } }: RootState) =>
|
|
||||||
local.instances
|
export const getInstances = ({ instances: { instances } }: RootState) =>
|
||||||
export const getLocalInstance = ({ instances: { local } }: RootState) =>
|
instances
|
||||||
local.activeIndex !== null ? local.instances[local.activeIndex] : undefined
|
|
||||||
export const getLocalUrl = ({ instances: { local } }: RootState) =>
|
export const getInstance = ({ instances: { instances } }: RootState) => {
|
||||||
local.activeIndex !== null
|
const instanceActive = findInstanceActive(instances)
|
||||||
? local.instances[local.activeIndex].url
|
return instanceActive !== -1 ? instances[instanceActive] : null
|
||||||
: undefined
|
}
|
||||||
export const getLocalUri = ({ instances: { local } }: RootState) =>
|
|
||||||
local.activeIndex !== null
|
export const getInstanceUrl = ({ instances: { instances } }: RootState) => {
|
||||||
? local.instances[local.activeIndex].uri
|
const instanceActive = findInstanceActive(instances)
|
||||||
: undefined
|
return instanceActive !== -1 ? instances[instanceActive].url : null
|
||||||
export const getLocalUrls = ({ instances: { local } }: RootState) =>
|
}
|
||||||
local.activeIndex !== null
|
|
||||||
? local.instances[local.activeIndex].urls
|
export const getInstanceUri = ({ instances: { instances } }: RootState) => {
|
||||||
: undefined
|
const instanceActive = findInstanceActive(instances)
|
||||||
export const getLocalMaxTootChar = ({ instances: { local } }: RootState) =>
|
return instanceActive !== -1 ? instances[instanceActive].uri : null
|
||||||
local.activeIndex !== null
|
}
|
||||||
? local.instances[local.activeIndex].max_toot_chars
|
|
||||||
: 500
|
export const getInstanceUrls = ({ instances: { instances } }: RootState) => {
|
||||||
export const getLocalAccount = ({ instances: { local } }: RootState) =>
|
const instanceActive = findInstanceActive(instances)
|
||||||
local.activeIndex !== null
|
return instanceActive !== -1 ? instances[instanceActive].urls : null
|
||||||
? local.instances[local.activeIndex].account
|
}
|
||||||
: undefined
|
|
||||||
export const getLocalNotification = ({ instances: { local } }: RootState) =>
|
export const getInstanceMaxTootChar = ({
|
||||||
local.activeIndex !== null
|
instances: { instances }
|
||||||
? local.instances[local.activeIndex].notification
|
}: RootState) => {
|
||||||
: undefined
|
const instanceActive = findInstanceActive(instances)
|
||||||
export const getLocalDrafts = ({ instances: { local } }: RootState) =>
|
return instanceActive !== -1 ? instances[instanceActive].max_toot_chars : null
|
||||||
local.activeIndex !== null
|
}
|
||||||
? local.instances[local.activeIndex].drafts
|
|
||||||
: undefined
|
export const getInstanceAccount = ({ instances: { instances } }: RootState) => {
|
||||||
export const getRemoteUrl = ({ instances: { remote } }: RootState) => remote.url
|
const instanceActive = findInstanceActive(instances)
|
||||||
|
return instanceActive !== -1 ? instances[instanceActive].account : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInstanceNotification = ({
|
||||||
|
instances: { instances }
|
||||||
|
}: RootState) => {
|
||||||
|
const instanceActive = findInstanceActive(instances)
|
||||||
|
return instanceActive !== -1 ? instances[instanceActive].notification : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInstanceDrafts = ({ instances: { instances } }: RootState) => {
|
||||||
|
const instanceActive = findInstanceActive(instances)
|
||||||
|
return instanceActive !== -1 ? instances[instanceActive].drafts : null
|
||||||
|
}
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
updateLocalActiveIndex,
|
updateInstanceActive,
|
||||||
updateLocalAccount,
|
updateInstanceAccount,
|
||||||
updateLocalNotification,
|
updateInstanceNotification,
|
||||||
updateLocalDraft,
|
updateInstanceDraft,
|
||||||
removeLocalDraft
|
removeInstanceDraft
|
||||||
} = instancesSlice.actions
|
} = instancesSlice.actions
|
||||||
|
|
||||||
export default instancesSlice.reducer
|
export default instancesSlice.reducer
|
||||||
|
@ -9,6 +9,14 @@ enum availableLanguages {
|
|||||||
'en'
|
'en'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const changeAnalytics = createAsyncThunk(
|
||||||
|
'settings/changeAnalytics',
|
||||||
|
async (newValue: SettingsState['analytics']) => {
|
||||||
|
await Analytics.setAnalyticsCollectionEnabled(newValue)
|
||||||
|
return newValue
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export type SettingsState = {
|
export type SettingsState = {
|
||||||
language: keyof availableLanguages
|
language: keyof availableLanguages
|
||||||
theme: 'light' | 'dark' | 'auto'
|
theme: 'light' | 'dark' | 'auto'
|
||||||
@ -17,6 +25,9 @@ export type SettingsState = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const settingsInitialState = {
|
export const settingsInitialState = {
|
||||||
|
notification: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
language: Object.keys(
|
language: Object.keys(
|
||||||
pickBy(availableLanguages, (_, key) => Localization.locale.includes(key))
|
pickBy(availableLanguages, (_, key) => Localization.locale.includes(key))
|
||||||
)
|
)
|
||||||
@ -31,14 +42,6 @@ export const settingsInitialState = {
|
|||||||
analytics: true
|
analytics: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export const changeAnalytics = createAsyncThunk(
|
|
||||||
'settings/changeAnalytics',
|
|
||||||
async (newValue: SettingsState['analytics']) => {
|
|
||||||
await Analytics.setAnalyticsCollectionEnabled(newValue)
|
|
||||||
return newValue
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const settingsSlice = createSlice({
|
const settingsSlice = createSlice({
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
initialState: settingsInitialState as SettingsState,
|
initialState: settingsInitialState as SettingsState,
|
||||||
|
70
yarn.lock
70
yarn.lock
@ -1224,6 +1224,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^8.3.0"
|
"@hapi/hoek" "^8.3.0"
|
||||||
|
|
||||||
|
"@ide/backoff@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ide/backoff/-/backoff-1.0.0.tgz#466842c25bd4a4833e0642fab41ccff064010176"
|
||||||
|
integrity sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==
|
||||||
|
|
||||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||||
@ -2936,6 +2941,16 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||||
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
||||||
|
|
||||||
|
assert@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32"
|
||||||
|
integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==
|
||||||
|
dependencies:
|
||||||
|
es6-object-assign "^1.1.0"
|
||||||
|
is-nan "^1.2.1"
|
||||||
|
object-is "^1.0.1"
|
||||||
|
util "^0.12.0"
|
||||||
|
|
||||||
assign-symbols@^1.0.0:
|
assign-symbols@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
|
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
|
||||||
@ -3197,7 +3212,7 @@ babel-preset-jest@^26.6.2:
|
|||||||
babel-plugin-jest-hoist "^26.6.2"
|
babel-plugin-jest-hoist "^26.6.2"
|
||||||
babel-preset-current-node-syntax "^1.0.0"
|
babel-preset-current-node-syntax "^1.0.0"
|
||||||
|
|
||||||
badgin@^1.1.2:
|
badgin@^1.1.2, badgin@^1.1.5:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/badgin/-/badgin-1.2.2.tgz#cbb0b71b047230c681a68911eb24136f0632adc6"
|
resolved "https://registry.yarnpkg.com/badgin/-/badgin-1.2.2.tgz#cbb0b71b047230c681a68911eb24136f0632adc6"
|
||||||
integrity sha512-XtoSjNhy2D09qGiLhFWBJmBwBlmleQuwyYyjddWNCJ3gqGRBOBR25VGcd8CAOSghpEUmghB3LD4NpHrUG89zCg==
|
integrity sha512-XtoSjNhy2D09qGiLhFWBJmBwBlmleQuwyYyjddWNCJ3gqGRBOBR25VGcd8CAOSghpEUmghB3LD4NpHrUG89zCg==
|
||||||
@ -4236,6 +4251,11 @@ es-to-primitive@^1.2.1:
|
|||||||
is-date-object "^1.0.1"
|
is-date-object "^1.0.1"
|
||||||
is-symbol "^1.0.2"
|
is-symbol "^1.0.2"
|
||||||
|
|
||||||
|
es6-object-assign@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||||
|
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
||||||
|
|
||||||
escalade@^3.1.1:
|
escalade@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||||
@ -4455,6 +4475,14 @@ expo-constants@*:
|
|||||||
"@expo/config" "^3.3.18"
|
"@expo/config" "^3.3.18"
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
|
expo-constants@9.3.1:
|
||||||
|
version "9.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-9.3.1.tgz#1cab4896ea5e626fc7f19f49893526c94b481443"
|
||||||
|
integrity sha512-58ENdEeVxZ29INv87IqZf5ZD4+NjvxzrHOCFha8iW3TbTL8GXY/6QjlIZ/yGHu4TwBoatozeJQ+9WCCz/hXM0A==
|
||||||
|
dependencies:
|
||||||
|
fbjs "1.0.0"
|
||||||
|
uuid "^3.3.2"
|
||||||
|
|
||||||
expo-constants@^9.3.3, expo-constants@~9.3.0, expo-constants@~9.3.3:
|
expo-constants@^9.3.3, expo-constants@~9.3.0, expo-constants@~9.3.3:
|
||||||
version "9.3.5"
|
version "9.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-9.3.5.tgz#78085763e8ed100a5f2df7c682fd99631aa03d5e"
|
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-9.3.5.tgz#78085763e8ed100a5f2df7c682fd99631aa03d5e"
|
||||||
@ -4560,6 +4588,19 @@ expo-location@~10.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-10.0.0.tgz#2923411649434f2f079343b163b13c5c9eee8b2d"
|
resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-10.0.0.tgz#2923411649434f2f079343b163b13c5c9eee8b2d"
|
||||||
integrity sha512-QLEb0iaBv4/blLxxfKRj2/HPisY+1t+g6MgegqZ1j1U/0qih4dvzUQrxie9ZOZyQB9gnXFCRnZv3QzHEb52dcA==
|
integrity sha512-QLEb0iaBv4/blLxxfKRj2/HPisY+1t+g6MgegqZ1j1U/0qih4dvzUQrxie9ZOZyQB9gnXFCRnZv3QzHEb52dcA==
|
||||||
|
|
||||||
|
expo-notifications@~0.8.2:
|
||||||
|
version "0.8.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.8.2.tgz#69e04a4e48ec6bafaeb354d284fbc23c26f2d62d"
|
||||||
|
integrity sha512-eX/HB96FqXzSMAwtA/fhxB1tGYELQywPm7oBTfnALHH3MFHy1bW7NZYGcU82sDF+DF09uLZ4Fn4p5ValMWA5TA==
|
||||||
|
dependencies:
|
||||||
|
"@ide/backoff" "^1.0.0"
|
||||||
|
abort-controller "^3.0.0"
|
||||||
|
assert "^2.0.0"
|
||||||
|
badgin "^1.1.5"
|
||||||
|
expo-application "~2.4.1"
|
||||||
|
expo-constants "9.3.1"
|
||||||
|
uuid "^3.4.0"
|
||||||
|
|
||||||
expo-permissions@~10.0.0:
|
expo-permissions@~10.0.0:
|
||||||
version "10.0.0"
|
version "10.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-10.0.0.tgz#5b31c54d561d00c7e46cd02321bc3704c51c584b"
|
resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-10.0.0.tgz#5b31c54d561d00c7e46cd02321bc3704c51c584b"
|
||||||
@ -5612,11 +5653,24 @@ is-generator-fn@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
|
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
|
||||||
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
|
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
|
||||||
|
|
||||||
|
is-generator-function@^1.0.7:
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.8.tgz#dfb5c2b120e02b0a8d9d2c6806cd5621aa922f7b"
|
||||||
|
integrity sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==
|
||||||
|
|
||||||
is-map@^2.0.1, is-map@^2.0.2:
|
is-map@^2.0.1, is-map@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
|
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
|
||||||
integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==
|
integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==
|
||||||
|
|
||||||
|
is-nan@^1.2.1:
|
||||||
|
version "1.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
|
||||||
|
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.0"
|
||||||
|
define-properties "^1.1.3"
|
||||||
|
|
||||||
is-negative-zero@^2.0.1:
|
is-negative-zero@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
|
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
|
||||||
@ -7899,7 +7953,7 @@ object-inspect@^1.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
|
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
|
||||||
integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
|
integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
|
||||||
|
|
||||||
object-is@^1.1.4:
|
object-is@^1.0.1, object-is@^1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068"
|
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068"
|
||||||
integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==
|
integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==
|
||||||
@ -10218,6 +10272,18 @@ util-deprecate@~1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||||
|
|
||||||
|
util@^0.12.0:
|
||||||
|
version "0.12.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888"
|
||||||
|
integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==
|
||||||
|
dependencies:
|
||||||
|
inherits "^2.0.3"
|
||||||
|
is-arguments "^1.0.4"
|
||||||
|
is-generator-function "^1.0.7"
|
||||||
|
is-typed-array "^1.1.3"
|
||||||
|
safe-buffer "^5.1.2"
|
||||||
|
which-typed-array "^1.1.2"
|
||||||
|
|
||||||
utils-merge@1.0.1:
|
utils-merge@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user