Update threads to use design system
parent
55500e2f66
commit
55ca7dcce1
|
@ -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)
|
||||
|
|
|
@ -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]}>
|
||||
· {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} />
|
||||
)}
|
||||
{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}
|
||||
<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},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.replyingToText} numberOfLines={2}>
|
||||
{item.replyingTo.text}
|
||||
</Text>
|
||||
</View>
|
||||
) : undefined}
|
||||
)}
|
||||
{item.replies?.length !== 0 && (
|
||||
<View
|
||||
style={[
|
||||
styles.childReplyLine,
|
||||
{borderColor: pal.colors.replyLine},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<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,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
]}
|
||||
/>
|
||||
|
|
|
@ -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]}>
|
||||
{handle}
|
||||
</Text>
|
||||
) : undefined}
|
||||
</Text>
|
||||
</Link>
|
||||
<Text style={[styles.metaItem, pal.textLight, theme.typography.h6]}>
|
||||
<Text type="h6" style={[styles.metaItem, pal.textLight]}>
|
||||
· {ago(opts.timestamp)}
|
||||
</Text>
|
||||
<View style={s.flex1} />
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue