Update threads to use design system

zio/stable
Paul Frazee 2022-12-30 12:19:45 -06:00
parent 55500e2f66
commit 55ca7dcce1
6 changed files with 63 additions and 137 deletions

View File

@ -16,18 +16,6 @@ function* reactKeyGenerator(): Generator<string> {
}
}
interface ReplyingTo {
author: {
handle: string
displayName?: string
avatar?: string
}
text: string
}
interface OriginalRecord {
text: string
}
function isThreadViewPost(
v: GetPostThread.ThreadViewPost | GetPostThread.NotFoundPost | UnknownPost,
): v is GetPostThread.ThreadViewPost {
@ -51,9 +39,6 @@ export class PostThreadViewPostModel {
parent?: PostThreadViewPostModel | GetPostThread.NotFoundPost
replies?: (PostThreadViewPostModel | GetPostThread.NotFoundPost)[]
// added data
replyingTo?: ReplyingTo
constructor(
public rootStore: RootStoreModel,
reactKey: string,
@ -70,7 +55,6 @@ export class PostThreadViewPostModel {
v: GetPostThread.ThreadViewPost,
includeParent = true,
includeChildren = true,
isFirstChild = true,
) {
// parents
if (includeParent && v.parent) {
@ -89,25 +73,9 @@ export class PostThreadViewPostModel {
this.parent = v.parent
}
}
if (
!includeParent &&
v.parent &&
isThreadViewPost(v.parent) &&
!isFirstChild
) {
this.replyingTo = {
author: {
handle: v.parent.post.author.handle,
displayName: v.parent.post.author.displayName,
avatar: v.parent.post.author.avatar,
},
text: (v.parent.record as OriginalRecord).text,
}
}
// replies
if (includeChildren && v.replies) {
const replies = []
let isChildFirstChild = true
for (const item of v.replies) {
if (isThreadViewPost(item)) {
const itemModel = new PostThreadViewPostModel(
@ -117,15 +85,8 @@ export class PostThreadViewPostModel {
)
itemModel._depth = this._depth + 1
if (item.replies) {
itemModel.assignTreeModels(
keyGen,
item,
false,
true,
isChildFirstChild,
)
itemModel.assignTreeModels(keyGen, item, false, true)
}
isChildFirstChild = false
replies.push(itemModel)
} else if (isNotFoundPost(item)) {
replies.push(item)

View File

@ -12,16 +12,16 @@ import {Text} from '../util/text/Text'
import {PostDropdownBtn} from '../util/forms/DropdownButton'
import * as Toast from '../util/Toast'
import {UserAvatar} from '../util/UserAvatar'
import {s, colors} from '../../lib/styles'
import {s} from '../../lib/styles'
import {ago, pluralize} from '../../../lib/strings'
import {useStores} from '../../../state'
import {PostMeta} from '../util/PostMeta'
import {PostEmbeds} from '../util/PostEmbeds'
import {PostCtrls} from '../util/PostCtrls'
import {ComposePrompt} from '../composer/Prompt'
import {usePalette} from '../../lib/hooks/usePalette'
const PARENT_REPLY_LINE_LENGTH = 8
const REPLYING_TO_LINE_LENGTH = 6
export const PostThreadItem = observer(function PostThreadItem({
item,
@ -30,6 +30,7 @@ export const PostThreadItem = observer(function PostThreadItem({
item: PostThreadViewPostModel
onPostReply: () => void
}) {
const pal = usePalette('default')
const store = useStores()
const [deleted, setDeleted] = useState(false)
const record = item.post.record as unknown as AppBskyFeedPost.Record
@ -97,9 +98,12 @@ export const PostThreadItem = observer(function PostThreadItem({
if (deleted) {
return (
<View style={[styles.outer, s.p20, s.flexRow]}>
<FontAwesomeIcon icon={['far', 'trash-can']} style={[s.gray4]} />
<Text style={[s.gray5, s.ml10]}>This post has been deleted.</Text>
<View style={[styles.outer, pal.view, s.p20, s.flexRow]}>
<FontAwesomeIcon
icon={['far', 'trash-can']}
style={{color: pal.colors.icon}}
/>
<Text style={[pal.textLight, s.ml10]}>This post has been deleted.</Text>
</View>
)
}
@ -107,7 +111,8 @@ export const PostThreadItem = observer(function PostThreadItem({
if (item._isHighlightedPost) {
return (
<>
<View style={styles.outer}>
<View
style={[styles.outer, {borderTopColor: pal.colors.border}, pal.view]}>
<View style={styles.layout}>
<View style={styles.layoutAvi}>
<Link href={authorHref} title={authorTitle}>
@ -125,11 +130,11 @@ export const PostThreadItem = observer(function PostThreadItem({
style={styles.metaItem}
href={authorHref}
title={authorTitle}>
<Text style={[s.f16, s.bold, s.black]} numberOfLines={1}>
<Text type="h5" style={[pal.text]} numberOfLines={1}>
{item.post.author.displayName || item.post.author.handle}
</Text>
</Link>
<Text style={[styles.metaItem, s.f15, s.gray5]}>
<Text type="h6" style={[styles.metaItem, pal.textLight]}>
&middot; {ago(item.post.indexedAt)}
</Text>
<View style={s.flex1} />
@ -152,7 +157,7 @@ export const PostThreadItem = observer(function PostThreadItem({
style={styles.metaItem}
href={authorHref}
title={authorTitle}>
<Text style={[s.f15, s.gray5]} numberOfLines={1}>
<Text type="h6" style={[pal.textLight]} numberOfLines={1}>
@{item.post.author.handle}
</Text>
</Link>
@ -167,22 +172,23 @@ export const PostThreadItem = observer(function PostThreadItem({
styles.postTextLargeContainer,
]}>
<RichText
type="h3"
text={record.text}
entities={record.entities}
style={[styles.postText, styles.postTextLarge]}
/>
</View>
) : undefined}
<PostEmbeds embed={item.post.embed} style={s.mb10} />
{item._isHighlightedPost && hasEngagement ? (
<View style={styles.expandedInfo}>
<View
style={[styles.expandedInfo, {borderColor: pal.colors.border}]}>
{item.post.repostCount ? (
<Link
style={styles.expandedInfoItem}
href={repostsHref}
title={repostsTitle}>
<Text style={[s.gray5, s.semiBold, s.f17]}>
<Text style={[s.bold, s.black, s.f17]}>
<Text type="h6" style={pal.textLight}>
<Text type="h5" style={pal.text}>
{item.post.repostCount}
</Text>{' '}
{pluralize(item.post.repostCount, 'repost')}
@ -196,8 +202,8 @@ export const PostThreadItem = observer(function PostThreadItem({
style={styles.expandedInfoItem}
href={upvotesHref}
title={upvotesTitle}>
<Text style={[s.gray5, s.semiBold, s.f17]}>
<Text style={[s.bold, s.black, s.f17]}>
<Text type="h6" style={pal.textLight}>
<Text type="h5" style={pal.text}>
{item.post.upvoteCount}
</Text>{' '}
{pluralize(item.post.upvoteCount, 'upvote')}
@ -233,27 +239,27 @@ export const PostThreadItem = observer(function PostThreadItem({
} else {
return (
<>
<Link style={styles.outer} href={itemHref} title={itemTitle} noFeedback>
{!item.replyingTo && record.reply && (
<View style={styles.parentReplyLine} />
<Link
style={[styles.outer, {borderTopColor: pal.colors.border}, pal.view]}
href={itemHref}
title={itemTitle}
noFeedback>
{record.reply && (
<View
style={[
styles.parentReplyLine,
{borderColor: pal.colors.replyLine},
]}
/>
)}
{item.replies?.length !== 0 && (
<View
style={[
styles.childReplyLine,
{borderColor: pal.colors.replyLine},
]}
/>
)}
{item.replies?.length !== 0 && <View style={styles.childReplyLine} />}
{item.replyingTo ? (
<View style={styles.replyingTo}>
<View style={styles.replyingToLine} />
<View style={styles.replyingToAvatar}>
<UserAvatar
handle={item.replyingTo.author.handle}
displayName={item.replyingTo.author.displayName}
avatar={item.replyingTo.author.avatar}
size={30}
/>
</View>
<Text style={styles.replyingToText} numberOfLines={2}>
{item.replyingTo.text}
</Text>
</View>
) : undefined}
<View style={styles.layout}>
<View style={styles.layoutAvi}>
<Link href={authorHref} title={authorTitle}>
@ -282,7 +288,7 @@ export const PostThreadItem = observer(function PostThreadItem({
<RichText
text={record.text}
entities={record.entities}
style={[styles.postText]}
style={pal.text}
/>
</View>
) : (
@ -304,11 +310,15 @@ export const PostThreadItem = observer(function PostThreadItem({
</Link>
{item._hasMore ? (
<Link
style={styles.loadMore}
style={[
styles.loadMore,
{borderTopColor: pal.colors.border},
pal.view,
]}
href={itemHref}
title={itemTitle}
noFeedback>
<Text style={styles.loadMoreText}>Load more</Text>
<Text style={pal.link}>Load more</Text>
</Link>
) : undefined}
</>
@ -318,9 +328,7 @@ export const PostThreadItem = observer(function PostThreadItem({
const styles = StyleSheet.create({
outer: {
backgroundColor: colors.white,
borderTopWidth: 1,
borderTopColor: colors.gray2,
},
parentReplyLine: {
position: 'absolute',
@ -328,7 +336,6 @@ const styles = StyleSheet.create({
top: -1 * PARENT_REPLY_LINE_LENGTH + 6,
height: PARENT_REPLY_LINE_LENGTH,
borderLeftWidth: 2,
borderLeftColor: colors.gray2,
},
childReplyLine: {
position: 'absolute',
@ -336,31 +343,6 @@ const styles = StyleSheet.create({
top: 65,
bottom: 0,
borderLeftWidth: 2,
borderLeftColor: colors.gray2,
},
replyingToLine: {
position: 'absolute',
left: 34,
bottom: -1 * REPLYING_TO_LINE_LENGTH,
height: REPLYING_TO_LINE_LENGTH,
borderLeftWidth: 2,
borderLeftColor: colors.gray2,
},
replyingTo: {
flexDirection: 'row',
backgroundColor: colors.white,
paddingLeft: 8,
paddingTop: 12,
paddingBottom: 0,
paddingRight: 24,
},
replyingToAvatar: {
marginLeft: 12,
marginRight: 20,
},
replyingToText: {
flex: 1,
color: colors.gray5,
},
layout: {
flexDirection: 'row',
@ -386,12 +368,6 @@ const styles = StyleSheet.create({
paddingRight: 5,
maxWidth: 240,
},
postText: {
fontFamily: 'System',
fontSize: 16,
lineHeight: 20.8, // 1.3 of 16px
color: 'black',
},
postTextContainer: {
flexDirection: 'row',
alignItems: 'center',
@ -399,10 +375,6 @@ const styles = StyleSheet.create({
paddingBottom: 8,
minHeight: 36,
},
postTextLarge: {
fontSize: 24,
lineHeight: 32,
},
postTextLargeContainer: {
paddingLeft: 4,
paddingBottom: 20,
@ -410,7 +382,6 @@ const styles = StyleSheet.create({
expandedInfo: {
flexDirection: 'row',
padding: 10,
borderColor: colors.gray2,
borderTopWidth: 1,
borderBottomWidth: 1,
marginTop: 5,
@ -420,15 +391,8 @@ const styles = StyleSheet.create({
marginRight: 10,
},
loadMore: {
borderTopWidth: 1,
paddingLeft: 28,
paddingVertical: 10,
backgroundColor: colors.white,
borderRadius: 6,
margin: 2,
marginBottom: 0,
},
loadMoreText: {
fontSize: 17,
color: colors.blue3,
},
})

View File

@ -16,7 +16,7 @@ import {PostEmbeds} from '../util/PostEmbeds'
import {RichText} from '../util/text/RichText'
import * as Toast from '../util/Toast'
import {UserAvatar} from '../util/UserAvatar'
import {s, colors, lh} from '../../lib/styles'
import {s, colors} from '../../lib/styles'
import {useStores} from '../../../state'
import {useTheme} from '../../lib/ThemeContext'
import {usePalette} from '../../lib/hooks/usePalette'
@ -114,17 +114,14 @@ export const FeedItem = observer(function ({
<Link style={outerStyles} href={itemHref} title={itemTitle} noFeedback>
{item._isThreadChild && (
<View
style={[
styles.topReplyLine,
{borderLeftColor: pal.colors.replyLine},
]}
style={[styles.topReplyLine, {borderColor: pal.colors.replyLine}]}
/>
)}
{(showReplyLine || item._isThreadParent) && (
<View
style={[
styles.bottomReplyLine,
{borderLeftColor: pal.colors.replyLine},
{borderColor: pal.colors.replyLine},
isNoTop ? {top: 64} : undefined,
]}
/>

View File

@ -48,16 +48,16 @@ export function PostMeta(opts: PostMetaOpts) {
style={styles.metaItem}
href={opts.authorHref}
title={opts.authorHandle}>
<Text style={[pal.text, theme.typography.h5]} numberOfLines={1}>
<Text type="h5" style={[pal.text]} numberOfLines={1}>
{displayName}
{handle ? (
<Text style={[pal.textLight, theme.typography.h6]}>
<Text type="h6" style={[pal.textLight]}>
&nbsp;{handle}
</Text>
) : undefined}
</Text>
</Link>
<Text style={[styles.metaItem, pal.textLight, theme.typography.h6]}>
<Text type="h6" style={[styles.metaItem, pal.textLight]}>
&middot; {ago(opts.timestamp)}
</Text>
<View style={s.flex1} />

View File

@ -29,7 +29,7 @@ export function RichText({
}) {
const theme = useTheme()
const pal = usePalette('default')
const lineHeightStyle = lh(theme, 'body1', 1.2)
const lineHeightStyle = lh(theme, type, 1.2)
if (!entities?.length) {
if (/^\p{Extended_Pictographic}+$/u.test(text) && text.length <= 5) {
style = {
@ -38,7 +38,11 @@ export function RichText({
}
return <Text style={[style, pal.text]}>{text}</Text>
}
return <Text style={[style, pal.text, lineHeightStyle]}>{text}</Text>
return (
<Text type={type} style={[style, pal.text, lineHeightStyle]}>
{text}
</Text>
)
}
if (!style) style = []
else if (!Array.isArray(style)) style = [style]

View File

@ -199,6 +199,6 @@ export function lh(
height: number,
): TextStyle {
return {
lineHeight: (theme.typography[type].lineHeight || 16) * height,
lineHeight: (theme.typography[type].fontSize || 16) * height,
}
}