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