Oauth does not work yet

This commit is contained in:
Zhiyuan Zheng 2020-11-20 01:41:46 +01:00
parent aecb5c5b3d
commit ab0062e73c
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
11 changed files with 263 additions and 123 deletions

View File

@ -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 })
}

View File

@ -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>

View File

@ -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

View File

@ -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>
)
}

View File

@ -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

View File

@ -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>
}

View File

@ -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>
)
}

19
src/stacks/Me/Base.tsx Normal file
View File

@ -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

View File

@ -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 {

View File

@ -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: {

View File

@ -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)
}