mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Basic attachment done
Switch from ky to axios
This commit is contained in:
		| @@ -14,6 +14,7 @@ | |||||||
|     "@react-navigation/bottom-tabs": "^5.10.6", |     "@react-navigation/bottom-tabs": "^5.10.6", | ||||||
|     "@react-navigation/native": "^5.8.6", |     "@react-navigation/native": "^5.8.6", | ||||||
|     "@reduxjs/toolkit": "^1.4.0", |     "@reduxjs/toolkit": "^1.4.0", | ||||||
|  |     "axios": "^0.21.0", | ||||||
|     "expo": "~39.0.4", |     "expo": "~39.0.4", | ||||||
|     "expo-auth-session": "~2.0.0", |     "expo-auth-session": "~2.0.0", | ||||||
|     "expo-av": "~8.6.0", |     "expo-av": "~8.6.0", | ||||||
| @@ -25,7 +26,6 @@ | |||||||
|     "expo-splash-screen": "~0.6.1", |     "expo-splash-screen": "~0.6.1", | ||||||
|     "expo-status-bar": "~1.0.2", |     "expo-status-bar": "~1.0.2", | ||||||
|     "i18next": "^19.8.4", |     "i18next": "^19.8.4", | ||||||
|     "ky": "^0.24.0", |  | ||||||
|     "lodash": "^4.17.20", |     "lodash": "^4.17.20", | ||||||
|     "react": "16.13.1", |     "react": "16.13.1", | ||||||
|     "react-dom": "16.13.1", |     "react-dom": "16.13.1", | ||||||
|   | |||||||
| @@ -1,37 +1,38 @@ | |||||||
|  | import axios from 'axios' | ||||||
| import { store, RootState } from 'src/store' | import { store, RootState } from 'src/store' | ||||||
| import ky from 'ky' |  | ||||||
|  |  | ||||||
| const client = async ({ | const client = async ({ | ||||||
|   version = 'v1', |  | ||||||
|   method, |   method, | ||||||
|   instance, |   instance, | ||||||
|   instanceUrl, |   instanceDomain, | ||||||
|   endpoint, |   version = 'v1', | ||||||
|  |   url, | ||||||
|  |   params, | ||||||
|   headers, |   headers, | ||||||
|   query, |   body, | ||||||
|   body |   onUploadProgress | ||||||
| }: { | }: { | ||||||
|   version?: 'v1' | 'v2' |  | ||||||
|   method: 'get' | 'post' | 'put' | 'delete' |   method: 'get' | 'post' | 'put' | 'delete' | ||||||
|   instance: 'local' | 'remote' |   instance: 'local' | 'remote' | ||||||
|   instanceUrl?: string |   instanceDomain?: string | ||||||
|   endpoint: string |   version?: 'v1' | 'v2' | ||||||
|   headers?: { [key: string]: string } |   url: string | ||||||
|   query?: { |   params?: { | ||||||
|     [key: string]: string | number | boolean |     [key: string]: string | number | boolean | ||||||
|   } |   } | ||||||
|  |   headers?: { [key: string]: string } | ||||||
|   body?: FormData |   body?: FormData | ||||||
|  |   onUploadProgress?: (progressEvent: any) => void | ||||||
| }): Promise<any> => { | }): Promise<any> => { | ||||||
|   const state: RootState['instances'] = store.getState().instances |   const state: RootState['instances'] = store.getState().instances | ||||||
|   const url = |   const domain = | ||||||
|     instance === 'remote' ? instanceUrl || state.remote.url : state.local.url |     instance === 'remote' ? instanceDomain || state.remote.url : state.local.url | ||||||
|  |  | ||||||
|   let response |   return axios({ | ||||||
|   // try { |     method, | ||||||
|   response = await ky(endpoint, { |     baseURL: `https://${domain}/api/${version}/`, | ||||||
|     method: method, |     url, | ||||||
|     prefixUrl: `https://${url}/api/${version}`, |     params, | ||||||
|     searchParams: query, |  | ||||||
|     headers: { |     headers: { | ||||||
|       'Content-Type': 'application/json', |       'Content-Type': 'application/json', | ||||||
|       ...headers, |       ...headers, | ||||||
| @@ -39,28 +40,35 @@ const client = async ({ | |||||||
|         Authorization: `Bearer ${state.local.token}` |         Authorization: `Bearer ${state.local.token}` | ||||||
|       }) |       }) | ||||||
|     }, |     }, | ||||||
|     ...(body && { body: body }), |     ...(body && { data: body }), | ||||||
|     throwHttpErrors: false |     ...(onUploadProgress && { onUploadProgress: onUploadProgress }) | ||||||
|   }) |   }) | ||||||
|   // } catch (error) { |     .then(response => | ||||||
|   //   return Promise.reject('ky error: ' + error.json()) |       Promise.resolve({ | ||||||
|   // } |         headers: response.headers, | ||||||
|   console.log('Query: /' + endpoint) |         body: response.data | ||||||
|   if (response.ok) { |       }) | ||||||
|     return Promise.resolve({ |     ) | ||||||
|       headers: response.headers, |     .catch(error => { | ||||||
|       body: await response.json() |       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('axios error', error.response) | ||||||
|  |         return Promise.reject({ | ||||||
|  |           headers: error.response.headers, | ||||||
|  |           body: error.response.data.error | ||||||
|  |         }) | ||||||
|  |       } 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('axios error', error) | ||||||
|  |         return Promise.reject() | ||||||
|  |       } else { | ||||||
|  |         console.error('axios error', error.message) | ||||||
|  |         return Promise.reject({ body: error.message }) | ||||||
|  |       } | ||||||
|     }) |     }) | ||||||
|   } else { |  | ||||||
|     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 }) |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export default client | export default client | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ const fireMutation = async ({ | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'post', |         method: 'post', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `statuses/${id}/${prevState ? 'un' : ''}${type}` |         url: `statuses/${id}/${prevState ? 'un' : ''}${type}` | ||||||
|       }) // bug in response from Mastodon |       }) // bug in response from Mastodon | ||||||
|  |  | ||||||
|       if (!res.body[stateKey] === prevState) { |       if (!res.body[stateKey] === prevState) { | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ const fireMutation = async ({ | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'post', |         method: 'post', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `accounts/${id}/${type}` |         url: `accounts/${id}/${type}` | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|       if (res.body[stateKey!] === true) { |       if (res.body[stateKey!] === true) { | ||||||
| @@ -35,8 +35,8 @@ const fireMutation = async ({ | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'post', |         method: 'post', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `reports`, |         url: `reports`, | ||||||
|         query: { |         params: { | ||||||
|           account_id: id! |           account_id: id! | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ const fireMutation = async ({ domain }: { domain: string }) => { | |||||||
|   const res = await client({ |   const res = await client({ | ||||||
|     method: 'post', |     method: 'post', | ||||||
|     instance: 'local', |     instance: 'local', | ||||||
|     endpoint: `domain_blocks`, |     url: `domain_blocks`, | ||||||
|     query: { |     params: { | ||||||
|       domain: domain! |       domain: domain! | ||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ const fireMutation = async ({ | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'post', |         method: 'post', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `statuses/${id}/${prevState ? 'un' : ''}${type}` |         url: `statuses/${id}/${prevState ? 'un' : ''}${type}` | ||||||
|       }) // bug in response from Mastodon |       }) // bug in response from Mastodon | ||||||
|  |  | ||||||
|       if (!res.body[stateKey] === prevState) { |       if (!res.body[stateKey] === prevState) { | ||||||
| @@ -37,7 +37,7 @@ const fireMutation = async ({ | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'delete', |         method: 'delete', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `statuses/${id}` |         url: `statuses/${id}` | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|       if (res.body[stateKey] === id) { |       if (res.body[stateKey] === id) { | ||||||
|   | |||||||
| @@ -29,7 +29,7 @@ const fireMutation = async ({ | |||||||
|   const res = await client({ |   const res = await client({ | ||||||
|     method: 'post', |     method: 'post', | ||||||
|     instance: 'local', |     instance: 'local', | ||||||
|     endpoint: `polls/${id}/votes`, |     url: `polls/${id}/votes`, | ||||||
|     body: formData |     body: formData | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -62,8 +62,8 @@ const Login: React.FC = () => { | |||||||
|     const res = await client({ |     const res = await client({ | ||||||
|       method: 'post', |       method: 'post', | ||||||
|       instance: 'remote', |       instance: 'remote', | ||||||
|       instanceUrl: instance, |       instanceDomain: instance, | ||||||
|       endpoint: `apps`, |       url: `apps`, | ||||||
|       body: formData |       body: formData | ||||||
|     }) |     }) | ||||||
|     if (res.body?.client_id.length > 0) { |     if (res.body?.client_id.length > 0) { | ||||||
|   | |||||||
| @@ -34,11 +34,10 @@ export type PostState = { | |||||||
|     active: boolean |     active: boolean | ||||||
|     total: number |     total: number | ||||||
|     options: { |     options: { | ||||||
|       '1': string |       '1': string | undefined | ||||||
|       '2': string |       '2': string | undefined | ||||||
|       '3': string |       '3': string | undefined | ||||||
|       '4': string |       '4': string | undefined | ||||||
|       [key: string]: string |  | ||||||
|     } |     } | ||||||
|     multiple: boolean |     multiple: boolean | ||||||
|     expire: |     expire: | ||||||
| @@ -51,12 +50,8 @@ export type PostState = { | |||||||
|       | '604800' |       | '604800' | ||||||
|       | string |       | string | ||||||
|   } |   } | ||||||
|   attachments: { |   attachments: Mastodon.Attachment[] | ||||||
|     id: string |   attachmentUploadProgress: { progress: number; aspect: number } | undefined | ||||||
|     url: string |  | ||||||
|     preview_url: string |  | ||||||
|     description: string |  | ||||||
|   }[] |  | ||||||
|   visibility: 'public' | 'unlisted' | 'private' | 'direct' |   visibility: 'public' | 'unlisted' | 'private' | 'direct' | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -82,19 +77,12 @@ export type PostAction = | |||||||
|       payload: PostState['poll'] |       payload: PostState['poll'] | ||||||
|     } |     } | ||||||
|   | { |   | { | ||||||
|       type: 'attachments/add' |       type: 'attachments' | ||||||
|       payload: { |       payload: PostState['attachments'] | ||||||
|         id: string |  | ||||||
|         url: string |  | ||||||
|         preview_url: string |  | ||||||
|         description: string |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   | { |   | { | ||||||
|       type: 'attachments/remove' |       type: 'attachmentUploadProgress' | ||||||
|       payload: { |       payload: PostState['attachmentUploadProgress'] | ||||||
|         id: string |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   | { |   | { | ||||||
|       type: 'visibility' |       type: 'visibility' | ||||||
| @@ -114,15 +102,16 @@ const postInitialState: PostState = { | |||||||
|     active: false, |     active: false, | ||||||
|     total: 2, |     total: 2, | ||||||
|     options: { |     options: { | ||||||
|       '1': '', |       '1': undefined, | ||||||
|       '2': '', |       '2': undefined, | ||||||
|       '3': '', |       '3': undefined, | ||||||
|       '4': '' |       '4': undefined | ||||||
|     }, |     }, | ||||||
|     multiple: false, |     multiple: false, | ||||||
|     expire: '86400' |     expire: '86400' | ||||||
|   }, |   }, | ||||||
|   attachments: [], |   attachments: [], | ||||||
|  |   attachmentUploadProgress: undefined, | ||||||
|   visibility: |   visibility: | ||||||
|     getLocalAccountPreferences(store.getState())[ |     getLocalAccountPreferences(store.getState())[ | ||||||
|       'posting:default:visibility' |       'posting:default:visibility' | ||||||
| @@ -140,13 +129,10 @@ const postReducer = (state: PostState, action: PostAction): PostState => { | |||||||
|       return { ...state, emoji: action.payload } |       return { ...state, emoji: action.payload } | ||||||
|     case 'poll': |     case 'poll': | ||||||
|       return { ...state, poll: action.payload } |       return { ...state, poll: action.payload } | ||||||
|     case 'attachments/add': |     case 'attachments': | ||||||
|       return { ...state, attachments: state.attachments.concat(action.payload) } |       return { ...state, attachments: action.payload } | ||||||
|     case 'attachments/remove': |     case 'attachmentUploadProgress': | ||||||
|       return { |       return { ...state, attachmentUploadProgress: action.payload } | ||||||
|         ...state, |  | ||||||
|         attachments: state.attachments.filter(a => a.id !== action.payload.id) |  | ||||||
|       } |  | ||||||
|     case 'visibility': |     case 'visibility': | ||||||
|       return { ...state, visibility: action.payload } |       return { ...state, visibility: action.payload } | ||||||
|     default: |     default: | ||||||
| @@ -186,27 +172,29 @@ const Compose: React.FC = () => { | |||||||
|       ]) |       ]) | ||||||
|     } else { |     } else { | ||||||
|       const formData = new FormData() |       const formData = new FormData() | ||||||
|  |  | ||||||
|       formData.append('status', postState.text.raw) |       formData.append('status', postState.text.raw) | ||||||
|  |  | ||||||
|       if (postState.poll.active) { |       if (postState.poll.active) { | ||||||
|         Object.values(postState.poll.options) |         Object.values(postState.poll.options) | ||||||
|           .filter(e => e.length) |           .filter(e => e?.length) | ||||||
|           .forEach(e => { |           .forEach(e => formData.append('poll[options][]', e!)) | ||||||
|             formData.append('poll[options][]', e) |  | ||||||
|           }) |  | ||||||
|         formData.append('poll[expires_in]', postState.poll.expire) |         formData.append('poll[expires_in]', postState.poll.expire) | ||||||
|         formData.append('poll[multiple]', postState.poll.multiple.toString()) |         formData.append('poll[multiple]', postState.poll.multiple.toString()) | ||||||
|       } |       } | ||||||
|       if (postState.attachments.length > 0) { |  | ||||||
|         postState.attachments.forEach(attachment => |       if (postState.attachments.length) { | ||||||
|           formData.append('media_ids[]', attachment.id) |         postState.attachments.forEach(e => | ||||||
|  |           formData.append('media_ids[]', e!.id) | ||||||
|         ) |         ) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       formData.append('visibility', postState.visibility) |       formData.append('visibility', postState.visibility) | ||||||
|  |  | ||||||
|       client({ |       client({ | ||||||
|         method: 'post', |         method: 'post', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: 'statuses', |         url: 'statuses', | ||||||
|         headers: { |         headers: { | ||||||
|           'Idempotency-Key': Date.now().toString() + Math.random().toString() |           'Idempotency-Key': Date.now().toString() + Math.random().toString() | ||||||
|         }, |         }, | ||||||
|   | |||||||
| @@ -8,6 +8,8 @@ import { | |||||||
|   Text, |   Text, | ||||||
|   TextInput |   TextInput | ||||||
| } from 'react-native' | } from 'react-native' | ||||||
|  | import { useSelector } from 'react-redux' | ||||||
|  | import { getLocalToken, getLocalUrl } from 'src/utils/slices/instancesSlice' | ||||||
| import { StyleConstants } from 'src/utils/styles/constants' | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
| import { useTheme } from 'src/utils/styles/ThemeManager' | import { useTheme } from 'src/utils/styles/ThemeManager' | ||||||
| import { PostAction, PostState } from '../Compose' | import { PostAction, PostState } from '../Compose' | ||||||
| @@ -25,6 +27,8 @@ const ComposeActions: React.FC<Props> = ({ | |||||||
|   postDispatch |   postDispatch | ||||||
| }) => { | }) => { | ||||||
|   const { theme } = useTheme() |   const { theme } = useTheme() | ||||||
|  |   const localUrl = useSelector(getLocalUrl) | ||||||
|  |   const localToken = useSelector(getLocalToken) | ||||||
|  |  | ||||||
|   const getVisibilityIcon = () => { |   const getVisibilityIcon = () => { | ||||||
|     switch (postState.visibility) { |     switch (postState.visibility) { | ||||||
| @@ -50,20 +54,30 @@ const ComposeActions: React.FC<Props> = ({ | |||||||
|       <Feather |       <Feather | ||||||
|         name='aperture' |         name='aperture' | ||||||
|         size={24} |         size={24} | ||||||
|         color={postState.poll.active ? theme.secondary : theme.primary} |         color={ | ||||||
|         onPress={async () => |           postState.poll.active || postState.attachments.length >= 4 | ||||||
|           !postState.poll.active && |             ? theme.secondary | ||||||
|           (await addAttachments({ postState, postDispatch })) |             : theme.primary | ||||||
|         } |         } | ||||||
|  |         onPress={async () => { | ||||||
|  |           if (!postState.poll.active && postState.attachments.length < 4) { | ||||||
|  |             await addAttachments({ postState, postDispatch }) | ||||||
|  |           } | ||||||
|  |         }} | ||||||
|       /> |       /> | ||||||
|       <Feather |       <Feather | ||||||
|         name='bar-chart-2' |         name='bar-chart-2' | ||||||
|         size={24} |         size={24} | ||||||
|         color={ |         color={ | ||||||
|           postState.attachments.length > 0 ? theme.secondary : theme.primary |           postState.attachments.length || postState.attachmentUploadProgress | ||||||
|  |             ? theme.secondary | ||||||
|  |             : theme.primary | ||||||
|         } |         } | ||||||
|         onPress={() => { |         onPress={() => { | ||||||
|           if (postState.attachments.length === 0) { |           if ( | ||||||
|  |             !postState.attachments.length && | ||||||
|  |             !postState.attachmentUploadProgress | ||||||
|  |           ) { | ||||||
|             postDispatch({ |             postDispatch({ | ||||||
|               type: 'poll', |               type: 'poll', | ||||||
|               payload: { ...postState.poll, active: !postState.poll.active } |               payload: { ...postState.poll, active: !postState.poll.active } | ||||||
|   | |||||||
| @@ -1,8 +1,15 @@ | |||||||
| import React, { Dispatch } from 'react' | import React, { Dispatch, useCallback } from 'react' | ||||||
| import { Image, StyleSheet, View } from 'react-native' | import { | ||||||
|  |   ActivityIndicator, | ||||||
|  |   FlatList, | ||||||
|  |   Image, | ||||||
|  |   StyleSheet, | ||||||
|  |   View | ||||||
|  | } from 'react-native' | ||||||
| import { Feather } from '@expo/vector-icons' | import { Feather } from '@expo/vector-icons' | ||||||
|  |  | ||||||
| import { PostAction, PostState } from '../Compose' | import { PostAction, PostState } from '../Compose' | ||||||
|  | import { StyleConstants } from 'src/utils/styles/constants' | ||||||
|  |  | ||||||
| export interface Props { | export interface Props { | ||||||
|   postState: PostState |   postState: PostState | ||||||
| @@ -10,31 +17,66 @@ export interface Props { | |||||||
| } | } | ||||||
|  |  | ||||||
| const ComposeAttachments: React.FC<Props> = ({ postState, postDispatch }) => { | const ComposeAttachments: React.FC<Props> = ({ postState, postDispatch }) => { | ||||||
|  |   const renderImage = useCallback(({ item, index }) => { | ||||||
|  |     return ( | ||||||
|  |       <View key={index}> | ||||||
|  |         <Image | ||||||
|  |           style={[ | ||||||
|  |             styles.image, | ||||||
|  |             { | ||||||
|  |               width: (item.meta?.original?.aspect || 1) * 200 | ||||||
|  |             } | ||||||
|  |           ]} | ||||||
|  |           source={{ uri: item!.preview_url }} | ||||||
|  |         /> | ||||||
|  |         <Feather | ||||||
|  |           name='edit' | ||||||
|  |           size={24} | ||||||
|  |           color='white' | ||||||
|  |           style={styles.buttonEdit} | ||||||
|  |         /> | ||||||
|  |         <Feather | ||||||
|  |           name='trash-2' | ||||||
|  |           size={24} | ||||||
|  |           color='white' | ||||||
|  |           style={styles.buttonRemove} | ||||||
|  |           onPress={() => | ||||||
|  |             postDispatch({ | ||||||
|  |               type: 'attachments', | ||||||
|  |               payload: postState.attachments.filter(e => e.id !== item.id) | ||||||
|  |             }) | ||||||
|  |           } | ||||||
|  |         /> | ||||||
|  |       </View> | ||||||
|  |     ) | ||||||
|  |   }, []) | ||||||
|  |  | ||||||
|  |   const listFooter = useCallback(() => { | ||||||
|  |     return postState.attachmentUploadProgress ? ( | ||||||
|  |       <View | ||||||
|  |         style={{ | ||||||
|  |           width: postState.attachmentUploadProgress.aspect * 200, | ||||||
|  |           height: 200, | ||||||
|  |           flex: 1, | ||||||
|  |           backgroundColor: 'gray', | ||||||
|  |           marginLeft: StyleConstants.Spacing.Global.PagePadding, | ||||||
|  |           marginTop: StyleConstants.Spacing.Global.PagePadding, | ||||||
|  |           marginBottom: StyleConstants.Spacing.Global.PagePadding | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <ActivityIndicator /> | ||||||
|  |       </View> | ||||||
|  |     ) : null | ||||||
|  |   }, [postState.attachmentUploadProgress]) | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <View style={styles.base}> |     <View style={styles.base}> | ||||||
|       {postState.attachments.map((attachment, index) => ( |       <FlatList | ||||||
|         <View key={index} style={styles.imageContainer}> |         horizontal | ||||||
|           <Image |         data={postState.attachments} | ||||||
|             style={styles.image} |         renderItem={renderImage} | ||||||
|             source={{ uri: attachment.preview_url }} |         ListFooterComponent={listFooter} | ||||||
|           /> |       /> | ||||||
|           <Feather |  | ||||||
|             name='edit' |  | ||||||
|             size={24} |  | ||||||
|             color='white' |  | ||||||
|             style={styles.buttonEdit} |  | ||||||
|           /> |  | ||||||
|           <Feather |  | ||||||
|             name='trash-2' |  | ||||||
|             size={24} |  | ||||||
|             color='white' |  | ||||||
|             style={styles.buttonRemove} |  | ||||||
|             onPress={() => |  | ||||||
|               postDispatch({ type: 'attachments/remove', payload: attachment }) |  | ||||||
|             } |  | ||||||
|           /> |  | ||||||
|         </View> |  | ||||||
|       ))} |  | ||||||
|     </View> |     </View> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| @@ -43,13 +85,14 @@ const styles = StyleSheet.create({ | |||||||
|   base: { |   base: { | ||||||
|     flex: 1, |     flex: 1, | ||||||
|     flexDirection: 'row', |     flexDirection: 'row', | ||||||
|     backgroundColor: 'lightgreen' |     marginRight: StyleConstants.Spacing.Global.PagePadding, | ||||||
|   }, |     height: 200 | ||||||
|   imageContainer: { |  | ||||||
|     flexBasis: 100 |  | ||||||
|   }, |   }, | ||||||
|   image: { |   image: { | ||||||
|     flex: 1 |     flex: 1, | ||||||
|  |     marginLeft: StyleConstants.Spacing.Global.PagePadding, | ||||||
|  |     marginTop: StyleConstants.Spacing.Global.PagePadding, | ||||||
|  |     marginBottom: StyleConstants.Spacing.Global.PagePadding | ||||||
|   }, |   }, | ||||||
|   buttonEdit: { |   buttonEdit: { | ||||||
|     position: 'absolute', |     position: 'absolute', | ||||||
|   | |||||||
| @@ -73,7 +73,7 @@ const ComposeRoot: React.FC<Props> = ({ postState, postDispatch }) => { | |||||||
|  |  | ||||||
|   const textInputRef = useRef<TextInput>(null) |   const textInputRef = useRef<TextInput>(null) | ||||||
|  |  | ||||||
|   const listFooter = useMemo(() => { |   const listFooter = () => { | ||||||
|     return ( |     return ( | ||||||
|       <> |       <> | ||||||
|         {postState.emoji.active && ( |         {postState.emoji.active && ( | ||||||
| @@ -86,7 +86,8 @@ const ComposeRoot: React.FC<Props> = ({ postState, postDispatch }) => { | |||||||
|           </View> |           </View> | ||||||
|         )} |         )} | ||||||
|  |  | ||||||
|         {postState.attachments.length > 0 && ( |         {(postState.attachments.length > 0 || | ||||||
|  |           postState.attachmentUploadProgress) && ( | ||||||
|           <View style={styles.attachments}> |           <View style={styles.attachments}> | ||||||
|             <ComposeAttachments |             <ComposeAttachments | ||||||
|               postState={postState} |               postState={postState} | ||||||
| @@ -101,11 +102,7 @@ const ComposeRoot: React.FC<Props> = ({ postState, postDispatch }) => { | |||||||
|         )} |         )} | ||||||
|       </> |       </> | ||||||
|     ) |     ) | ||||||
|   }, [ |   } | ||||||
|     postState.emoji.active, |  | ||||||
|     postState.attachments.length, |  | ||||||
|     postState.poll.active |  | ||||||
|   ]) |  | ||||||
|  |  | ||||||
|   const listEmpty = useMemo(() => { |   const listEmpty = useMemo(() => { | ||||||
|     if (isFetching) { |     if (isFetching) { | ||||||
| @@ -215,8 +212,7 @@ const styles = StyleSheet.create({ | |||||||
|   contentView: { flex: 1 }, |   contentView: { flex: 1 }, | ||||||
|  |  | ||||||
|   attachments: { |   attachments: { | ||||||
|     flex: 1, |     flex: 1 | ||||||
|     height: 100 |  | ||||||
|   }, |   }, | ||||||
|   poll: { |   poll: { | ||||||
|     flex: 1, |     flex: 1, | ||||||
|   | |||||||
| @@ -1,35 +1,55 @@ | |||||||
| import { Dispatch } from 'react' | import { Dispatch } from 'react' | ||||||
| import { ActionSheetIOS, Alert } from 'react-native' | import { ActionSheetIOS, Alert } from 'react-native' | ||||||
| import * as ImagePicker from 'expo-image-picker' | import * as ImagePicker from 'expo-image-picker' | ||||||
| import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types' |  | ||||||
|  |  | ||||||
| import { PostAction, PostState } from '../Compose' | import { PostAction, PostState } from '../Compose' | ||||||
| import client from 'src/api/client' | import client from 'src/api/client' | ||||||
|  | import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types' | ||||||
|  |  | ||||||
| const uploadAttachment = async (uri: ImageInfo['uri']) => { | const uploadAttachment = async ({ | ||||||
|   const filename = uri.split('/').pop() |   result, | ||||||
|  |   postState, | ||||||
|  |   postDispatch | ||||||
|  | }: { | ||||||
|  |   result: ImageInfo | ||||||
|  |   postState: PostState | ||||||
|  |   postDispatch: Dispatch<PostAction> | ||||||
|  | }) => { | ||||||
|  |   const filename = result.uri.split('/').pop() | ||||||
|  |  | ||||||
|   const match = /\.(\w+)$/.exec(filename!) |   const match = /\.(\w+)$/.exec(filename!) | ||||||
|   const type = match ? `image/${match[1]}` : `image` |   const type = match ? `image/${match[1]}` : `image` | ||||||
|  |  | ||||||
|   const formData = new FormData() |   const formData = new FormData() | ||||||
|   formData.append('file', { uri: uri, name: filename, type: type }) |   // @ts-ignore | ||||||
|  |   formData.append('file', { uri: result.uri, name: filename, type: type }) | ||||||
|  |  | ||||||
|   return client({ |   client({ | ||||||
|     method: 'post', |     method: 'post', | ||||||
|     instance: 'local', |     instance: 'local', | ||||||
|     endpoint: 'media', |     url: 'media', | ||||||
|     body: formData |     body: formData, | ||||||
|  |     onUploadProgress: p => { | ||||||
|  |       postDispatch({ | ||||||
|  |         type: 'attachmentUploadProgress', | ||||||
|  |         payload: { | ||||||
|  |           progress: p.loaded / p.total, | ||||||
|  |           aspect: result.width / result.height | ||||||
|  |         } | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|   }) |   }) | ||||||
|     .then(res => { |     .then(({ body }: { body: Mastodon.Attachment }) => { | ||||||
|       if (res.body.id && res.body.type !== 'unknown') { |       postDispatch({ | ||||||
|         console.log('url: ' + res.body.preview_url) |         type: 'attachmentUploadProgress', | ||||||
|         return Promise.resolve({ |         payload: undefined | ||||||
|           id: res.body.id, |       }) | ||||||
|           url: res.body.url, |       if (body.id) { | ||||||
|           preview_url: res.body.preview_url, |         postDispatch({ | ||||||
|           description: res.body.description |           type: 'attachments', | ||||||
|  |           payload: postState.attachments.concat([body]) | ||||||
|         }) |         }) | ||||||
|  |         return Promise.resolve() | ||||||
|       } else { |       } else { | ||||||
|         Alert.alert('上传失败', '', [ |         Alert.alert('上传失败', '', [ | ||||||
|           { |           { | ||||||
| @@ -50,12 +70,11 @@ const uploadAttachment = async (uri: ImageInfo['uri']) => { | |||||||
| } | } | ||||||
|  |  | ||||||
| const addAttachments = async ({ | const addAttachments = async ({ | ||||||
|   postState, |   ...params | ||||||
|   postDispatch |  | ||||||
| }: { | }: { | ||||||
|   postState: PostState |   postState: PostState | ||||||
|   postDispatch: Dispatch<PostAction> |   postDispatch: Dispatch<PostAction> | ||||||
| }) => { | }): Promise<any> => { | ||||||
|   ActionSheetIOS.showActionSheetWithOptions( |   ActionSheetIOS.showActionSheetWithOptions( | ||||||
|     { |     { | ||||||
|       options: ['从相册选取', '现照', '取消'], |       options: ['从相册选取', '现照', '取消'], | ||||||
| @@ -63,19 +82,13 @@ const addAttachments = async ({ | |||||||
|     }, |     }, | ||||||
|     async buttonIndex => { |     async buttonIndex => { | ||||||
|       if (buttonIndex === 0) { |       if (buttonIndex === 0) { | ||||||
|         let result = await ImagePicker.launchImageLibraryAsync({ |         const result = await ImagePicker.launchImageLibraryAsync({ | ||||||
|           mediaTypes: ImagePicker.MediaTypeOptions.All, |           mediaTypes: ImagePicker.MediaTypeOptions.All, | ||||||
|           exif: false |           exif: false | ||||||
|         }) |         }) | ||||||
|  |  | ||||||
|         if (!result.cancelled) { |         if (!result.cancelled) { | ||||||
|           const response = await uploadAttachment(result.uri) |           await uploadAttachment({ result, ...params }) | ||||||
|           if (response.id) { |  | ||||||
|             postDispatch({ |  | ||||||
|               type: 'attachments/add', |  | ||||||
|               payload: response |  | ||||||
|             }) |  | ||||||
|           } |  | ||||||
|         } |         } | ||||||
|       } else if (buttonIndex === 1) { |       } else if (buttonIndex === 1) { | ||||||
|         // setResult(Math.floor(Math.random() * 100) + 1) |         // setResult(Math.floor(Math.random() * 100) + 1) | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ export const accountFetch = async (key: string, { id }: { id: string }) => { | |||||||
|   const res = await client({ |   const res = await client({ | ||||||
|     method: 'get', |     method: 'get', | ||||||
|     instance: 'local', |     instance: 'local', | ||||||
|     endpoint: `accounts/${id}` |     url: `accounts/${id}` | ||||||
|   }) |   }) | ||||||
|   return Promise.resolve(res.body) |   return Promise.resolve(res.body) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ export const emojisFetch = async () => { | |||||||
|   const res = await client({ |   const res = await client({ | ||||||
|     method: 'get', |     method: 'get', | ||||||
|     instance: 'local', |     instance: 'local', | ||||||
|     endpoint: 'custom_emojis' |     url: 'custom_emojis' | ||||||
|   }) |   }) | ||||||
|   return Promise.resolve(res.body) |   return Promise.resolve(res.body) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ export const instanceFetch = async ( | |||||||
|   const res = await client({ |   const res = await client({ | ||||||
|     method: 'get', |     method: 'get', | ||||||
|     instance: 'remote', |     instance: 'remote', | ||||||
|     instanceUrl: instance, |     instanceDomain: instance, | ||||||
|     endpoint: `instance` |     url: `instance` | ||||||
|   }) |   }) | ||||||
|   return Promise.resolve(res.body) |   return Promise.resolve(res.body) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ export const listsFetch = async () => { | |||||||
|   const res = await client({ |   const res = await client({ | ||||||
|     method: 'get', |     method: 'get', | ||||||
|     instance: 'local', |     instance: 'local', | ||||||
|     endpoint: 'lists' |     url: 'lists' | ||||||
|   }) |   }) | ||||||
|   return Promise.resolve(res.body) |   return Promise.resolve(res.body) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,8 +16,8 @@ export const searchFetch = async ( | |||||||
|     version: 'v2', |     version: 'v2', | ||||||
|     method: 'get', |     method: 'get', | ||||||
|     instance: 'local', |     instance: 'local', | ||||||
|     endpoint: 'search', |     url: 'search', | ||||||
|     query: { type, q: term, limit } |     params: { type, q: term, limit } | ||||||
|   }) |   }) | ||||||
|   return Promise.resolve(res.body) |   return Promise.resolve(res.body) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,14 +6,14 @@ export const timelineFetch = async ( | |||||||
|   key: string, |   key: string, | ||||||
|   { |   { | ||||||
|     page, |     page, | ||||||
|     query = {}, |     params = {}, | ||||||
|     account, |     account, | ||||||
|     hashtag, |     hashtag, | ||||||
|     list, |     list, | ||||||
|     toot |     toot | ||||||
|   }: { |   }: { | ||||||
|     page: string |     page: string | ||||||
|     query?: { |     params?: { | ||||||
|       [key: string]: string | number | boolean |       [key: string]: string | number | boolean | ||||||
|     } |     } | ||||||
|     account?: string |     account?: string | ||||||
| @@ -31,10 +31,10 @@ export const timelineFetch = async ( | |||||||
|   if (pagination && pagination.id) { |   if (pagination && pagination.id) { | ||||||
|     switch (pagination.direction) { |     switch (pagination.direction) { | ||||||
|       case 'prev': |       case 'prev': | ||||||
|         query.min_id = pagination.id |         params.min_id = pagination.id | ||||||
|         break |         break | ||||||
|       case 'next': |       case 'next': | ||||||
|         query.max_id = pagination.id |         params.max_id = pagination.id | ||||||
|         break |         break | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -44,18 +44,18 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: 'timelines/home', |         url: 'timelines/home', | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
|     case 'Local': |     case 'Local': | ||||||
|       query.local = 'true' |       params.local = 'true' | ||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: 'timelines/public', |         url: 'timelines/public', | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -63,8 +63,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: 'timelines/public', |         url: 'timelines/public', | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -72,8 +72,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'remote', |         instance: 'remote', | ||||||
|         endpoint: 'timelines/public', |         url: 'timelines/public', | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -81,8 +81,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: 'notifications', |         url: 'notifications', | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -90,8 +90,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `accounts/${account}/statuses`, |         url: `accounts/${account}/statuses`, | ||||||
|         query: { |         params: { | ||||||
|           pinned: 'true' |           pinned: 'true' | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
| @@ -99,8 +99,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `accounts/${account}/statuses`, |         url: `accounts/${account}/statuses`, | ||||||
|         query: { |         params: { | ||||||
|           exclude_replies: 'true' |           exclude_replies: 'true' | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
| @@ -111,8 +111,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `accounts/${account}/statuses`, |         url: `accounts/${account}/statuses`, | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -120,8 +120,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `accounts/${account}/statuses`, |         url: `accounts/${account}/statuses`, | ||||||
|         query: { |         params: { | ||||||
|           only_media: 'true' |           only_media: 'true' | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
| @@ -131,8 +131,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `timelines/tag/${hashtag}`, |         url: `timelines/tag/${hashtag}`, | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -140,8 +140,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `conversations`, |         url: `conversations`, | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -149,8 +149,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `bookmarks`, |         url: `bookmarks`, | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -158,8 +158,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `favourites`, |         url: `favourites`, | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -167,8 +167,8 @@ export const timelineFetch = async ( | |||||||
|       res = await client({ |       res = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `timelines/list/${list}`, |         url: `timelines/list/${list}`, | ||||||
|         query |         params | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ toots: res.body, pointer: null }) |       return Promise.resolve({ toots: res.body, pointer: null }) | ||||||
|  |  | ||||||
| @@ -176,12 +176,12 @@ export const timelineFetch = async ( | |||||||
|       const current = await client({ |       const current = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `statuses/${toot}` |         url: `statuses/${toot}` | ||||||
|       }) |       }) | ||||||
|       const context = await client({ |       const context = await client({ | ||||||
|         method: 'get', |         method: 'get', | ||||||
|         instance: 'local', |         instance: 'local', | ||||||
|         endpoint: `statuses/${toot}/context` |         url: `statuses/${toot}/context` | ||||||
|       }) |       }) | ||||||
|       return Promise.resolve({ |       return Promise.resolve({ | ||||||
|         toots: [ |         toots: [ | ||||||
|   | |||||||
| @@ -50,16 +50,16 @@ export const updateLocal = createAsyncThunk( | |||||||
|     } = await client({ |     } = await client({ | ||||||
|       method: 'get', |       method: 'get', | ||||||
|       instance: 'remote', |       instance: 'remote', | ||||||
|       instanceUrl: url, |       instanceDomain: url, | ||||||
|       endpoint: `accounts/verify_credentials`, |       url: `accounts/verify_credentials`, | ||||||
|       headers: { Authorization: `Bearer ${token}` } |       headers: { Authorization: `Bearer ${token}` } | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
|     const { body: preferences } = await client({ |     const { body: preferences } = await client({ | ||||||
|       method: 'get', |       method: 'get', | ||||||
|       instance: 'remote', |       instance: 'remote', | ||||||
|       instanceUrl: url, |       instanceDomain: url, | ||||||
|       endpoint: `preferences`, |       url: `preferences`, | ||||||
|       headers: { Authorization: `Bearer ${token}` } |       headers: { Authorization: `Bearer ${token}` } | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
| @@ -91,14 +91,11 @@ const instancesSlice = createSlice({ | |||||||
| }) | }) | ||||||
|  |  | ||||||
| export const getLocalUrl = (state: RootState) => state.instances.local.url | export const getLocalUrl = (state: RootState) => state.instances.local.url | ||||||
|  | export const getLocalToken = (state: RootState) => state.instances.local.token | ||||||
| export const getRemoteUrl = (state: RootState) => state.instances.remote.url | export const getRemoteUrl = (state: RootState) => state.instances.remote.url | ||||||
| export const getLocalAccountId = (state: RootState) => | export const getLocalAccountId = (state: RootState) => | ||||||
|   state.instances.local.account.id |   state.instances.local.account.id | ||||||
| export const getLocalAccountPreferences = (state: RootState) => | export const getLocalAccountPreferences = (state: RootState) => | ||||||
|   state.instances.local.account.preferences |   state.instances.local.account.preferences | ||||||
|  |  | ||||||
| // export const { |  | ||||||
| //   updateLocalInstance, |  | ||||||
| //   updateLocalAccount |  | ||||||
| // } = instancesSlice.actions |  | ||||||
| export default instancesSlice.reducer | export default instancesSlice.reducer | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -1740,6 +1740,13 @@ available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: | |||||||
|   dependencies: |   dependencies: | ||||||
|     array-filter "^1.0.0" |     array-filter "^1.0.0" | ||||||
|  |  | ||||||
|  | axios@^0.21.0: | ||||||
|  |   version "0.21.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.0.tgz#26df088803a2350dff2c27f96fef99fe49442aca" | ||||||
|  |   integrity sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw== | ||||||
|  |   dependencies: | ||||||
|  |     follow-redirects "^1.10.0" | ||||||
|  |  | ||||||
| babel-plugin-dynamic-import-node@^2.3.3: | babel-plugin-dynamic-import-node@^2.3.3: | ||||||
|   version "2.3.3" |   version "2.3.3" | ||||||
|   resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" |   resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" | ||||||
| @@ -3060,6 +3067,11 @@ find-up@^4.1.0: | |||||||
|     locate-path "^5.0.0" |     locate-path "^5.0.0" | ||||||
|     path-exists "^4.0.0" |     path-exists "^4.0.0" | ||||||
|  |  | ||||||
|  | follow-redirects@^1.10.0: | ||||||
|  |   version "1.13.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" | ||||||
|  |   integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== | ||||||
|  |  | ||||||
| fontfaceobserver@^2.1.0: | fontfaceobserver@^2.1.0: | ||||||
|   version "2.1.0" |   version "2.1.0" | ||||||
|   resolved "https://registry.yarnpkg.com/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz#e2705d293e2c585a6531c2a722905657317a2991" |   resolved "https://registry.yarnpkg.com/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz#e2705d293e2c585a6531c2a722905657317a2991" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user