mirror of https://github.com/tooot-app/app
Oauth does not work yet
This commit is contained in:
parent
aecb5c5b3d
commit
ab0062e73c
|
@ -5,14 +5,16 @@ const client = async ({
|
|||
version = 'v1',
|
||||
method,
|
||||
instance,
|
||||
instanceUrl,
|
||||
endpoint,
|
||||
headers,
|
||||
query,
|
||||
body
|
||||
}: {
|
||||
version?: 'v1' | 'v2'
|
||||
method: 'get' | 'post' | 'delete'
|
||||
method: 'get' | 'post' | 'put' | 'delete'
|
||||
instance: 'local' | 'remote'
|
||||
instanceUrl?: string
|
||||
endpoint: string
|
||||
headers?: { [key: string]: string }
|
||||
query?: {
|
||||
|
@ -21,12 +23,13 @@ const client = async ({
|
|||
body?: FormData
|
||||
}): Promise<any> => {
|
||||
const state: RootState['instanceInfo'] = store.getState().instanceInfo
|
||||
const url = instanceUrl || store.getState().instanceInfo[instance]
|
||||
|
||||
let response
|
||||
// try {
|
||||
response = await ky(endpoint, {
|
||||
method: method,
|
||||
prefixUrl: `https://${state[instance]}/api/${version}`,
|
||||
prefixUrl: `https://${url}/api/${version}`,
|
||||
searchParams: query,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
@ -42,14 +45,19 @@ const client = async ({
|
|||
// return Promise.reject('ky error: ' + error.json())
|
||||
// }
|
||||
console.log('upload done')
|
||||
if (response.ok) {
|
||||
if (response?.ok) {
|
||||
console.log('returning ok')
|
||||
return Promise.resolve({
|
||||
headers: response.headers,
|
||||
body: await response.json()
|
||||
})
|
||||
} else {
|
||||
const errorResponse = await response.json()
|
||||
let errorResponse
|
||||
try {
|
||||
errorResponse = await response.json()
|
||||
} catch (error) {
|
||||
return Promise.reject({ body: 'Nothing found' })
|
||||
}
|
||||
console.error(response.status + ': ' + errorResponse.error)
|
||||
return Promise.reject({ body: errorResponse.error })
|
||||
}
|
||||
|
|
|
@ -8,6 +8,11 @@ import sharedScreens from 'src/stacks/Shared/sharedScreens'
|
|||
|
||||
const Stack = createNativeStackNavigator()
|
||||
|
||||
export type ScreenMe = {
|
||||
'Me-Base': undefined
|
||||
'Me-Authentication': undefined
|
||||
}
|
||||
|
||||
const Me: React.FC = () => {
|
||||
return (
|
||||
<Stack.Navigator>
|
||||
|
|
|
@ -2,15 +2,19 @@ import React from 'react'
|
|||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||
|
||||
import Instance from './Authentication/Instance'
|
||||
import Webview from './Authentication/Webview'
|
||||
|
||||
const Stack = createNativeStackNavigator()
|
||||
|
||||
export default function Base () {
|
||||
export type ScreenMeAuthentication = {
|
||||
'Me-Authentication-Instance': undefined
|
||||
}
|
||||
|
||||
const Base = () => {
|
||||
return (
|
||||
<Stack.Navigator>
|
||||
<Stack.Screen name='Me-Authentication-Instance' component={Instance} />
|
||||
<Stack.Screen name='Me-Authentication-Webview' component={Webview} />
|
||||
</Stack.Navigator>
|
||||
)
|
||||
}
|
||||
|
||||
export default Base
|
|
@ -1,31 +0,0 @@
|
|||
import React, { useState } from 'react'
|
||||
import { Button, TextInput, View } from 'react-native'
|
||||
|
||||
export default function Instance ({ navigation }) {
|
||||
const [instance, onChangeInstance] = useState()
|
||||
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
|
||||
onChangeText={text => onChangeInstance(text)}
|
||||
value={instance}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
clearButtonMode='unless-editing'
|
||||
keyboardType='url'
|
||||
textContentType='URL'
|
||||
placeholder='输入服务器'
|
||||
placeholderTextColor='#888888'
|
||||
/>
|
||||
<Button
|
||||
title='登录'
|
||||
onPress={() =>
|
||||
navigation.navigate('Me-Authentication-Webview', {
|
||||
instance: instance
|
||||
})
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
import React, { useCallback, useState } from 'react'
|
||||
import { Button, Text, TextInput, View } from 'react-native'
|
||||
import { StackNavigationProp } from '@react-navigation/stack'
|
||||
import { useQuery } from 'react-query'
|
||||
import { debounce } from 'lodash'
|
||||
|
||||
import { instanceFetch } from 'src/stacks/common/instanceFetch'
|
||||
import { ScreenMeAuthentication } from '../Authentication'
|
||||
import client from 'src/api/client'
|
||||
import * as AppAuth from 'expo-app-auth'
|
||||
|
||||
const Instance: React.FC = () => {
|
||||
const [instance, setInstance] = useState('')
|
||||
|
||||
const { isSuccess, refetch, data } = useQuery(
|
||||
['Instance', { instance }],
|
||||
instanceFetch,
|
||||
{
|
||||
enabled: false,
|
||||
retry: false
|
||||
}
|
||||
)
|
||||
|
||||
const onChangeText = useCallback(
|
||||
debounce(
|
||||
text => {
|
||||
setInstance(text)
|
||||
refetch()
|
||||
},
|
||||
1000,
|
||||
{
|
||||
leading: true
|
||||
}
|
||||
),
|
||||
[]
|
||||
)
|
||||
|
||||
const signInAsync = async (id: string) => {
|
||||
let authState = await AppAuth.authAsync({
|
||||
issuer: `https://${instance}`,
|
||||
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()
|
||||
formData.append('client_name', 'test_dudu')
|
||||
formData.append('redirect_uris', 'exp://127.0.0.1:19000')
|
||||
formData.append('scopes', 'read write follow push')
|
||||
|
||||
const res = await client({
|
||||
method: 'post',
|
||||
instance: 'remote',
|
||||
instanceUrl: instance,
|
||||
endpoint: `apps`,
|
||||
body: formData
|
||||
})
|
||||
if (res.body?.client_id.length > 0) {
|
||||
return Promise.resolve(res.body)
|
||||
} else {
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
|
||||
const oauthFlow = async () => {
|
||||
const applicationData = await oauthCreateApplication()
|
||||
if (applicationData.client_id.length > 0) {
|
||||
await signInAsync(applicationData.client_id)
|
||||
} else {
|
||||
console.error('Application data error')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
|
||||
onChangeText={onChangeText}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
clearButtonMode='unless-editing'
|
||||
keyboardType='url'
|
||||
textContentType='URL'
|
||||
placeholder='输入服务器'
|
||||
placeholderTextColor='#888888'
|
||||
/>
|
||||
<Button
|
||||
title='登录'
|
||||
disabled={!data?.uri}
|
||||
onPress={async () => await oauthFlow()}
|
||||
/>
|
||||
{isSuccess && data && data.uri && (
|
||||
<View>
|
||||
<Text>{data.title}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default Instance
|
|
@ -1,9 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Button, View } from 'react-native'
|
||||
import * as AppAuth from 'expo-app-auth'
|
||||
|
||||
export default function Webview ({ navigation, route }) {
|
||||
const { instance } = route.params
|
||||
|
||||
return <View></View>
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Button, View } from 'react-native'
|
||||
|
||||
export default function Base ({ navigation: { navigate } }) {
|
||||
return (
|
||||
<View>
|
||||
<Button title='登录' onPress={() => navigate('Me-Authentication')} />
|
||||
</View>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react'
|
||||
import { Button, View } from 'react-native'
|
||||
import { StackNavigationProp } from '@react-navigation/stack'
|
||||
|
||||
import { ScreenMe } from '../Me'
|
||||
|
||||
export interface Props {
|
||||
navigation: StackNavigationProp<ScreenMe, 'Me-Base'>
|
||||
}
|
||||
|
||||
const Base: React.FC<Props> = ({ navigation: { navigate } }) => {
|
||||
return (
|
||||
<View>
|
||||
<Button title='登录' onPress={() => navigate('Me-Authentication')} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default Base
|
|
@ -204,6 +204,12 @@ const PostToot: React.FC = () => {
|
|||
formData.append('poll[expires_in]', postState.poll.expire)
|
||||
formData.append('poll[multiple]', postState.poll.multiple.toString())
|
||||
}
|
||||
if (postState.attachments.length > 0) {
|
||||
postState.attachments.forEach(attachment =>
|
||||
formData.append('media_ids[]', attachment.id)
|
||||
)
|
||||
}
|
||||
formData.append('visibility', postState.visibility)
|
||||
|
||||
client({
|
||||
method: 'post',
|
||||
|
@ -220,7 +226,10 @@ const PostToot: React.FC = () => {
|
|||
Alert.alert('发布成功', '', [
|
||||
{
|
||||
text: '好的',
|
||||
onPress: () => navigation.goBack()
|
||||
onPress: () => {
|
||||
// clear homepage cache
|
||||
navigation.goBack()
|
||||
}
|
||||
}
|
||||
])
|
||||
} else {
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
ActionSheetIOS,
|
||||
Keyboard,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
|
@ -166,80 +167,93 @@ const PostMain: React.FC<Props> = ({ postState, postDispatch }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<TextInput
|
||||
style={[
|
||||
styles.textInput,
|
||||
{
|
||||
flex: postState.overlay ? 0 : 1,
|
||||
minHeight: editorMinHeight + 14
|
||||
}
|
||||
]}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
autoFocus
|
||||
enablesReturnKeyAutomatically
|
||||
multiline
|
||||
placeholder='想说点什么'
|
||||
onChangeText={content => onChangeText({ content })}
|
||||
onContentSizeChange={({ nativeEvent }) => {
|
||||
setEditorMinHeight(nativeEvent.contentSize.height)
|
||||
}}
|
||||
onSelectionChange={({
|
||||
nativeEvent: {
|
||||
selection: { start, end }
|
||||
}
|
||||
}) => {
|
||||
postDispatch({ type: 'selection', payload: { start, end } })
|
||||
}}
|
||||
scrollEnabled
|
||||
<View style={styles.base}>
|
||||
<ScrollView
|
||||
style={styles.contentView}
|
||||
alwaysBounceVertical={false}
|
||||
keyboardDismissMode='interactive'
|
||||
>
|
||||
<Text>{postState.text.formatted}</Text>
|
||||
</TextInput>
|
||||
{postState.attachments.length > 0 && (
|
||||
<View style={styles.attachments}>
|
||||
<PostAttachments postState={postState} postDispatch={postDispatch} />
|
||||
</View>
|
||||
)}
|
||||
{postState.poll.active && (
|
||||
<View style={styles.poll}>
|
||||
<PostPoll postState={postState} postDispatch={postDispatch} />
|
||||
</View>
|
||||
)}
|
||||
{postState.overlay === 'suggestions' ? (
|
||||
<View style={styles.suggestions}>
|
||||
<PostSuggestions
|
||||
onChangeText={onChangeText}
|
||||
postState={postState}
|
||||
postDispatch={postDispatch}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{postState.overlay === 'emojis' ? (
|
||||
<View style={styles.emojis}>
|
||||
<PostEmojis
|
||||
onChangeText={onChangeText}
|
||||
postState={postState}
|
||||
postDispatch={postDispatch}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<TextInput
|
||||
style={[
|
||||
styles.textInput
|
||||
// {
|
||||
// flex: postState.overlay ? 0 : 1,
|
||||
// minHeight: editorMinHeight + 14
|
||||
// }
|
||||
]}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
autoFocus
|
||||
enablesReturnKeyAutomatically
|
||||
multiline
|
||||
placeholder='想说点什么'
|
||||
onChangeText={content => onChangeText({ content })}
|
||||
onContentSizeChange={({ nativeEvent }) => {
|
||||
setEditorMinHeight(nativeEvent.contentSize.height)
|
||||
}}
|
||||
onSelectionChange={({
|
||||
nativeEvent: {
|
||||
selection: { start, end }
|
||||
}
|
||||
}) => {
|
||||
postDispatch({ type: 'selection', payload: { start, end } })
|
||||
}}
|
||||
scrollEnabled
|
||||
>
|
||||
<Text>{postState.text.formatted}</Text>
|
||||
</TextInput>
|
||||
{postState.attachments.length > 0 && (
|
||||
<View style={styles.attachments}>
|
||||
<PostAttachments
|
||||
postState={postState}
|
||||
postDispatch={postDispatch}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{postState.poll.active && (
|
||||
<View style={styles.poll}>
|
||||
<PostPoll postState={postState} postDispatch={postDispatch} />
|
||||
</View>
|
||||
)}
|
||||
{postState.overlay === 'suggestions' ? (
|
||||
<View style={styles.suggestions}>
|
||||
<PostSuggestions
|
||||
onChangeText={onChangeText}
|
||||
postState={postState}
|
||||
postDispatch={postDispatch}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{postState.overlay === 'emojis' ? (
|
||||
<View style={styles.emojis}>
|
||||
<PostEmojis
|
||||
onChangeText={onChangeText}
|
||||
postState={postState}
|
||||
postDispatch={postDispatch}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</ScrollView>
|
||||
<Pressable style={styles.additions} onPress={() => Keyboard.dismiss()}>
|
||||
<Feather
|
||||
name='paperclip'
|
||||
size={24}
|
||||
color={postState.poll.active ? 'gray' : 'black'}
|
||||
onPress={async () =>
|
||||
await addAttachments({ postState, postDispatch })
|
||||
!postState.poll.active &&
|
||||
(await addAttachments({ postState, postDispatch }))
|
||||
}
|
||||
/>
|
||||
<Feather
|
||||
name='bar-chart-2'
|
||||
size={24}
|
||||
color={postState.attachments.length > 0 ? 'gray' : 'black'}
|
||||
onPress={() =>
|
||||
postState.attachments.length === 0 &&
|
||||
postDispatch({
|
||||
type: 'poll',
|
||||
payload: { ...postState.poll, active: !postState.poll.active }
|
||||
|
@ -295,18 +309,22 @@ const PostMain: React.FC<Props> = ({ postState, postDispatch }) => {
|
|||
// (PostEmojis as any).whyDidYouRender = true
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
main: {
|
||||
base: {
|
||||
flex: 1
|
||||
},
|
||||
textInput: {
|
||||
contentView: {
|
||||
flex: 1,
|
||||
backgroundColor: 'gray'
|
||||
},
|
||||
textInput: {
|
||||
backgroundColor: 'lightgray',
|
||||
paddingBottom: 20
|
||||
},
|
||||
attachments: {
|
||||
flex: 1,
|
||||
height: 100
|
||||
},
|
||||
poll: {
|
||||
flex: 1,
|
||||
height: 100
|
||||
},
|
||||
suggestions: {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import client from 'src/api/client'
|
||||
|
||||
export const instanceFetch = async (
|
||||
key: string,
|
||||
{ instance }: { instance: string }
|
||||
) => {
|
||||
const res = await client({
|
||||
method: 'get',
|
||||
instance: 'remote',
|
||||
instanceUrl: instance,
|
||||
endpoint: `instance`
|
||||
})
|
||||
return Promise.resolve(res.body)
|
||||
}
|
Loading…
Reference in New Issue