mirror of https://github.com/tooot-app/app
parent
ab0062e73c
commit
3280663144
|
@ -18,7 +18,7 @@
|
||||||
"@react-navigation/stack": "^5.12.3",
|
"@react-navigation/stack": "^5.12.3",
|
||||||
"@reduxjs/toolkit": "^1.4.0",
|
"@reduxjs/toolkit": "^1.4.0",
|
||||||
"expo": "~39.0.4",
|
"expo": "~39.0.4",
|
||||||
"expo-app-auth": "~9.2.0",
|
"expo-auth-session": "~2.0.0",
|
||||||
"expo-av": "~8.6.0",
|
"expo-av": "~8.6.0",
|
||||||
"expo-image-picker": "~9.1.1",
|
"expo-image-picker": "~9.1.1",
|
||||||
"expo-secure-store": "~9.2.0",
|
"expo-secure-store": "~9.2.0",
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
declare namespace store {
|
declare namespace App {
|
||||||
type InstanceInfoState = {
|
|
||||||
local: string
|
|
||||||
localToken: string
|
|
||||||
remote: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pages =
|
type Pages =
|
||||||
| 'Following'
|
| 'Following'
|
||||||
| 'Local'
|
| 'Local'
|
|
@ -1,4 +1,4 @@
|
||||||
declare namespace mastodon {
|
declare namespace Mastodon {
|
||||||
type Account = {
|
type Account = {
|
||||||
// Base
|
// Base
|
||||||
id: string
|
id: string
|
||||||
|
@ -184,6 +184,14 @@ declare namespace mastodon {
|
||||||
emojis: Emoji[]
|
emojis: Emoji[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Preferences = {
|
||||||
|
'posting:default:visibility'?: 'public' | 'unlisted' | 'private' | 'direct'
|
||||||
|
'posting:default:sensitive'?: boolean
|
||||||
|
'posting:default:language'?: string
|
||||||
|
'reading:expand:media'?: 'default' | 'show_all' | 'hide_all'
|
||||||
|
'reading:expand:spoilers'?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
type Status = {
|
type Status = {
|
||||||
// Base
|
// Base
|
||||||
id: string
|
id: string
|
||||||
|
|
|
@ -22,8 +22,9 @@ const client = async ({
|
||||||
}
|
}
|
||||||
body?: FormData
|
body?: FormData
|
||||||
}): Promise<any> => {
|
}): Promise<any> => {
|
||||||
const state: RootState['instanceInfo'] = store.getState().instanceInfo
|
const state: RootState['instances'] = store.getState().instances
|
||||||
const url = instanceUrl || store.getState().instanceInfo[instance]
|
const url =
|
||||||
|
instance === 'remote' ? instanceUrl || state.remote.url : state.local.url
|
||||||
|
|
||||||
let response
|
let response
|
||||||
// try {
|
// try {
|
||||||
|
@ -35,7 +36,7 @@ const client = async ({
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...headers,
|
...headers,
|
||||||
...(instance === 'local' && {
|
...(instance === 'local' && {
|
||||||
Authorization: `Bearer ${state.localToken}`
|
Authorization: `Bearer ${state.local.token}`
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
...(body && { body: body }),
|
...(body && { body: body }),
|
||||||
|
|
|
@ -15,7 +15,7 @@ const renderNode = ({
|
||||||
node: HTMLViewNode
|
node: HTMLViewNode
|
||||||
index: number
|
index: number
|
||||||
navigation: any
|
navigation: any
|
||||||
mentions?: mastodon.Mention[]
|
mentions?: Mastodon.Mention[]
|
||||||
showFullLink: boolean
|
showFullLink: boolean
|
||||||
}) => {
|
}) => {
|
||||||
if (node.name == 'a') {
|
if (node.name == 'a') {
|
||||||
|
@ -80,9 +80,9 @@ const renderNode = ({
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
content: string
|
content: string
|
||||||
emojis?: mastodon.Emoji[]
|
emojis?: Mastodon.Emoji[]
|
||||||
emojiSize?: number
|
emojiSize?: number
|
||||||
mentions?: mastodon.Mention[]
|
mentions?: Mastodon.Mention[]
|
||||||
showFullLink?: boolean
|
showFullLink?: boolean
|
||||||
linesTruncated?: number
|
linesTruncated?: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Emojis from './Emojis'
|
||||||
export interface Props {
|
export interface Props {
|
||||||
action: 'favourite' | 'follow' | 'mention' | 'poll' | 'reblog'
|
action: 'favourite' | 'follow' | 'mention' | 'poll' | 'reblog'
|
||||||
name?: string
|
name?: string
|
||||||
emojis?: mastodon.Emoji[]
|
emojis?: Mastodon.Emoji[]
|
||||||
notification?: boolean
|
notification?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,11 @@ import {
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import Toast from 'react-native-toast-message'
|
import Toast from 'react-native-toast-message'
|
||||||
import { useMutation, useQueryCache } from 'react-query'
|
import { useMutation, useQueryCache } from 'react-query'
|
||||||
import { useSelector } from 'react-redux'
|
|
||||||
import { Feather } from '@expo/vector-icons'
|
import { Feather } from '@expo/vector-icons'
|
||||||
|
|
||||||
import client from 'src/api/client'
|
import client from 'src/api/client'
|
||||||
|
import { getLocalAccountId } from 'src/stacks/common/instancesSlice'
|
||||||
|
import store from 'src/stacks/common/store'
|
||||||
|
|
||||||
const fireMutation = async ({
|
const fireMutation = async ({
|
||||||
id,
|
id,
|
||||||
|
@ -106,12 +107,12 @@ const fireMutation = async ({
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
queryKey: store.QueryKey
|
queryKey: App.QueryKey
|
||||||
status: mastodon.Status
|
status: Mastodon.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
|
const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
|
||||||
const localAccountId = useSelector(state => state.instanceInfo.localAccountId)
|
const localAccountId = getLocalAccountId(store.getState())
|
||||||
const [modalVisible, setModalVisible] = useState(false)
|
const [modalVisible, setModalVisible] = useState(false)
|
||||||
|
|
||||||
const queryCache = useQueryCache()
|
const queryCache = useQueryCache()
|
||||||
|
@ -129,7 +130,7 @@ const ActionsStatus: React.FC<Props> = ({ queryKey, status }) => {
|
||||||
// oldData &&
|
// oldData &&
|
||||||
// oldData.map((paging: any) => {
|
// oldData.map((paging: any) => {
|
||||||
// paging.toots.map(
|
// paging.toots.map(
|
||||||
// (status: mastodon.Status | mastodon.Notification, i: number) => {
|
// (status: Mastodon.Status | Mastodon.Notification, i: number) => {
|
||||||
// if (status.id === newData.id) {
|
// if (status.id === newData.id) {
|
||||||
// paging.toots[i] = newData
|
// paging.toots[i] = newData
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -5,7 +5,7 @@ import AttachmentImage from './Attachment/AttachmentImage'
|
||||||
import AttachmentVideo from './Attachment/AttachmentVideo'
|
import AttachmentVideo from './Attachment/AttachmentVideo'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
media_attachments: mastodon.Attachment[]
|
media_attachments: Mastodon.Attachment[]
|
||||||
sensitive: boolean
|
sensitive: boolean
|
||||||
width: number
|
width: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Button, Image, Modal, StyleSheet, Pressable, View } from 'react-native'
|
||||||
import ImageViewer from 'react-native-image-zoom-viewer'
|
import ImageViewer from 'react-native-image-zoom-viewer'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
media_attachments: mastodon.Attachment[]
|
media_attachments: Mastodon.Attachment[]
|
||||||
sensitive: boolean
|
sensitive: boolean
|
||||||
width: number
|
width: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Video } from 'expo-av'
|
||||||
import { Feather } from '@expo/vector-icons'
|
import { Feather } from '@expo/vector-icons'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
media_attachments: mastodon.Attachment[]
|
media_attachments: Mastodon.Attachment[]
|
||||||
sensitive: boolean
|
sensitive: boolean
|
||||||
width: number
|
width: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
card: mastodon.Card
|
card: Mastodon.Card
|
||||||
}
|
}
|
||||||
|
|
||||||
const Card: React.FC<Props> = ({ card }) => {
|
const Card: React.FC<Props> = ({ card }) => {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import ParseContent from 'src/components/ParseContent'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
content: string
|
content: string
|
||||||
emojis: mastodon.Emoji[]
|
emojis: Mastodon.Emoji[]
|
||||||
mentions: mastodon.Mention[]
|
mentions: Mastodon.Mention[]
|
||||||
spoiler_text?: string
|
spoiler_text?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ const regexEmoji = new RegExp(/(:[a-z0-9_]+:)/)
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
content: string
|
content: string
|
||||||
emojis: mastodon.Emoji[]
|
emojis: Mastodon.Emoji[]
|
||||||
dimension: number
|
dimension: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,11 @@ import Emojis from './Emojis'
|
||||||
import relativeTime from 'src/utils/relativeTime'
|
import relativeTime from 'src/utils/relativeTime'
|
||||||
import client from 'src/api/client'
|
import client from 'src/api/client'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
import {
|
||||||
|
getLocalAccountId,
|
||||||
|
getLocalUrl
|
||||||
|
} from 'src/stacks/common/instancesSlice'
|
||||||
|
import store from 'src/stacks/common/store'
|
||||||
|
|
||||||
const fireMutation = async ({
|
const fireMutation = async ({
|
||||||
id,
|
id,
|
||||||
|
@ -116,14 +121,14 @@ const fireMutation = async ({
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
queryKey: store.QueryKey
|
queryKey: App.QueryKey
|
||||||
accountId: string
|
accountId: string
|
||||||
domain: string
|
domain: string
|
||||||
name: string
|
name: string
|
||||||
emojis?: mastodon.Emoji[]
|
emojis?: Mastodon.Emoji[]
|
||||||
account: string
|
account: string
|
||||||
created_at: string
|
created_at: string
|
||||||
application?: mastodon.Application
|
application?: Mastodon.Application
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header: React.FC<Props> = ({
|
const Header: React.FC<Props> = ({
|
||||||
|
@ -137,8 +142,8 @@ const Header: React.FC<Props> = ({
|
||||||
application
|
application
|
||||||
}) => {
|
}) => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const localAccountId = useSelector(state => state.instanceInfo.localAccountId)
|
const localAccountId = getLocalAccountId(store.getState())
|
||||||
const localDomain = useSelector(state => state.instanceInfo.local)
|
const localDomain = getLocalUrl(store.getState())
|
||||||
const [since, setSince] = useState(relativeTime(created_at))
|
const [since, setSince] = useState(relativeTime(created_at))
|
||||||
const [modalVisible, setModalVisible] = useState(false)
|
const [modalVisible, setModalVisible] = useState(false)
|
||||||
|
|
||||||
|
@ -158,7 +163,7 @@ const Header: React.FC<Props> = ({
|
||||||
// oldData &&
|
// oldData &&
|
||||||
// oldData.map((paging: any) => {
|
// oldData.map((paging: any) => {
|
||||||
// paging.toots.map(
|
// paging.toots.map(
|
||||||
// (status: mastodon.Status | mastodon.Notification, i: number) => {
|
// (status: Mastodon.Status | Mastodon.Notification, i: number) => {
|
||||||
// if (status.id === newData.id) {
|
// if (status.id === newData.id) {
|
||||||
// paging.toots[i] = newData
|
// paging.toots[i] = newData
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { StyleSheet, Text, View } from 'react-native'
|
||||||
import Emojis from './Emojis'
|
import Emojis from './Emojis'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
poll: mastodon.Poll
|
poll: Mastodon.Poll
|
||||||
}
|
}
|
||||||
// When haven't voted, result should not be shown but intead let people vote
|
// When haven't voted, result should not be shown but intead let people vote
|
||||||
const Poll: React.FC<Props> = ({ poll }) => {
|
const Poll: React.FC<Props> = ({ poll }) => {
|
||||||
|
|
|
@ -12,8 +12,8 @@ import Card from './Status/Card'
|
||||||
import ActionsStatus from './Status/ActionsStatus'
|
import ActionsStatus from './Status/ActionsStatus'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
notification: mastodon.Notification
|
notification: Mastodon.Notification
|
||||||
queryKey: store.QueryKey
|
queryKey: App.QueryKey
|
||||||
}
|
}
|
||||||
|
|
||||||
const TootNotification: React.FC<Props> = ({ notification, queryKey }) => {
|
const TootNotification: React.FC<Props> = ({ notification, queryKey }) => {
|
||||||
|
|
|
@ -12,8 +12,8 @@ import Card from './Status/Card'
|
||||||
import ActionsStatus from './Status/ActionsStatus'
|
import ActionsStatus from './Status/ActionsStatus'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
status: mastodon.Status
|
status: Mastodon.Status
|
||||||
queryKey: store.QueryKey
|
queryKey: App.QueryKey
|
||||||
}
|
}
|
||||||
|
|
||||||
const StatusInTimeline: React.FC<Props> = ({ status, queryKey }) => {
|
const StatusInTimeline: React.FC<Props> = ({ status, queryKey }) => {
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { Button, Text, TextInput, View } from 'react-native'
|
import { Button, Text, TextInput, View } from 'react-native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
|
||||||
import { useQuery } from 'react-query'
|
import { useQuery } from 'react-query'
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash'
|
||||||
|
|
||||||
import { instanceFetch } from 'src/stacks/common/instanceFetch'
|
import { instanceFetch } from 'src/stacks/common/instanceFetch'
|
||||||
import { ScreenMeAuthentication } from '../Authentication'
|
|
||||||
import client from 'src/api/client'
|
import client from 'src/api/client'
|
||||||
import * as AppAuth from 'expo-app-auth'
|
import * as AuthSession from 'expo-auth-session'
|
||||||
|
import { useDispatch } from 'react-redux'
|
||||||
|
import { updateLocal } from 'src/stacks/common/instancesSlice'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
|
||||||
const Instance: React.FC = () => {
|
const Instance: React.FC = () => {
|
||||||
|
const navigation = useNavigation()
|
||||||
|
const dispatch = useDispatch()
|
||||||
const [instance, setInstance] = useState('')
|
const [instance, setInstance] = useState('')
|
||||||
|
const [applicationData, setApplicationData] = useState<{
|
||||||
|
clientId: string
|
||||||
|
clientSecret: string
|
||||||
|
}>()
|
||||||
|
|
||||||
const { isSuccess, refetch, data } = useQuery(
|
const { isSuccess, refetch, data } = useQuery(
|
||||||
['Instance', { instance }],
|
['Instance', { instance }],
|
||||||
|
@ -25,6 +32,7 @@ const Instance: React.FC = () => {
|
||||||
debounce(
|
debounce(
|
||||||
text => {
|
text => {
|
||||||
setInstance(text)
|
setInstance(text)
|
||||||
|
setApplicationData(undefined)
|
||||||
refetch()
|
refetch()
|
||||||
},
|
},
|
||||||
1000,
|
1000,
|
||||||
|
@ -35,26 +43,10 @@ const Instance: React.FC = () => {
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
const signInAsync = async (id: string) => {
|
const createApplication = async () => {
|
||||||
let authState = await AppAuth.authAsync({
|
if (applicationData) {
|
||||||
issuer: `https://${instance}`,
|
return Promise.resolve()
|
||||||
scopes: ['read', 'write', 'follow', 'push'],
|
}
|
||||||
clientId: id,
|
|
||||||
redirectUrl: 'exp://127.0.0.1:19000',
|
|
||||||
serviceConfiguration: {
|
|
||||||
authorizationEndpoint: `https://${instance}/oauth/authorize`,
|
|
||||||
revocationEndpoint: `https://${instance}/oauth/revoke`,
|
|
||||||
tokenEndpoint: `https://${instance}/oauth/token`
|
|
||||||
},
|
|
||||||
additionalParameters: {
|
|
||||||
response_type: 'code'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
console.log(authState)
|
|
||||||
return authState
|
|
||||||
}
|
|
||||||
|
|
||||||
const oauthCreateApplication = async () => {
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('client_name', 'test_dudu')
|
formData.append('client_name', 'test_dudu')
|
||||||
formData.append('redirect_uris', 'exp://127.0.0.1:19000')
|
formData.append('redirect_uris', 'exp://127.0.0.1:19000')
|
||||||
|
@ -68,20 +60,60 @@ const Instance: React.FC = () => {
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
if (res.body?.client_id.length > 0) {
|
if (res.body?.client_id.length > 0) {
|
||||||
return Promise.resolve(res.body)
|
setApplicationData({
|
||||||
|
clientId: res.body.client_id,
|
||||||
|
clientSecret: res.body.client_secret
|
||||||
|
})
|
||||||
|
return Promise.resolve()
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject()
|
return Promise.reject()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const oauthFlow = async () => {
|
const [request, response, promptAsync] = AuthSession.useAuthRequest(
|
||||||
const applicationData = await oauthCreateApplication()
|
{
|
||||||
if (applicationData.client_id.length > 0) {
|
clientId: applicationData?.clientId!,
|
||||||
await signInAsync(applicationData.client_id)
|
clientSecret: applicationData?.clientSecret,
|
||||||
} else {
|
scopes: ['read', 'write', 'follow', 'push'],
|
||||||
console.error('Application data error')
|
redirectUri: 'exp://127.0.0.1:19000'
|
||||||
|
// usePKCE: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
authorizationEndpoint: `https://${instance}/oauth/authorize`
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
;(async () => {
|
||||||
|
if (request?.clientId) {
|
||||||
|
await promptAsync()
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}, [request])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
;(async () => {
|
||||||
|
if (response?.type === 'success') {
|
||||||
|
const { accessToken } = await AuthSession.exchangeCodeAsync(
|
||||||
|
{
|
||||||
|
clientId: applicationData?.clientId!,
|
||||||
|
clientSecret: applicationData?.clientSecret,
|
||||||
|
scopes: ['read', 'write', 'follow', 'push'],
|
||||||
|
redirectUri: 'exp://127.0.0.1:19000',
|
||||||
|
code: response.params.code,
|
||||||
|
extraParams: {
|
||||||
|
grant_type: 'authorization_code'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tokenEndpoint: `https://${instance}/oauth/token`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
dispatch(updateLocal({ url: instance, token: accessToken }))
|
||||||
|
navigation.navigate('Local')
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}, [response])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
@ -99,7 +131,7 @@ const Instance: React.FC = () => {
|
||||||
<Button
|
<Button
|
||||||
title='登录'
|
title='登录'
|
||||||
disabled={!data?.uri}
|
disabled={!data?.uri}
|
||||||
onPress={async () => await oauthFlow()}
|
onPress={async () => await createApplication()}
|
||||||
/>
|
/>
|
||||||
{isSuccess && data && data.uri && (
|
{isSuccess && data && data.uri && (
|
||||||
<View>
|
<View>
|
||||||
|
|
|
@ -58,8 +58,8 @@ const Information = ({
|
||||||
account,
|
account,
|
||||||
emojis
|
emojis
|
||||||
}: {
|
}: {
|
||||||
account: mastodon.Account
|
account: Mastodon.Account
|
||||||
emojis: mastodon.Emoji[]
|
emojis: Mastodon.Emoji[]
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.information}>
|
<View style={styles.information}>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { useNavigation } from '@react-navigation/native'
|
||||||
import store from 'src/stacks/common/store'
|
import store from 'src/stacks/common/store'
|
||||||
import PostMain from './PostToot/PostMain'
|
import PostMain from './PostToot/PostMain'
|
||||||
import client from 'src/api/client'
|
import client from 'src/api/client'
|
||||||
|
import { getLocalAccountPreferences } from '../common/instancesSlice'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator()
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ export type PostState = {
|
||||||
offset: number
|
offset: number
|
||||||
}
|
}
|
||||||
| undefined
|
| undefined
|
||||||
emojis: mastodon.Emoji[] | undefined
|
emojis: Mastodon.Emoji[] | undefined
|
||||||
poll: {
|
poll: {
|
||||||
active: boolean
|
active: boolean
|
||||||
total: number
|
total: number
|
||||||
|
@ -130,9 +131,10 @@ const postInitialState: PostState = {
|
||||||
expire: '86400'
|
expire: '86400'
|
||||||
},
|
},
|
||||||
attachments: [],
|
attachments: [],
|
||||||
visibility: store.getState().instanceInfo.localAccount.locked
|
visibility:
|
||||||
? 'private'
|
getLocalAccountPreferences(store.getState())[
|
||||||
: 'public'
|
'posting:default:visibility'
|
||||||
|
] || 'public'
|
||||||
}
|
}
|
||||||
const postReducer = (state: PostState, action: PostAction): PostState => {
|
const postReducer = (state: PostState, action: PostAction): PostState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
|
@ -4,13 +4,12 @@ import { setFocusHandler, useInfiniteQuery } from 'react-query'
|
||||||
|
|
||||||
import StatusInNotifications from 'src/components/StatusInNotifications'
|
import StatusInNotifications from 'src/components/StatusInNotifications'
|
||||||
import StatusInTimeline from 'src/components/StatusInTimeline'
|
import StatusInTimeline from 'src/components/StatusInTimeline'
|
||||||
import store from './store'
|
|
||||||
import { timelineFetch } from './timelineFetch'
|
import { timelineFetch } from './timelineFetch'
|
||||||
|
|
||||||
// Opening nesting hashtag pages
|
// Opening nesting hashtag pages
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
page: store.Pages
|
page: App.Pages
|
||||||
hashtag?: string
|
hashtag?: string
|
||||||
list?: string
|
list?: string
|
||||||
toot?: string
|
toot?: string
|
||||||
|
@ -38,7 +37,7 @@ const Timeline: React.FC<Props> = ({
|
||||||
return () => AppState.removeEventListener('change', handleAppStateChange)
|
return () => AppState.removeEventListener('change', handleAppStateChange)
|
||||||
})
|
})
|
||||||
|
|
||||||
const queryKey: store.QueryKey = [
|
const queryKey: App.QueryKey = [
|
||||||
page,
|
page,
|
||||||
{ page, hashtag, list, toot, account }
|
{ page, hashtag, list, toot, account }
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { Dimensions, FlatList, View } from 'react-native'
|
import { Dimensions, FlatList, Text, View } from 'react-native'
|
||||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
import SegmentedControl from '@react-native-community/segmented-control'
|
import SegmentedControl from '@react-native-community/segmented-control'
|
||||||
import { Feather } from '@expo/vector-icons'
|
import { Feather } from '@expo/vector-icons'
|
||||||
|
|
||||||
import Timeline from './Timeline'
|
import Timeline from './Timeline'
|
||||||
import sharedScreens from 'src/stacks/Shared/sharedScreens'
|
import sharedScreens from 'src/stacks/Shared/sharedScreens'
|
||||||
|
import { getLocalRegistered } from './instancesSlice'
|
||||||
|
import store from './store'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator()
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
const Page = ({ item: { page } }: { item: { page: store.TimelinePage } }) => {
|
const Page = ({ item: { page } }: { item: { page: App.Pages } }) => {
|
||||||
return (
|
return (
|
||||||
<View style={{ width: Dimensions.get('window').width }}>
|
<View style={{ width: Dimensions.get('window').width }}>
|
||||||
<Timeline page={page} />
|
<Timeline page={page} />
|
||||||
|
@ -19,10 +21,11 @@ const Page = ({ item: { page } }: { item: { page: store.TimelinePage } }) => {
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
name: string
|
name: string
|
||||||
content: { title: string; page: string }[]
|
content: { title: string; page: App.Pages }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const TimelinesCombined: React.FC<Props> = ({ name, content }) => {
|
const TimelinesCombined: React.FC<Props> = ({ name, content }) => {
|
||||||
|
const localRegistered = getLocalRegistered(store.getState())
|
||||||
const [segment, setSegment] = useState(0)
|
const [segment, setSegment] = useState(0)
|
||||||
const [renderHeader, setRenderHeader] = useState(false)
|
const [renderHeader, setRenderHeader] = useState(false)
|
||||||
const [segmentManuallyTriggered, setSegmentManuallyTriggered] = useState(
|
const [segmentManuallyTriggered, setSegmentManuallyTriggered] = useState(
|
||||||
|
@ -68,7 +71,13 @@ const TimelinesCombined: React.FC<Props> = ({ name, content }) => {
|
||||||
data={content}
|
data={content}
|
||||||
keyExtractor={({ page }) => page}
|
keyExtractor={({ page }) => page}
|
||||||
renderItem={({ item, index }) => {
|
renderItem={({ item, index }) => {
|
||||||
return <Page key={index} item={item} />
|
return localRegistered || item.page === 'RemotePublic' ? (
|
||||||
|
<Page key={index} item={item} />
|
||||||
|
) : (
|
||||||
|
<View style={{ width: Dimensions.get('window').width }}>
|
||||||
|
<Text>请先登录</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
ref={horizontalPaging}
|
ref={horizontalPaging}
|
||||||
bounces={false}
|
bounces={false}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { createSlice } from '@reduxjs/toolkit'
|
|
||||||
|
|
||||||
const instanceInfoSlice = createSlice({
|
|
||||||
name: 'instanceInfo',
|
|
||||||
initialState: {},
|
|
||||||
reducers: {
|
|
||||||
// increment (state) {
|
|
||||||
// state.value++
|
|
||||||
// },
|
|
||||||
// decrement (state) {
|
|
||||||
// state.value--
|
|
||||||
// },
|
|
||||||
// incrementByAmount (state, action) {
|
|
||||||
// state.value += action.payload
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// export const getCurrent = state => state.current
|
|
||||||
// export const getCurrentToken = state => state.currentToken
|
|
||||||
// export const getRemote = state => state.remote
|
|
||||||
|
|
||||||
// export const { increment, decrement, incrementByAmount } = counterSlice.actions
|
|
||||||
export default instanceInfoSlice.reducer
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
|
import client from 'src/api/client'
|
||||||
|
import { RootState } from './store'
|
||||||
|
|
||||||
|
type InstancesState = {
|
||||||
|
local: {
|
||||||
|
url: string | undefined
|
||||||
|
token: string | undefined
|
||||||
|
account: {
|
||||||
|
id: Mastodon.Account['id'] | undefined
|
||||||
|
preferences: Mastodon.Preferences
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remote: {
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateLocal = createAsyncThunk(
|
||||||
|
'instances/updateLocal',
|
||||||
|
async ({
|
||||||
|
url,
|
||||||
|
token
|
||||||
|
}: {
|
||||||
|
url: InstancesState['local']['url']
|
||||||
|
token: InstancesState['local']['token']
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
body: { id }
|
||||||
|
} = await client({
|
||||||
|
method: 'get',
|
||||||
|
instance: 'remote',
|
||||||
|
instanceUrl: url,
|
||||||
|
endpoint: `accounts/verify_credentials`,
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
|
||||||
|
const { body: preferences } = await client({
|
||||||
|
method: 'get',
|
||||||
|
instance: 'remote',
|
||||||
|
instanceUrl: url,
|
||||||
|
endpoint: `preferences`,
|
||||||
|
headers: { Authorization: `Bearer ${token}` }
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
token,
|
||||||
|
account: {
|
||||||
|
id,
|
||||||
|
preferences
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const instancesSlice = createSlice({
|
||||||
|
name: 'instances',
|
||||||
|
initialState: {
|
||||||
|
local: {
|
||||||
|
url: undefined,
|
||||||
|
token: undefined,
|
||||||
|
account: {
|
||||||
|
id: undefined,
|
||||||
|
preferences: {
|
||||||
|
'posting:default:visibility': undefined,
|
||||||
|
'posting:default:sensitive': undefined,
|
||||||
|
'posting:default:language': undefined,
|
||||||
|
'reading:expand:media': undefined,
|
||||||
|
'reading:expand:spoilers': undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remote: {
|
||||||
|
url: 'mastodon.social'
|
||||||
|
}
|
||||||
|
} as InstancesState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: builder => {
|
||||||
|
builder.addCase(updateLocal.fulfilled, (state, action) => {
|
||||||
|
state.local = action.payload
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getLocalRegistered = (state: RootState) =>
|
||||||
|
state.instances.local.url !== undefined &&
|
||||||
|
state.instances.local.token !== undefined
|
||||||
|
export const getLocalUrl = (state: RootState) => state.instances.local.url
|
||||||
|
export const getLocalAccountId = (state: RootState) =>
|
||||||
|
state.instances.local.account.id
|
||||||
|
export const getLocalAccountPreferences = (state: RootState) =>
|
||||||
|
state.instances.local.account.preferences
|
||||||
|
|
||||||
|
// export const {
|
||||||
|
// updateLocalInstance,
|
||||||
|
// updateLocalAccount
|
||||||
|
// } = instancesSlice.actions
|
||||||
|
export default instancesSlice.reducer
|
|
@ -1,27 +1,11 @@
|
||||||
import { configureStore } from '@reduxjs/toolkit'
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
import instanceInfoSlice from 'src/stacks/common/instanceInfoSlice'
|
import instancesSlice from 'src/stacks/common/instancesSlice'
|
||||||
|
|
||||||
// get site information from local storage and pass to reducers
|
|
||||||
const preloadedState = {
|
|
||||||
instanceInfo: {
|
|
||||||
local: 'social.xmflsct.com',
|
|
||||||
localToken: 'qjzJ0IjvZ1apsn0_wBkGcdjKgX7Dao9KEPhGwggPwAo',
|
|
||||||
localAccountId: '1',
|
|
||||||
localAccount: {
|
|
||||||
locked: false
|
|
||||||
},
|
|
||||||
remote: 'mastodon.social'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const reducer = {
|
|
||||||
instanceInfo: instanceInfoSlice
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
preloadedState,
|
reducer: {
|
||||||
reducer
|
instances: instancesSlice
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default store
|
export default store
|
||||||
|
|
|
@ -95,7 +95,7 @@ export const timelineFetch = async (
|
||||||
pinned: 'true'
|
pinned: 'true'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
let toots: mastodon.Status[] = res.body
|
let toots: Mastodon.Status[] = res.body
|
||||||
res = await client({
|
res = await client({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
instance: 'local',
|
instance: 'local',
|
||||||
|
|
98
yarn.lock
98
yarn.lock
|
@ -1797,6 +1797,11 @@ base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.3:
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||||
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
||||||
|
|
||||||
|
base64-js@^1.3.0:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
base@^0.11.1:
|
base@^0.11.1:
|
||||||
version "0.11.2"
|
version "0.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
||||||
|
@ -2148,6 +2153,13 @@ commondir@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||||
|
|
||||||
|
compare-urls@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/compare-urls/-/compare-urls-2.0.0.tgz#9b378c4abd43980a8700fffec9afb85de4df9075"
|
||||||
|
integrity sha512-eCJcWn2OYFEIqbm70ta7LQowJOOZZqq1a2YbbFCFI1uwSvj+TWMwXVn7vPR1ceFNcAIt5RSTDbwdlX82gYLTkA==
|
||||||
|
dependencies:
|
||||||
|
normalize-url "^2.0.1"
|
||||||
|
|
||||||
compare-versions@^3.4.0:
|
compare-versions@^3.4.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62"
|
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62"
|
||||||
|
@ -2656,13 +2668,6 @@ expand-brackets@^2.1.4:
|
||||||
snapdragon "^0.8.1"
|
snapdragon "^0.8.1"
|
||||||
to-regex "^3.0.1"
|
to-regex "^3.0.1"
|
||||||
|
|
||||||
expo-app-auth@~9.2.0:
|
|
||||||
version "9.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/expo-app-auth/-/expo-app-auth-9.2.0.tgz#88868639f96c4fae973b86bd8712477e2db9dc2c"
|
|
||||||
integrity sha512-aRsRTfCfpuR8n8nO22WwwwTB7JhhJv7T9ZB5f68u6NN/FTF5CnhI3m5t0lawmirNSdmK+mGL0L+gvRyVyt1WLQ==
|
|
||||||
dependencies:
|
|
||||||
invariant "^2.2.4"
|
|
||||||
|
|
||||||
expo-asset@~8.2.0:
|
expo-asset@~8.2.0:
|
||||||
version "8.2.0"
|
version "8.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-8.2.0.tgz#2f72491064c02d4de32e9187d474deb9cec33003"
|
resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-8.2.0.tgz#2f72491064c02d4de32e9187d474deb9cec33003"
|
||||||
|
@ -2674,6 +2679,19 @@ expo-asset@~8.2.0:
|
||||||
path-browserify "^1.0.0"
|
path-browserify "^1.0.0"
|
||||||
url-parse "^1.4.4"
|
url-parse "^1.4.4"
|
||||||
|
|
||||||
|
expo-auth-session@~2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-auth-session/-/expo-auth-session-2.0.0.tgz#513cabd7f0ae090b2fe6ceb6cde7362f269b6a85"
|
||||||
|
integrity sha512-B+Wu9t4aUxman9WD0abBCjhj3rXmnoei2TcEyBSGT5q/buJEFC7opwppm+g92RL37dx+uDaSPBaVZW4jbehyVw==
|
||||||
|
dependencies:
|
||||||
|
expo-constants "^9.2.0"
|
||||||
|
expo-crypto "^8.3.0"
|
||||||
|
expo-linking "~1.0.4"
|
||||||
|
expo-random "^9.0.0"
|
||||||
|
expo-web-browser "^8.5.0"
|
||||||
|
invariant "^2.2.4"
|
||||||
|
qs "6.9.1"
|
||||||
|
|
||||||
expo-av@~8.6.0:
|
expo-av@~8.6.0:
|
||||||
version "8.6.0"
|
version "8.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/expo-av/-/expo-av-8.6.0.tgz#ab434c05241c1dcdca0edd57acb03aa21a3e46cc"
|
resolved "https://registry.yarnpkg.com/expo-av/-/expo-av-8.6.0.tgz#ab434c05241c1dcdca0edd57acb03aa21a3e46cc"
|
||||||
|
@ -2682,7 +2700,7 @@ expo-av@~8.6.0:
|
||||||
lodash "^4.17.15"
|
lodash "^4.17.15"
|
||||||
nullthrows "^1.1.0"
|
nullthrows "^1.1.0"
|
||||||
|
|
||||||
expo-constants@~9.2.0:
|
expo-constants@^9.2.0, expo-constants@~9.2.0:
|
||||||
version "9.2.0"
|
version "9.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-9.2.0.tgz#e86a38793deaff9018878afac65bce2543c80a4c"
|
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-9.2.0.tgz#e86a38793deaff9018878afac65bce2543c80a4c"
|
||||||
integrity sha512-WKwiEMvBgPrEPEyZKm21UUB2KWQux9OCWf6ZDORLTln7kO3rsbaJEprfWUWTP7AxyaLMYfN+/0WFHjZc25SZWQ==
|
integrity sha512-WKwiEMvBgPrEPEyZKm21UUB2KWQux9OCWf6ZDORLTln7kO3rsbaJEprfWUWTP7AxyaLMYfN+/0WFHjZc25SZWQ==
|
||||||
|
@ -2690,6 +2708,11 @@ expo-constants@~9.2.0:
|
||||||
fbjs "1.0.0"
|
fbjs "1.0.0"
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
|
expo-crypto@^8.3.0:
|
||||||
|
version "8.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-crypto/-/expo-crypto-8.3.0.tgz#b46f09dd813f24b77edb367520b69149496db86e"
|
||||||
|
integrity sha512-PbfxihKJsAkAy5M6jfv5Eiv9pRHU1BjBa7jLcwKDH6aUiWiJNjUvfdwku/Odt/lpXDuNcyDASy4WrD/PZDHtLA==
|
||||||
|
|
||||||
expo-error-recovery@~1.3.0:
|
expo-error-recovery@~1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/expo-error-recovery/-/expo-error-recovery-1.3.0.tgz#8f4ba957c766e4b9279493eee042d37a562fb3ea"
|
resolved "https://registry.yarnpkg.com/expo-error-recovery/-/expo-error-recovery-1.3.0.tgz#8f4ba957c766e4b9279493eee042d37a562fb3ea"
|
||||||
|
@ -2749,6 +2772,13 @@ expo-permissions@~9.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-9.3.0.tgz#b90c7e9e411d306c1c7ba2bae18d5c53c201affd"
|
resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-9.3.0.tgz#b90c7e9e411d306c1c7ba2bae18d5c53c201affd"
|
||||||
integrity sha512-ylSJZVvEGJVFTKsFrUL2S6gCvFt+/o8TJ3xT4WaMjHe2/2Z7R8ng6NR47Kt54t7XBIV/SZ7DIY9uRiR7TPuNYA==
|
integrity sha512-ylSJZVvEGJVFTKsFrUL2S6gCvFt+/o8TJ3xT4WaMjHe2/2Z7R8ng6NR47Kt54t7XBIV/SZ7DIY9uRiR7TPuNYA==
|
||||||
|
|
||||||
|
expo-random@^9.0.0:
|
||||||
|
version "9.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-9.0.1.tgz#8bea5ed7fce3d7d622bee04e1ec59415b00c9726"
|
||||||
|
integrity sha512-IIxFUpu2wLRbrWeMR74sJjDDxjXk7+hY8DAxnkW6aBgK405BctT6SxXebzpYRTx9IlQ5Drf93Gdz9xMTEDWGlg==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.0"
|
||||||
|
|
||||||
expo-secure-store@~9.2.0:
|
expo-secure-store@~9.2.0:
|
||||||
version "9.2.0"
|
version "9.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-9.2.0.tgz#5c7072058f9d0dd80dd9f17732a8512a2a30c52c"
|
resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-9.2.0.tgz#5c7072058f9d0dd80dd9f17732a8512a2a30c52c"
|
||||||
|
@ -2774,6 +2804,13 @@ expo-status-bar@~1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-1.0.2.tgz#2441a77c56be31597898337b0d086981f2adefd8"
|
resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-1.0.2.tgz#2441a77c56be31597898337b0d086981f2adefd8"
|
||||||
integrity sha512-5313u744GcLzCadxIPXyTkYw77++UXv1dXCuhYDxDbtsEf93iMra7WSvzyE8a7mRQLIIPRuGnBOdrL/V1C7EOQ==
|
integrity sha512-5313u744GcLzCadxIPXyTkYw77++UXv1dXCuhYDxDbtsEf93iMra7WSvzyE8a7mRQLIIPRuGnBOdrL/V1C7EOQ==
|
||||||
|
|
||||||
|
expo-web-browser@^8.5.0:
|
||||||
|
version "8.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-8.5.0.tgz#294fac01d9d7c8e496fddeb0ca7cf58d96a1f159"
|
||||||
|
integrity sha512-ysfGgTd16gjTAcxbywR/07Pye6XUZDoQsfeo+BEzP2kRgCWGJ5ll5WfCVNxcHInp9eo8G+n5nKqhd7Fv4gX7rg==
|
||||||
|
dependencies:
|
||||||
|
compare-urls "^2.0.0"
|
||||||
|
|
||||||
expo@~39.0.4:
|
expo@~39.0.4:
|
||||||
version "39.0.4"
|
version "39.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/expo/-/expo-39.0.4.tgz#320b7453ac055fc37c64942d5ba442f4e2781993"
|
resolved "https://registry.yarnpkg.com/expo/-/expo-39.0.4.tgz#320b7453ac055fc37c64942d5ba442f4e2781993"
|
||||||
|
@ -3512,6 +3549,11 @@ is-obj@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
|
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
|
||||||
integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
|
integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
|
||||||
|
|
||||||
|
is-plain-obj@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||||
|
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
|
||||||
|
|
||||||
is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
|
||||||
|
@ -4466,6 +4508,15 @@ normalize-path@^2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
remove-trailing-separator "^1.0.1"
|
remove-trailing-separator "^1.0.1"
|
||||||
|
|
||||||
|
normalize-url@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
|
||||||
|
integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
|
||||||
|
dependencies:
|
||||||
|
prepend-http "^2.0.0"
|
||||||
|
query-string "^5.0.1"
|
||||||
|
sort-keys "^2.0.0"
|
||||||
|
|
||||||
npm-run-path@^2.0.0:
|
npm-run-path@^2.0.0:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
|
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
|
||||||
|
@ -4777,6 +4828,11 @@ pouchdb-collections@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/pouchdb-collections/-/pouchdb-collections-1.0.1.tgz#fe63a17da977611abef7cb8026cb1a9553fd8359"
|
resolved "https://registry.yarnpkg.com/pouchdb-collections/-/pouchdb-collections-1.0.1.tgz#fe63a17da977611abef7cb8026cb1a9553fd8359"
|
||||||
integrity sha1-/mOhfal3YRq+98uAJssalVP9g1k=
|
integrity sha1-/mOhfal3YRq+98uAJssalVP9g1k=
|
||||||
|
|
||||||
|
prepend-http@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||||
|
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
||||||
|
|
||||||
pretty-format@^23.6.0:
|
pretty-format@^23.6.0:
|
||||||
version "23.6.0"
|
version "23.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
|
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
|
||||||
|
@ -4846,11 +4902,25 @@ pump@^3.0.0:
|
||||||
end-of-stream "^1.1.0"
|
end-of-stream "^1.1.0"
|
||||||
once "^1.3.1"
|
once "^1.3.1"
|
||||||
|
|
||||||
|
qs@6.9.1:
|
||||||
|
version "6.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9"
|
||||||
|
integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==
|
||||||
|
|
||||||
qs@^6.5.0:
|
qs@^6.5.0:
|
||||||
version "6.9.4"
|
version "6.9.4"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
|
||||||
integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
|
integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
|
||||||
|
|
||||||
|
query-string@^5.0.1:
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb"
|
||||||
|
integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==
|
||||||
|
dependencies:
|
||||||
|
decode-uri-component "^0.2.0"
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
strict-uri-encode "^1.0.0"
|
||||||
|
|
||||||
query-string@^6.13.6:
|
query-string@^6.13.6:
|
||||||
version "6.13.6"
|
version "6.13.6"
|
||||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.13.6.tgz#e5ac7c74f2a5da43fbca0b883b4f0bafba439966"
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.13.6.tgz#e5ac7c74f2a5da43fbca0b883b4f0bafba439966"
|
||||||
|
@ -5519,6 +5589,13 @@ snapdragon@^0.8.1:
|
||||||
source-map-resolve "^0.5.0"
|
source-map-resolve "^0.5.0"
|
||||||
use "^3.1.0"
|
use "^3.1.0"
|
||||||
|
|
||||||
|
sort-keys@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
|
||||||
|
integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=
|
||||||
|
dependencies:
|
||||||
|
is-plain-obj "^1.0.0"
|
||||||
|
|
||||||
source-map-resolve@^0.5.0:
|
source-map-resolve@^0.5.0:
|
||||||
version "0.5.3"
|
version "0.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
|
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
|
||||||
|
@ -5605,6 +5682,11 @@ stream-buffers@~2.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
|
resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
|
||||||
integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=
|
integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=
|
||||||
|
|
||||||
|
strict-uri-encode@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
|
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
|
||||||
|
|
||||||
strict-uri-encode@^2.0.0:
|
strict-uri-encode@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
||||||
|
|
Loading…
Reference in New Issue