mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Use ky instead of fetch
				
					
				
			This commit is contained in:
		
							
								
								
									
										67
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										67
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -4631,6 +4631,11 @@ | |||||||
|         "graceful-fs": "^4.1.9" |         "graceful-fs": "^4.1.9" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "ky": { | ||||||
|  |       "version": "0.24.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/ky/-/ky-0.24.0.tgz", | ||||||
|  |       "integrity": "sha512-/vpuQguwV30jErrqLpoaU/YJAFALrUkqqWLILnSoBOj5/O/LKzro/pPNtxbLgY6m4w5XNM6YZ3v7/or8qLlFuw==" | ||||||
|  |     }, | ||||||
|     "leven": { |     "leven": { | ||||||
|       "version": "3.1.0", |       "version": "3.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", |       "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", | ||||||
| @@ -6020,6 +6025,21 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", |       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", | ||||||
|       "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" |       "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" | ||||||
|     }, |     }, | ||||||
|  |     "path-to-regexp": { | ||||||
|  |       "version": "1.8.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", | ||||||
|  |       "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", | ||||||
|  |       "requires": { | ||||||
|  |         "isarray": "0.0.1" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "isarray": { | ||||||
|  |           "version": "0.0.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", | ||||||
|  |           "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "pify": { |     "pify": { | ||||||
|       "version": "4.0.1", |       "version": "4.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", |       "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", | ||||||
| @@ -6717,6 +6737,14 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.1.4.tgz", |       "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.1.4.tgz", | ||||||
|       "integrity": "sha512-bXx3hqz4LovFoMnJIRGIWL2oJ/PHadXviBKvgZV9yNErtURQLJSn0yfQytVtiqslhaBMZOJwH4R6HiClyofvBg==" |       "integrity": "sha512-bXx3hqz4LovFoMnJIRGIWL2oJ/PHadXviBKvgZV9yNErtURQLJSn0yfQytVtiqslhaBMZOJwH4R6HiClyofvBg==" | ||||||
|     }, |     }, | ||||||
|  |     "react-native-safe-area-view": { | ||||||
|  |       "version": "0.14.9", | ||||||
|  |       "resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.14.9.tgz", | ||||||
|  |       "integrity": "sha512-WII/ulhpVyL/qbYb7vydq7dJAfZRBcEhg4/UWt6F6nAKpLa3gAceMOxBxI914ppwSP/TdUsandFy6lkJQE0z4A==", | ||||||
|  |       "requires": { | ||||||
|  |         "hoist-non-react-statics": "^2.3.1" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "react-native-screens": { |     "react-native-screens": { | ||||||
|       "version": "2.10.1", |       "version": "2.10.1", | ||||||
|       "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.10.1.tgz", |       "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.10.1.tgz", | ||||||
| @@ -6754,6 +6782,45 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "react-navigation": { | ||||||
|  |       "version": "4.4.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-4.4.3.tgz", | ||||||
|  |       "integrity": "sha512-tNBQQzbw0PVo9FLypQUUCISMcXW0wCW8oQeHtY0spWf35KC3IZHq/WcBm4E956wFsaqrDMGCUnyaVrxZNSuUGg==", | ||||||
|  |       "requires": { | ||||||
|  |         "@react-navigation/core": "^3.7.9", | ||||||
|  |         "@react-navigation/native": "^3.8.3" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "@react-navigation/core": { | ||||||
|  |           "version": "3.7.9", | ||||||
|  |           "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-3.7.9.tgz", | ||||||
|  |           "integrity": "sha512-EknbzM8OI9A5alRxXtQRV5Awle68B+z1QAxNty5DxmlS3BNfmduWNGnim159ROyqxkuDffK9L/U/Tbd45mx+Jg==", | ||||||
|  |           "requires": { | ||||||
|  |             "hoist-non-react-statics": "^3.3.2", | ||||||
|  |             "path-to-regexp": "^1.8.0", | ||||||
|  |             "query-string": "^6.13.6", | ||||||
|  |             "react-is": "^16.13.0" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "@react-navigation/native": { | ||||||
|  |           "version": "3.8.3", | ||||||
|  |           "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-3.8.3.tgz", | ||||||
|  |           "integrity": "sha512-1yLd2pi8SK3wPC58mWZ5fjW5uYr1gmMN8YwjkA2qVjyVYfzzctRkoFDu8poO5UzxEIgf/4ns6ezBtKY1Q601UQ==", | ||||||
|  |           "requires": { | ||||||
|  |             "hoist-non-react-statics": "^3.3.2", | ||||||
|  |             "react-native-safe-area-view": "^0.14.9" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "hoist-non-react-statics": { | ||||||
|  |           "version": "3.3.2", | ||||||
|  |           "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", | ||||||
|  |           "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", | ||||||
|  |           "requires": { | ||||||
|  |             "react-is": "^16.7.0" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "react-redux": { |     "react-redux": { | ||||||
|       "version": "7.2.2", |       "version": "7.2.2", | ||||||
|       "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", |       "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
									
									
									
									
								
							| @@ -12,13 +12,18 @@ | |||||||
|     "@react-native-async-storage/async-storage": "^1.13.0", |     "@react-native-async-storage/async-storage": "^1.13.0", | ||||||
|     "@react-native-community/masked-view": "0.1.10", |     "@react-native-community/masked-view": "0.1.10", | ||||||
|     "@react-native-community/segmented-control": "2.1.1", |     "@react-native-community/segmented-control": "2.1.1", | ||||||
|  |     "@react-native-community/viewpager": "4.1.6", | ||||||
|     "@react-navigation/bottom-tabs": "^5.9.2", |     "@react-navigation/bottom-tabs": "^5.9.2", | ||||||
|     "@react-navigation/native": "^5.7.6", |     "@react-navigation/native": "^5.7.6", | ||||||
|     "@react-navigation/stack": "^5.9.3", |     "@react-navigation/stack": "^5.9.3", | ||||||
|     "@reduxjs/toolkit": "^1.4.0", |     "@reduxjs/toolkit": "^1.4.0", | ||||||
|     "expo": "~39.0.2", |     "expo": "~39.0.2", | ||||||
|     "expo-app-auth": "~9.2.0", |     "expo-app-auth": "~9.2.0", | ||||||
|  |     "expo-av": "~8.6.0", | ||||||
|  |     "expo-secure-store": "~9.2.0", | ||||||
|  |     "expo-splash-screen": "~0.6.1", | ||||||
|     "expo-status-bar": "~1.0.2", |     "expo-status-bar": "~1.0.2", | ||||||
|  |     "ky": "^0.24.0", | ||||||
|     "prop-types": "^15.7.2", |     "prop-types": "^15.7.2", | ||||||
|     "react": "16.13.1", |     "react": "16.13.1", | ||||||
|     "react-dom": "16.13.1", |     "react-dom": "16.13.1", | ||||||
| @@ -33,11 +38,8 @@ | |||||||
|     "react-native-screens": "~2.10.1", |     "react-native-screens": "~2.10.1", | ||||||
|     "react-native-web": "~0.13.7", |     "react-native-web": "~0.13.7", | ||||||
|     "react-native-webview": "10.7.0", |     "react-native-webview": "10.7.0", | ||||||
|     "react-redux": "^7.2.1", |     "react-navigation": "^4.4.3", | ||||||
|     "expo-av": "~8.6.0", |     "react-redux": "^7.2.1" | ||||||
|     "expo-secure-store": "~9.2.0", |  | ||||||
|     "expo-splash-screen": "~0.6.1", |  | ||||||
|     "@react-native-community/viewpager": "4.1.6" |  | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@babel/core": "~7.9.0", |     "@babel/core": "~7.9.0", | ||||||
|   | |||||||
| @@ -1,43 +1,40 @@ | |||||||
| export async function client (url, query, { body, ...customConfig } = {}) { | import store from 'src/stacks/common/store' | ||||||
|   if (!url) { | import ky from 'ky' | ||||||
|     return Promise.reject('Missing URL.') |  | ||||||
|   } |  | ||||||
|   const headers = { 'Content-Type': 'application/json' } |  | ||||||
|  |  | ||||||
|   const config = { | export default async function client ({ | ||||||
|     method: body ? 'POST' : 'GET', |   method, // *  get / post | ||||||
|     ...customConfig, |   instance, // *  local / remote | ||||||
|     headers: { |   endpoint, // *  if url is empty | ||||||
|       ...headers, |   query, //    object | ||||||
|       ...customConfig.headers |   body //    object | ||||||
|     } | }) { | ||||||
|   } |   const state = store.getState().instanceInfo | ||||||
|  |  | ||||||
|   const queryString = query |   let response | ||||||
|     ? `?${query.map(({ key, value }) => `${key}=${value}`).join('&')}` |  | ||||||
|     : '' |  | ||||||
|  |  | ||||||
|   if (body) { |  | ||||||
|     config.body = JSON.stringify(body) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   let data |  | ||||||
|   try { |   try { | ||||||
|     const response = await fetch(`${url}${queryString}`, config) |     response = await ky(endpoint, { | ||||||
|     data = await response.json() |       method: method, | ||||||
|     if (response.ok) { |       prefixUrl: `https://${state[instance]}/api/v1`, | ||||||
|       return { headers: response.headers, body: data } |       searchParams: query, | ||||||
|     } |       headers: { | ||||||
|     throw new Error(response.statusText) |         'Content-Type': 'application/json', | ||||||
|   } catch (err) { |         ...(instance === 'local' && { | ||||||
|     return Promise.reject(err.message ? err.message : data) |           Authorization: `Bearer ${state.localToken}` | ||||||
|  |         }) | ||||||
|  |       }, | ||||||
|  |       ...(body && { json: body }) | ||||||
|  |     }) | ||||||
|  |   } catch { | ||||||
|  |     return Promise.reject('ky error') | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (response.ok) { | ||||||
|  |     return Promise.resolve({ | ||||||
|  |       headers: response.headers, | ||||||
|  |       body: await response.json() | ||||||
|  |     }) | ||||||
|  |   } else { | ||||||
|  |     console.error(response.error) | ||||||
|  |     return Promise.reject({ body: response.error_message }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| client.get = function (instance, endpoint, query, customConfig = {}) { |  | ||||||
|   return client(instance, endpoint, query, { ...customConfig, method: 'GET' }) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| client.post = function (instance, endpoint, query, body, customConfig = {}) { |  | ||||||
|   return client(instance, endpoint, query, { ...customConfig, body }) |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										61
									
								
								src/api/client.js.backup
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/api/client.js.backup
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | import store from 'src/stacks/common/store' | ||||||
|  |  | ||||||
|  | export async function client (instance, query, { body, ...customConfig } = {}) { | ||||||
|  |   const state = store.getState().instanceInfo | ||||||
|  |  | ||||||
|  |   let url | ||||||
|  |   let authHeader | ||||||
|  |   switch (instance.type) { | ||||||
|  |     case 'local': | ||||||
|  |       url = `https://${state.local}/${instance.endpoint}` | ||||||
|  |       authHeader = { | ||||||
|  |         Authorization: `Bearer ${state.localToken}` | ||||||
|  |       } | ||||||
|  |       break | ||||||
|  |     case 'remote': | ||||||
|  |       url = `https://${state.remote}/${instance.endpoint}` | ||||||
|  |       authHeader = {} | ||||||
|  |       break | ||||||
|  |     default: | ||||||
|  |       return Promise.reject('Instance type is not defined.') | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const headers = { 'Content-Type': 'application/json', ...authHeader } | ||||||
|  |  | ||||||
|  |   const config = { | ||||||
|  |     method: body ? 'POST' : 'GET', | ||||||
|  |     ...customConfig, | ||||||
|  |     headers: { | ||||||
|  |       ...headers, | ||||||
|  |       ...customConfig.headers | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const queryString = query | ||||||
|  |     ? `?${query.map(({ key, value }) => `${key}=${value}`).join('&')}` | ||||||
|  |     : '' | ||||||
|  |  | ||||||
|  |   if (body) { | ||||||
|  |     config.body = JSON.stringify(body) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   let data | ||||||
|  |   try { | ||||||
|  |     const response = await fetch(`${url}${queryString}`, config) | ||||||
|  |     data = await response.json() | ||||||
|  |     if (response.ok) { | ||||||
|  |       return { headers: response.headers, body: data } | ||||||
|  |     } | ||||||
|  |     throw new Error(response.statusText) | ||||||
|  |   } catch (err) { | ||||||
|  |     return Promise.reject(err.message ? err.message : data) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | client.get = function (instance, endpoint, query, customConfig = {}) { | ||||||
|  |   return client(instance, endpoint, query, { ...customConfig, method: 'GET' }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | client.post = function (instance, endpoint, query, body, customConfig = {}) { | ||||||
|  |   return client(instance, endpoint, query, { ...customConfig, body }) | ||||||
|  | } | ||||||
| @@ -3,7 +3,10 @@ import PropTypes from 'prop-types' | |||||||
| import { Pressable, StyleSheet, Text, View } from 'react-native' | import { Pressable, StyleSheet, Text, View } from 'react-native' | ||||||
| import { Feather } from '@expo/vector-icons' | import { Feather } from '@expo/vector-icons' | ||||||
|  |  | ||||||
|  | import action from 'src/components/action' | ||||||
|  |  | ||||||
| export default function Actions ({ | export default function Actions ({ | ||||||
|  |   id, | ||||||
|   replies_count, |   replies_count, | ||||||
|   reblogs_count, |   reblogs_count, | ||||||
|   reblogged, |   reblogged, | ||||||
| @@ -20,7 +23,7 @@ export default function Actions ({ | |||||||
|         <Feather name='repeat' /> |         <Feather name='repeat' /> | ||||||
|         <Text>{reblogs_count}</Text> |         <Text>{reblogs_count}</Text> | ||||||
|       </Pressable> |       </Pressable> | ||||||
|       <Pressable style={styles.action}> |       <Pressable style={styles.action} onPress={() => action('favourite', id)}> | ||||||
|         <Feather name='heart' /> |         <Feather name='heart' /> | ||||||
|         <Text>{favourites_count}</Text> |         <Text>{favourites_count}</Text> | ||||||
|       </Pressable> |       </Pressable> | ||||||
| @@ -34,15 +37,23 @@ export default function Actions ({ | |||||||
| const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||||||
|   actions: { |   actions: { | ||||||
|     flex: 1, |     flex: 1, | ||||||
|     flexDirection: 'row' |     flexDirection: 'row', | ||||||
|  |     marginTop: 4 | ||||||
|   }, |   }, | ||||||
|   action: { |   action: { | ||||||
|     width: '25%', |     width: '25%', | ||||||
|     flexDirection: 'row', |     flexDirection: 'row', | ||||||
|     justifyContent: 'center' |     justifyContent: 'center', | ||||||
|  |     paddingTop: 8, | ||||||
|  |     paddingBottom: 8 | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
|  |  | ||||||
| // Actions.propTypes = { | Actions.propTypes = { | ||||||
| //   uri: PropTypes.string |   id: PropTypes.string.isRequired, | ||||||
| // } |   replies_count: PropTypes.number.isRequired, | ||||||
|  |   reblogs_count: PropTypes.number.isRequired, | ||||||
|  |   reblogged: PropTypes.bool.isRequired, | ||||||
|  |   favourites_count: PropTypes.number.isRequired, | ||||||
|  |   favourited: PropTypes.bool.isRequired | ||||||
|  | } | ||||||
|   | |||||||
| @@ -35,7 +35,6 @@ export default function TootNotification ({ toot }) { | |||||||
|               account={actualAccount.acct} |               account={actualAccount.acct} | ||||||
|               created_at={toot.created_at} |               created_at={toot.created_at} | ||||||
|             /> |             /> | ||||||
|             {/* Can pass toot info to next page to speed up performance */} |  | ||||||
|             <Pressable |             <Pressable | ||||||
|               onPress={() => navigation.navigate('Toot', { toot: toot.id })} |               onPress={() => navigation.navigate('Toot', { toot: toot.id })} | ||||||
|             > |             > | ||||||
| @@ -52,7 +51,7 @@ export default function TootNotification ({ toot }) { | |||||||
|                     /> |                     /> | ||||||
|                   )} |                   )} | ||||||
|                   {toot.status.poll && <Poll poll={toot.status.poll} />} |                   {toot.status.poll && <Poll poll={toot.status.poll} />} | ||||||
|                   {toot.status.media_attachments && ( |                   {toot.status.media_attachments.length > 0 && ( | ||||||
|                     <Attachment |                     <Attachment | ||||||
|                       media_attachments={toot.status.media_attachments} |                       media_attachments={toot.status.media_attachments} | ||||||
|                       sensitive={toot.status.sensitive} |                       sensitive={toot.status.sensitive} | ||||||
| @@ -67,6 +66,7 @@ export default function TootNotification ({ toot }) { | |||||||
|             </Pressable> |             </Pressable> | ||||||
|             {toot.status && ( |             {toot.status && ( | ||||||
|               <Actions |               <Actions | ||||||
|  |                 id={toot.status.id} | ||||||
|                 replies_count={toot.status.replies_count} |                 replies_count={toot.status.replies_count} | ||||||
|                 reblogs_count={toot.status.reblogs_count} |                 reblogs_count={toot.status.reblogs_count} | ||||||
|                 reblogged={toot.status.reblogged} |                 reblogged={toot.status.reblogged} | ||||||
| @@ -78,7 +78,7 @@ export default function TootNotification ({ toot }) { | |||||||
|         </View> |         </View> | ||||||
|       </View> |       </View> | ||||||
|     ) |     ) | ||||||
|   }) |   }, [toot]) | ||||||
|  |  | ||||||
|   return tootView |   return tootView | ||||||
| } | } | ||||||
|   | |||||||
| @@ -50,9 +50,9 @@ export default function TootTimeline ({ toot }) { | |||||||
|             /> |             /> | ||||||
|             {/* Can pass toot info to next page to speed up performance */} |             {/* Can pass toot info to next page to speed up performance */} | ||||||
|             <Pressable |             <Pressable | ||||||
|               // onPress={() => |               onPress={() => | ||||||
|               //   navigation.navigate('Toot', { toot: actualContent.id }) |                 navigation.navigate('Toot', { toot: actualContent.id }) | ||||||
|               // } |               } | ||||||
|             > |             > | ||||||
|               {actualContent.content ? ( |               {actualContent.content ? ( | ||||||
|                 <Content |                 <Content | ||||||
| @@ -77,6 +77,7 @@ export default function TootTimeline ({ toot }) { | |||||||
|               {actualContent.card && <Card card={actualContent.card} />} |               {actualContent.card && <Card card={actualContent.card} />} | ||||||
|             </Pressable> |             </Pressable> | ||||||
|             <Actions |             <Actions | ||||||
|  |               id={actualContent.id} | ||||||
|               replies_count={actualContent.replies_count} |               replies_count={actualContent.replies_count} | ||||||
|               reblogs_count={actualContent.reblogs_count} |               reblogs_count={actualContent.reblogs_count} | ||||||
|               reblogged={actualContent.reblogged} |               reblogged={actualContent.reblogged} | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								src/components/action.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/components/action.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | import { Alert } from 'react-native' | ||||||
|  | import { useSelector } from 'react-redux' | ||||||
|  |  | ||||||
|  | import { client } from 'src/api/client' | ||||||
|  |  | ||||||
|  | export default async function action (type, id) { | ||||||
|  |   // If header if needed for remote server | ||||||
|  |   const header = { | ||||||
|  |     headers: { | ||||||
|  |       Authorization: `Bearer ${useSelector( | ||||||
|  |         state => state.instanceInfo.localToken | ||||||
|  |       )}` | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   const instance = `https://${useSelector( | ||||||
|  |     state => state.instanceInfo.local | ||||||
|  |   )}/api/v1/` | ||||||
|  |  | ||||||
|  |   let endpoint | ||||||
|  |   switch (type) { | ||||||
|  |     case 'favourite': | ||||||
|  |       endpoint = `${instance}statuses/${id}/favourite` | ||||||
|  |       break | ||||||
|  |     case 'unfavourite': | ||||||
|  |       endpoint = `${instance}statuses/${id}/unfavourite` | ||||||
|  |       break | ||||||
|  |     case 'reblog': | ||||||
|  |       endpoint = `${instance}statuses/${id}/reblog` | ||||||
|  |       break | ||||||
|  |     case 'unreblog': | ||||||
|  |       endpoint = `${instance}statuses/${id}/unreblog` | ||||||
|  |       break | ||||||
|  |     case 'bookmark': | ||||||
|  |       endpoint = `${instance}statuses/${id}/bookmark` | ||||||
|  |       break | ||||||
|  |     case 'unbookmark': | ||||||
|  |       endpoint = `${instance}statuses/${id}/unbookmark` | ||||||
|  |       break | ||||||
|  |     case 'mute': | ||||||
|  |       endpoint = `${instance}statuses/${id}/mute` | ||||||
|  |       break | ||||||
|  |     case 'unmute': | ||||||
|  |       endpoint = `${instance}statuses/${id}/unmute` | ||||||
|  |       break | ||||||
|  |     case 'pin': | ||||||
|  |       endpoint = `${instance}statuses/${id}/pin` | ||||||
|  |       break | ||||||
|  |     case 'unpin': | ||||||
|  |       endpoint = `${instance}statuses/${id}/unpin` | ||||||
|  |       break | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const res = await client.post(endpoint, [], header) | ||||||
|  |   console.log(res) | ||||||
|  |  | ||||||
|  |   const alert = { | ||||||
|  |     title: 'This is a title', | ||||||
|  |     message: 'This is a message' | ||||||
|  |   } | ||||||
|  |   Alert.alert(alert.title, alert.message, [ | ||||||
|  |     { text: 'OK', onPress: () => console.log('OK Pressed') } | ||||||
|  |   ]) | ||||||
|  | } | ||||||
| @@ -6,7 +6,7 @@ const propTypesAttachment = PropTypes.shape({ | |||||||
|   type: PropTypes.oneOf(['unknown', 'image', 'gifv', 'video', 'audio']) |   type: PropTypes.oneOf(['unknown', 'image', 'gifv', 'video', 'audio']) | ||||||
|     .isRequired, |     .isRequired, | ||||||
|   url: PropTypes.string.isRequired, |   url: PropTypes.string.isRequired, | ||||||
|   preview_url: PropTypes.string.isRequired, |   preview_url: PropTypes.string, | ||||||
|  |  | ||||||
|   // Others |   // Others | ||||||
|   remote_url: PropTypes.string, |   remote_url: PropTypes.string, | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ export default function Timeline ({ | |||||||
|   list, |   list, | ||||||
|   toot, |   toot, | ||||||
|   account, |   account, | ||||||
|   disableRefresh |   disableRefresh = false | ||||||
| }) { | }) { | ||||||
|   const dispatch = useDispatch() |   const dispatch = useDispatch() | ||||||
|   const state = useSelector(state => state.timelines[page]) |   const state = useSelector(state => state.timelines[page]) | ||||||
| @@ -50,11 +50,25 @@ export default function Timeline ({ | |||||||
|           {...(state.pointer && { initialScrollIndex: state.pointer })} |           {...(state.pointer && { initialScrollIndex: state.pointer })} | ||||||
|           {...(!disableRefresh && { |           {...(!disableRefresh && { | ||||||
|             onRefresh: () => |             onRefresh: () => | ||||||
|               dispatch(fetch({ page, paginationDirection: 'prev' })), |               dispatch( | ||||||
|  |                 fetch({ | ||||||
|  |                   page, | ||||||
|  |                   hashtag, | ||||||
|  |                   list, | ||||||
|  |                   paginationDirection: 'prev' | ||||||
|  |                 }) | ||||||
|  |               ), | ||||||
|             refreshing: state.status === 'loading', |             refreshing: state.status === 'loading', | ||||||
|             onEndReached: () => { |             onEndReached: () => { | ||||||
|               if (!timelineReady) { |               if (!timelineReady) { | ||||||
|                 dispatch(fetch({ page, paginationDirection: 'next' })) |                 dispatch( | ||||||
|  |                   fetch({ | ||||||
|  |                     page, | ||||||
|  |                     hashtag, | ||||||
|  |                     list, | ||||||
|  |                     paginationDirection: 'next' | ||||||
|  |                   }) | ||||||
|  |                 ) | ||||||
|                 setTimelineReady(true) |                 setTimelineReady(true) | ||||||
|               } |               } | ||||||
|             }, |             }, | ||||||
|   | |||||||
| @@ -101,7 +101,8 @@ TimelinesCombined.propTypes = { | |||||||
|   content: PropTypes.arrayOf( |   content: PropTypes.arrayOf( | ||||||
|     PropTypes.exact({ |     PropTypes.exact({ | ||||||
|       title: PropTypes.string.isRequired, |       title: PropTypes.string.isRequired, | ||||||
|       page: Timeline.propTypes.page |       page: Timeline.propTypes.page, | ||||||
|  |       instance: PropTypes.oneOf(['local', 'remote']) | ||||||
|     }) |     }) | ||||||
|   ).isRequired |   ).isRequired | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,12 +1,15 @@ | |||||||
| import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' | ||||||
|  |  | ||||||
| import { client } from 'src/api/client' | import client from 'src/api/client' | ||||||
|  |  | ||||||
| export const fetch = createAsyncThunk( | export const fetch = createAsyncThunk( | ||||||
|   'account/fetch', |   'account/fetch', | ||||||
|   async ({ id }, { getState }) => { |   async ({ id }, { getState }) => { | ||||||
|     const instanceLocal = `https://${getState().instanceInfo.local}/api/v1/` |     const res = await client({ | ||||||
|     const res = await client.get(`${instanceLocal}accounts/${id}`) |       method: 'get', | ||||||
|  |       instance: 'local', | ||||||
|  |       endpoint: `accounts/${id}` | ||||||
|  |     }) | ||||||
|     return res.body |     return res.body | ||||||
|   } |   } | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' | ||||||
|  |  | ||||||
| import { client } from 'src/api/client' | import client from 'src/api/client' | ||||||
|  |  | ||||||
| // Naming convention | // Naming convention | ||||||
| // Following:     timelines/home | // Following:     timelines/home | ||||||
| @@ -11,170 +11,161 @@ import { client } from 'src/api/client' | |||||||
| // Hashtag:       hastag | // Hashtag:       hastag | ||||||
| // List:          list | // List:          list | ||||||
|  |  | ||||||
| function getPagination (headers, direction) { |  | ||||||
|   if (!headers) console.error('Missing pagination headers') |  | ||||||
|   const paginationLinks = headers.get('Link') |  | ||||||
|   if (paginationLinks) { |  | ||||||
|     if (direction) { |  | ||||||
|       return { |  | ||||||
|         [direction]: paginationLinks.split( |  | ||||||
|           new RegExp(`<([^>]+)>; rel="${direction}"`) |  | ||||||
|         )[1] |  | ||||||
|       } |  | ||||||
|     } else { |  | ||||||
|       return { |  | ||||||
|         prev: paginationLinks.split(new RegExp(/<([^>]+)>; rel="prev"/))[1], |  | ||||||
|         next: paginationLinks.split(new RegExp(/<([^>]+)>; rel="next"/))[1] |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     return |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const fetch = createAsyncThunk( | export const fetch = createAsyncThunk( | ||||||
|   'timeline/fetch', |   'timeline/fetch', | ||||||
|   async ( |   async ( | ||||||
|     { page, paginationDirection, query = [], account, hashtag, list, toot }, |     { page, paginationDirection, query = {}, account, hashtag, list, toot }, | ||||||
|     { getState } |     { getState } | ||||||
|   ) => { |   ) => { | ||||||
|     const instanceLocal = `https://${getState().instanceInfo.local}/api/v1/` |  | ||||||
|     const instanceRemote = `https://${getState().instanceInfo.remote}/api/v1/` |  | ||||||
|     // If header if needed for remote server |  | ||||||
|     const header = { |  | ||||||
|       headers: { |  | ||||||
|         Authorization: `Bearer ${getState().instanceInfo.localToken}` |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let res |     let res | ||||||
|     // For same page, but only pagination |  | ||||||
|     if (paginationDirection) { |     if (paginationDirection) { | ||||||
|       res = await client.get( |       const allToots = getState().timelines[page].toots | ||||||
|         getState().timelines[page].pagination[paginationDirection], |       switch (paginationDirection) { | ||||||
|         query, |         case 'prev': | ||||||
|         header |           query.min_id = allToots[0].id | ||||||
|       ) |           break | ||||||
|       return { |         case 'next': | ||||||
|         toots: res.body, |           query.max_id = allToots[allToots.length - 1].id | ||||||
|         pagination: getPagination(res.headers, paginationDirection) |           break | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // For each page's first query |  | ||||||
|     switch (page) { |     switch (page) { | ||||||
|       case 'Following': |       case 'Following': | ||||||
|         res = await client.get(`${instanceLocal}timelines/home`, query, header) |         res = await client({ | ||||||
|  |           method: 'get', | ||||||
|  |           instance: 'local', | ||||||
|  |           endpoint: 'timelines/home', | ||||||
|  |           query | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body, |           toots: res.body | ||||||
|           pagination: getPagination(res.headers) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'Local': |       case 'Local': | ||||||
|         query.push({ key: 'local', value: 'true' }) |         query.local = 'true' | ||||||
|         res = await client.get( |         res = await client({ | ||||||
|           `${instanceLocal}timelines/public`, |           method: 'get', | ||||||
|           query, |           instance: 'local', | ||||||
|           header |           endpoint: 'timelines/public', | ||||||
|         ) |           query | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body, |           toots: res.body | ||||||
|           pagination: getPagination(res.headers) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'LocalPublic': |       case 'LocalPublic': | ||||||
|         res = await client.get( |         res = await client({ | ||||||
|           `${instanceLocal}timelines/public`, |           method: 'get', | ||||||
|           query, |           instance: 'local', | ||||||
|           header |           endpoint: 'timelines/public', | ||||||
|         ) |           query | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body, |           toots: res.body | ||||||
|           pagination: getPagination(res.headers) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'RemotePublic': |       case 'RemotePublic': | ||||||
|         res = await client.get(`${instanceRemote}timelines/public`, query) |         res = await client({ | ||||||
|  |           method: 'get', | ||||||
|  |           instance: 'remote', | ||||||
|  |           endpoint: 'timelines/public', | ||||||
|  |           query | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body, |           toots: res.body | ||||||
|           pagination: getPagination(res.headers) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'Notifications': |       case 'Notifications': | ||||||
|         res = await client.get(`${instanceLocal}notifications`, query, header) |         res = await client({ | ||||||
|  |           method: 'get', | ||||||
|  |           instance: 'local', | ||||||
|  |           endpoint: 'notifications', | ||||||
|  |           query | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body, |           toots: res.body | ||||||
|           pagination: getPagination(res.headers) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'Account_Default': |       case 'Account_Default': | ||||||
|         res = await client.get( |         res = await client({ | ||||||
|           `${instanceLocal}accounts/${account}/statuses`, |           method: 'get', | ||||||
|           [{ key: 'pinned', value: 'true' }], |           instance: 'local', | ||||||
|           header |           endpoint: `accounts/${account}/statuses`, | ||||||
|         ) |           query: { | ||||||
|  |             pinned: 'true' | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|         const toots = res.body |         const toots = res.body | ||||||
|         res = await client.get( |         res = await client({ | ||||||
|           `${instanceLocal}accounts/${account}/statuses`, |           method: 'get', | ||||||
|           [{ key: 'exclude_replies', value: 'true' }], |           instance: 'local', | ||||||
|           header |           endpoint: `accounts/${account}/statuses`, | ||||||
|         ) |           query: { | ||||||
|  |             exclude_replies: 'true' | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|         toots.push(...res.body) |         toots.push(...res.body) | ||||||
|         return { toots: toots } |         return { toots: toots } | ||||||
|  |  | ||||||
|       case 'Account_All': |       case 'Account_All': | ||||||
|         res = await client.get( |         res = await client({ | ||||||
|           `${instanceLocal}accounts/${account}/statuses`, |           method: 'get', | ||||||
|           query, |           instance: 'local', | ||||||
|           header |           endpoint: `accounts/${account}/statuses`, | ||||||
|         ) |           query | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body |           toots: res.body | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'Account_Media': |       case 'Account_Media': | ||||||
|         res = await client.get( |         res = await client({ | ||||||
|           `${instanceLocal}accounts/${account}/statuses`, |           method: 'get', | ||||||
|           [{ key: 'only_media', value: 'true' }], |           instance: 'local', | ||||||
|           header |           endpoint: `accounts/${account}/statuses`, | ||||||
|         ) |           query: { | ||||||
|  |             only_media: 'true' | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body |           toots: res.body | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'Hashtag': |       case 'Hashtag': | ||||||
|         res = await client.get( |         res = await client({ | ||||||
|           `${instanceLocal}timelines/tag/${hashtag}`, |           method: 'get', | ||||||
|           query, |           instance: 'local', | ||||||
|           header |           endpoint: `timelines/tag/${hashtag}`, | ||||||
|         ) |           query | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body, |           toots: res.body | ||||||
|           pagination: getPagination(res.headers) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'List': |       case 'List': | ||||||
|         res = await client.get( |         res = await client({ | ||||||
|           `${instanceLocal}timelines/list/${list}`, |           method: 'get', | ||||||
|           query, |           instance: 'local', | ||||||
|           header |           endpoint: `timelines/list/${list}`, | ||||||
|         ) |           query | ||||||
|  |         }) | ||||||
|         return { |         return { | ||||||
|           toots: res.body, |           toots: res.body | ||||||
|           pagination: getPagination(res.headers) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|       case 'Toot': |       case 'Toot': | ||||||
|         const current = await client.get( |         const current = await client({ | ||||||
|           `${instanceLocal}statuses/${toot}`, |           method: 'get', | ||||||
|           [], |           instance: 'local', | ||||||
|           header |           endpoint: `statuses/${toot}` | ||||||
|         ) |         }) | ||||||
|         const context = await client.get( |         const context = await client({ | ||||||
|           `${instanceLocal}statuses/${toot}/context`, |           method: 'get', | ||||||
|           [], |           instance: 'local', | ||||||
|           header |           endpoint: `statuses/${toot}/context` | ||||||
|         ) |         }) | ||||||
|         return { |         return { | ||||||
|           toots: [...context.ancestors, current, ...context.descendants], |           toots: [...context.ancestors, current, ...context.descendants], | ||||||
|           pointer: context.ancestors.length |           pointer: context.ancestors.length | ||||||
| @@ -188,7 +179,6 @@ export const fetch = createAsyncThunk( | |||||||
|  |  | ||||||
| const timelineInitState = { | const timelineInitState = { | ||||||
|   toots: [], |   toots: [], | ||||||
|   pagination: { prev: undefined, next: undefined }, |  | ||||||
|   pointer: undefined, |   pointer: undefined, | ||||||
|   status: 'idle' |   status: 'idle' | ||||||
| } | } | ||||||
| @@ -226,12 +216,6 @@ export const timelineSlice = createSlice({ | |||||||
|         state[action.meta.arg.page].toots.push(...action.payload.toots) |         state[action.meta.arg.page].toots.push(...action.payload.toots) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       if (action.payload.pagination) { |  | ||||||
|         state[action.meta.arg.page].pagination = { |  | ||||||
|           ...state[action.meta.arg.page].pagination, |  | ||||||
|           ...action.payload.pagination |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|       if (action.payload.pointer) { |       if (action.payload.pointer) { | ||||||
|         state[action.meta.arg.page].pointer = action.payload.pointer |         state[action.meta.arg.page].pointer = action.payload.pointer | ||||||
|       } |       } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user