mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Working emojis in reblog and names
This commit is contained in:
		| @@ -3,6 +3,7 @@ module.exports = function (api) { | ||||
|   return { | ||||
|     presets: ['babel-preset-expo'], | ||||
|     plugins: [ | ||||
|       ['@babel/plugin-proposal-optional-chaining'], | ||||
|       [ | ||||
|         'module-resolver', | ||||
|         { | ||||
|   | ||||
| @@ -34,6 +34,7 @@ | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@babel/core": "~7.9.0", | ||||
|     "@babel/plugin-proposal-optional-chaining": "^7.12.1", | ||||
|     "babel-plugin-module-resolver": "^4.0.0" | ||||
|   }, | ||||
|   "private": true | ||||
|   | ||||
| @@ -1,52 +1,60 @@ | ||||
| import PropTypes from 'prop-types' | ||||
| import React from 'react' | ||||
| import { Image, StyleSheet, Text, View } from 'react-native' | ||||
| import HTML from 'react-native-render-html' | ||||
| import { StyleSheet, View } from 'react-native' | ||||
|  | ||||
| import relativeTime from 'src/utils/relativeTime' | ||||
| import Reblog from './TootTimeline/Reblog' | ||||
| import Avatar from './TootTimeline/Avatar' | ||||
| import Header from './TootTimeline/Header' | ||||
| import Content from './TootTimeline/Content' | ||||
| import Actions from './TootTimeline/Actions' | ||||
|  | ||||
| export default function TootTimeline ({ item, notification }) { | ||||
|   return ( | ||||
|     <View style={styles.tootTimeline}> | ||||
|       <View style={styles.header}> | ||||
|         <Image | ||||
|           source={{ | ||||
|             uri: item.reblog ? item.reblog.account.avatar : item.account.avatar | ||||
|           }} | ||||
|           style={styles.avatar} | ||||
|       {item.reblog && ( | ||||
|         <Reblog | ||||
|           name={item.account.display_name || item.account.username} | ||||
|           emojis={item.account.emojis} | ||||
|         /> | ||||
|         <View> | ||||
|           <View style={styles.name}> | ||||
|             <Text> | ||||
|               {item.reblog | ||||
|                 ? item.reblog.account.display_name | ||||
|                 : item.account.display_name} | ||||
|             </Text> | ||||
|             <Text> | ||||
|               {item.reblog ? item.reblog.account.acct : item.account.acct} | ||||
|             </Text> | ||||
|           </View> | ||||
|           <View> | ||||
|             <Text>{relativeTime(item.created_at)}</Text> | ||||
|             {item.application && item.application.name !== 'Web' && ( | ||||
|               <Text onPress={() => Linking.openURL(item.application.website)}> | ||||
|                 {item.application.name} | ||||
|               </Text> | ||||
|             )} | ||||
|           </View> | ||||
|       )} | ||||
|       <View style={styles.toot}> | ||||
|         <Avatar uri={item.reblog?.account.avatar || item.account.avatar} /> | ||||
|         <View style={{flexGrow: 1}}> | ||||
|           <Header | ||||
|             name={ | ||||
|               (item.reblog?.account.display_name | ||||
|                 ? item.reblog?.account.display_name | ||||
|                 : item.reblog?.account.username) || | ||||
|               (item.account.display_name | ||||
|                 ? item.account.display_name | ||||
|                 : item.account.username) | ||||
|             } | ||||
|             emojis={item.reblog?.account.emojis || item.account.emojis} | ||||
|             account={item.reblog?.account.acct || item.account.acct} | ||||
|             created_at={item.created_at} | ||||
|             application={item.application || null} | ||||
|           /> | ||||
|           <Content | ||||
|             content={notification ? item.status.content : item.content} | ||||
|           /> | ||||
|         </View> | ||||
|       </View> | ||||
|       {notification ? ( | ||||
|         <HTML html={item.status.content} /> | ||||
|       ) : item.content ? ( | ||||
|         <HTML html={item.content} /> | ||||
|       ) : ( | ||||
|         <></> | ||||
|       )} | ||||
|       <Actions /> | ||||
|     </View> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   tootTimeline: { | ||||
|     flex: 1, | ||||
|     flexDirection: 'column', | ||||
|     padding: 12 | ||||
|   }, | ||||
|   toot: { | ||||
|     flexDirection: 'row' | ||||
|   } | ||||
| }) | ||||
|  | ||||
| TootTimeline.propTypes = { | ||||
|   item: PropTypes.shape({ | ||||
|     account: PropTypes.shape({ | ||||
| @@ -63,20 +71,3 @@ TootTimeline.propTypes = { | ||||
|   }).isRequired, | ||||
|   notification: PropTypes.bool | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   tootTimeline: { | ||||
|     flex: 1, | ||||
|     padding: 15 | ||||
|   }, | ||||
|   header: { | ||||
|     flexDirection: 'row' | ||||
|   }, | ||||
|   avatar: { | ||||
|     width: 40, | ||||
|     height: 40 | ||||
|   }, | ||||
|   name: { | ||||
|     flexDirection: 'row' | ||||
|   } | ||||
| }) | ||||
|   | ||||
							
								
								
									
										16
									
								
								src/components/TootTimeline/Actions.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/components/TootTimeline/Actions.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| import React from 'react' | ||||
| import PropTypes from 'prop-types' | ||||
| import { StyleSheet } from 'react-native' | ||||
|  | ||||
| export default function Actions () { | ||||
|   return <></> | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   width: 50, | ||||
|   height: 50 | ||||
| }) | ||||
|  | ||||
| // Actions.propTypes = { | ||||
| //   uri: PropTypes.string | ||||
| // } | ||||
							
								
								
									
										19
									
								
								src/components/TootTimeline/Avatar.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/components/TootTimeline/Avatar.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| import React from 'react' | ||||
| import PropTypes from 'prop-types' | ||||
| import { Image, StyleSheet } from 'react-native' | ||||
|  | ||||
| export default function Avatar ({ uri }) { | ||||
|   return <Image source={{ uri: uri }} style={styles.avatar} /> | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   avatar: { | ||||
|     width: 50, | ||||
|     height: 50, | ||||
|     marginRight: 8 | ||||
|   } | ||||
| }) | ||||
|  | ||||
| Avatar.propTypes = { | ||||
|   uri: PropTypes.string.isRequired | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/components/TootTimeline/Content.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/components/TootTimeline/Content.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import React, { useState } from 'react' | ||||
| import PropTypes from 'prop-types' | ||||
| import { Dimensions, StyleSheet, View } from 'react-native' | ||||
| import HTML from 'react-native-render-html' | ||||
|  | ||||
| // !! Need to solve dimension issue | ||||
|  | ||||
| export default function Content ({ content }) { | ||||
|   const [viewWidth, setViewWidth] = useState() | ||||
|   return ( | ||||
|     content && ( | ||||
|       <View | ||||
|         style={{ width: '100%' }} | ||||
|         onLayout={e => setViewWidth(e.nativeEvent.layout.width)} | ||||
|       > | ||||
|         {viewWidth && ( | ||||
|           <HTML html={content} containerStyle={{ width: viewWidth }} /> | ||||
|         )} | ||||
|       </View> | ||||
|     ) | ||||
|   ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   width: 50, | ||||
|   height: 50 | ||||
| }) | ||||
|  | ||||
| Content.propTypes = { | ||||
|   content: PropTypes.string | ||||
| } | ||||
							
								
								
									
										53
									
								
								src/components/TootTimeline/Emojis.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/components/TootTimeline/Emojis.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| import React from 'react' | ||||
| import PropTypes from 'prop-types' | ||||
| import { Image, Text } from 'react-native' | ||||
|  | ||||
| const regexEmoji = new RegExp(/(:[a-z0-9_]+:)/g) | ||||
| const regexEmojiSelect = new RegExp(/:([a-z0-9_]+):/) | ||||
|  | ||||
| export default function Emojis ({ content, emojis, dimension }) { | ||||
|   const hasEmojis = content.match(regexEmoji) | ||||
|   return hasEmojis ? ( | ||||
|     content.split(regexEmoji).map((str, i) => { | ||||
|       if (str.match(regexEmoji)) { | ||||
|         const emojiShortcode = str.split(regexEmojiSelect)[1] | ||||
|         const emojiIndex = emojis.findIndex(emoji => { | ||||
|           return emoji.shortcode === emojiShortcode | ||||
|         }) | ||||
|         return ( | ||||
|           <Image | ||||
|             key={i} | ||||
|             source={{ uri: emojis[emojiIndex].url }} | ||||
|             style={{ width: dimension, height: dimension }} | ||||
|           /> | ||||
|         ) | ||||
|       } else { | ||||
|         return ( | ||||
|           <Text | ||||
|             key={i} | ||||
|             style={{ fontSize: dimension, lineHeight: dimension + 1 }} | ||||
|           > | ||||
|             {str} | ||||
|           </Text> | ||||
|         ) | ||||
|       } | ||||
|     }) | ||||
|   ) : ( | ||||
|     <Text style={{ fontSize: dimension, lineHeight: dimension + 1 }}> | ||||
|       {content} | ||||
|     </Text> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| Emojis.propTypes = { | ||||
|   content: PropTypes.string.isRequired, | ||||
|   emojis: PropTypes.arrayOf( | ||||
|     PropTypes.exact({ | ||||
|       shortcode: PropTypes.string.isRequired, | ||||
|       url: PropTypes.string.isRequired, | ||||
|       static_url: PropTypes.string.isRequired, | ||||
|       visible_in_picker: PropTypes.bool.isRequired, | ||||
|       category: PropTypes.string | ||||
|     }) | ||||
|   ) | ||||
| } | ||||
							
								
								
									
										86
									
								
								src/components/TootTimeline/Header.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/components/TootTimeline/Header.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| import React, { useEffect, useState } from 'react' | ||||
| import PropTypes from 'prop-types' | ||||
| import { StyleSheet, Text, View } from 'react-native' | ||||
|  | ||||
| import Emojis from './Emojis' | ||||
| import relativeTime from 'src/utils/relativeTime' | ||||
|  | ||||
| export default function Header ({ | ||||
|   name, | ||||
|   emojis, | ||||
|   account, | ||||
|   created_at, | ||||
|   application | ||||
| }) { | ||||
|   const [since, setSince] = useState(relativeTime(created_at)) | ||||
|  | ||||
|   useEffect(() => { | ||||
|     const timer = setTimeout(() => { | ||||
|       setSince(relativeTime(created_at)) | ||||
|     }, 1000) | ||||
|   }) | ||||
|  | ||||
|   return ( | ||||
|     <View> | ||||
|       <View style={styles.names}> | ||||
|         <View style={styles.name}> | ||||
|           <Emojis content={name} emojis={emojis} dimension={14} /> | ||||
|         </View> | ||||
|         <Text style={styles.account}>@{account}</Text> | ||||
|       </View> | ||||
|       <View style={styles.meta}> | ||||
|         <View> | ||||
|           <Text style={styles.created_at}>{since}</Text> | ||||
|         </View> | ||||
|         {application && application.name !== 'Web' && ( | ||||
|           <View> | ||||
|             <Text | ||||
|               onPress={() => Linking.openURL(application.website)} | ||||
|               style={styles.application} | ||||
|             > | ||||
|               {application.name} | ||||
|             </Text> | ||||
|           </View> | ||||
|         )} | ||||
|       </View> | ||||
|     </View> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   names: { | ||||
|     flexDirection: 'row', | ||||
|     marginBottom: 8 | ||||
|   }, | ||||
|   name: { | ||||
|     flexDirection: 'row', | ||||
|     marginRight: 8 | ||||
|   }, | ||||
|   account: { | ||||
|     fontSize: 12, | ||||
|     lineHeight: 14 | ||||
|   }, | ||||
|   meta: { | ||||
|     flexDirection: 'row' | ||||
|   }, | ||||
|   created_at: { | ||||
|     fontSize: 12, | ||||
|     lineHeight: 12, | ||||
|     marginRight: 8 | ||||
|   }, | ||||
|   application: { | ||||
|     fontSize: 12, | ||||
|     lineHeight: 11 | ||||
|   } | ||||
| }) | ||||
|  | ||||
| Header.propTypes = { | ||||
|   name: PropTypes.string.isRequired, | ||||
|   emojis: Emojis.propTypes.emojis, | ||||
|   account: PropTypes.string.isRequired, | ||||
|   created_at: PropTypes.string.isRequired, | ||||
|   application: PropTypes.exact({ | ||||
|     name: PropTypes.string.isRequired, | ||||
|     website: PropTypes.string | ||||
|   }) | ||||
| } | ||||
							
								
								
									
										36
									
								
								src/components/TootTimeline/Reblog.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/components/TootTimeline/Reblog.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| import React from 'react' | ||||
| import PropTypes from 'prop-types' | ||||
| import { StyleSheet, Text, View } from 'react-native' | ||||
| import { Feather } from '@expo/vector-icons' | ||||
|  | ||||
| import Emojis from './Emojis' | ||||
|  | ||||
| export default function Reblog ({ name, emojis }) { | ||||
|   return ( | ||||
|     <View style={styles.reblog}> | ||||
|       <Feather name='repeat' size={12} color='black' style={styles.icon} /> | ||||
|       <View style={styles.name}> | ||||
|         <Emojis content={name} emojis={emojis} dimension={12} /> | ||||
|       </View> | ||||
|     </View> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| const styles = StyleSheet.create({ | ||||
|   reblog: { | ||||
|     flexDirection: 'row', | ||||
|     marginBottom: 8 | ||||
|   }, | ||||
|   icon: { | ||||
|     marginLeft: 50 - 12, | ||||
|     marginRight: 8 | ||||
|   }, | ||||
|   name: { | ||||
|     flexDirection: 'row' | ||||
|   } | ||||
| }) | ||||
|  | ||||
| Reblog.propTypes = { | ||||
|   name: PropTypes.string.isRequired, | ||||
|   emojis: Emojis.propTypes.emojis | ||||
| } | ||||
| @@ -21,7 +21,6 @@ const Default = ({ dispatch, toots, status, timeline }) => { | ||||
|           dispatch(fetch({ ...timeline, id: toots[toots.length - 1].id })) | ||||
|         } | ||||
|         onEndReachedThreshold={0.5} | ||||
|         style={{ height: '100%', width: '100%' }} | ||||
|       /> | ||||
|       {status === 'loading' && <ActivityIndicator />} | ||||
|     </> | ||||
| @@ -42,12 +41,9 @@ const Notifications = ({ dispatch, toots, status, timeline }) => { | ||||
|         } | ||||
|         refreshing={status === 'loading'} | ||||
|         onEndReached={() => | ||||
|           dispatch( | ||||
|             fetch({ ...timeline, id: toots[toots.length - 1].id }) | ||||
|           ) | ||||
|           dispatch(fetch({ ...timeline, id: toots[toots.length - 1].id })) | ||||
|         } | ||||
|         onEndReachedThreshold={0.5} | ||||
|         style={{ height: '100%', width: '100%' }} | ||||
|       /> | ||||
|       {status === 'loading' && <ActivityIndicator />} | ||||
|     </> | ||||
|   | ||||
| @@ -6,8 +6,8 @@ import timelineSlice from 'src/stacks/common/timelineSlice' | ||||
| // get site information from local storage and pass to reducers | ||||
| const preloadedState = { | ||||
|   instanceInfo: { | ||||
|     current: 'm.cmx.im', | ||||
|     currentToken: 'Cxx19XX2VNHnPy_dr_HCHMh4HvwHEvYwWrrU3r3BNzQ', | ||||
|     current: 'social.xmflsct.com', | ||||
|     currentToken: 'qjzJ0IjvZ1apsn0_wBkGcdjKgX7Dao9KEPhGwggPwAo', | ||||
|     remote: 'mastodon.social' | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user