Working emojis in reblog and names

This commit is contained in:
Zhiyuan Zheng 2020-10-25 01:35:41 +02:00
parent 2bc6e3277c
commit 996cf0c1e7
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
11 changed files with 289 additions and 59 deletions

View File

@ -3,6 +3,7 @@ module.exports = function (api) {
return {
presets: ['babel-preset-expo'],
plugins: [
['@babel/plugin-proposal-optional-chaining'],
[
'module-resolver',
{

View File

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

View File

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

View 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
// }

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

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

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

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

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

View File

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

View File

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