diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-07-20 19:30:07 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-07-20 19:30:07 -0500 |
commit | 39483d92db90b69c8e1b47d82829d79651a69d25 (patch) | |
tree | 1236a1ac65b1a0b27d95c5e6466ac06d194e4c0f | |
parent | c712cbbfe27cca5db5d87abd8d7fd3b749492fcc (diff) | |
download | voidsky-39483d92db90b69c8e1b47d82829d79651a69d25.tar.zst |
Factor out common styles; fixes and improvements to post-thread-view
-rw-r--r-- | src/state/models/post-thread-view.ts | 26 | ||||
-rw-r--r-- | src/view/com/feed/FeedItem.tsx | 60 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 1 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 125 | ||||
-rw-r--r-- | src/view/lib/strings.ts | 9 | ||||
-rw-r--r-- | src/view/lib/styles.ts | 37 |
6 files changed, 159 insertions, 99 deletions
diff --git a/src/state/models/post-thread-view.ts b/src/state/models/post-thread-view.ts index e2e0f7462..27a10cb8e 100644 --- a/src/state/models/post-thread-view.ts +++ b/src/state/models/post-thread-view.ts @@ -3,8 +3,20 @@ import {bsky, AdxUri} from '@adxp/mock-api' import _omit from 'lodash.omit' import {RootStoreModel} from './root-store' +function* reactKeyGenerator(): Generator<string> { + let counter = 0 + while (true) { + yield `item-${counter++}` + } +} + export class PostThreadViewPostModel implements bsky.PostThreadView.Post { + // ui state _reactKey: string = '' + _depth = 0 + _isHighlightedPost = false + + // data uri: string = '' author: bsky.PostThreadView.User = {did: '', name: '', displayName: ''} record: Record<string, unknown> = {} @@ -26,15 +38,15 @@ export class PostThreadViewPostModel implements bsky.PostThreadView.Post { } } - setReplies(v: bsky.PostThreadView.Post) { + setReplies(keyGen: Generator<string>, v: bsky.PostThreadView.Post) { if (v.replies) { const replies = [] - let counter = 0 for (const item of v.replies) { // TODO: validate .record - const itemModel = new PostThreadViewPostModel(`item-${counter++}`, item) + const itemModel = new PostThreadViewPostModel(keyGen.next().value, item) + itemModel._depth = this._depth + 1 if (item.replies) { - itemModel.setReplies(item) + itemModel.setReplies(keyGen, item) } replies.push(itemModel) } @@ -146,8 +158,10 @@ export class PostThreadViewModel implements bsky.PostThreadView.Response { private _replaceAll(res: bsky.PostThreadView.Response) { // TODO: validate .record - const thread = new PostThreadViewPostModel('item-0', res.thread) - thread.setReplies(res.thread) + const keyGen = reactKeyGenerator() + const thread = new PostThreadViewPostModel(keyGen.next().value, res.thread) + thread._isHighlightedPost = true + thread.setReplies(keyGen, res.thread) this.thread = thread } } diff --git a/src/view/com/feed/FeedItem.tsx b/src/view/com/feed/FeedItem.tsx index 7a57326f6..18af53dde 100644 --- a/src/view/com/feed/FeedItem.tsx +++ b/src/view/com/feed/FeedItem.tsx @@ -13,6 +13,7 @@ import moment from 'moment' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {OnNavigateContent} from '../../routes/types' import {FeedViewItemModel} from '../../../state/models/feed-view' +import {s} from '../../lib/styles' const IMAGES: Record<string, ImageSourcePropType> = { 'alice.com': require('../../assets/alice.jpg'), @@ -39,8 +40,11 @@ export const FeedItem = observer(function FeedItem({ <TouchableOpacity style={styles.outer} onPress={onPressOuter}> {item.repostedBy && ( <View style={styles.repostedBy}> - <FontAwesomeIcon icon="retweet" style={styles.repostedByIcon} /> - <Text style={styles.repostedByText}> + <FontAwesomeIcon + icon="retweet" + style={[styles.repostedByIcon, s.gray]} + /> + <Text style={[s.gray, s.bold, s.f13]}> Reposted by {item.repostedBy.displayName} </Text> </View> @@ -54,28 +58,30 @@ export const FeedItem = observer(function FeedItem({ </View> <View style={styles.layoutContent}> <View style={styles.meta}> - <Text style={[styles.metaItem, styles.metaDisplayName]}> + <Text style={[styles.metaItem, s.f15, s.bold]}> {item.author.displayName} </Text> - <Text style={[styles.metaItem, styles.metaName]}> + <Text style={[styles.metaItem, s.f14, s.gray]}> @{item.author.name} </Text> - <Text style={[styles.metaItem, styles.metaDate]}> + <Text style={[styles.metaItem, s.f14, s.gray]}> · {moment(item.indexedAt).fromNow(true)} </Text> </View> - <Text style={styles.postText}>{record.text}</Text> + <Text style={[styles.postText, s.f15, s['lh15-1.3']]}> + {record.text} + </Text> <View style={styles.ctrls}> <View style={styles.ctrl}> <FontAwesomeIcon - style={styles.ctrlReplyIcon} + style={[styles.ctrlIcon, s.gray]} icon={['far', 'comment']} /> <Text>{item.replyCount}</Text> </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={styles.ctrlRepostIcon} + style={[styles.ctrlIcon, s.gray]} icon="retweet" size={22} /> @@ -83,14 +89,14 @@ export const FeedItem = observer(function FeedItem({ </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={styles.ctrlLikeIcon} + style={[styles.ctrlIcon, s.gray]} icon={['far', 'heart']} /> <Text>{item.likeCount}</Text> </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={styles.ctrlShareIcon} + style={[styles.ctrlIcon, s.gray]} icon="share-from-square" /> </View> @@ -114,12 +120,6 @@ const styles = StyleSheet.create({ }, repostedByIcon: { marginRight: 2, - color: 'gray', - }, - repostedByText: { - color: 'gray', - fontWeight: 'bold', - fontSize: 13, }, layout: { flexDirection: 'row', @@ -144,20 +144,7 @@ const styles = StyleSheet.create({ metaItem: { paddingRight: 5, }, - metaDisplayName: { - fontSize: 15, - fontWeight: 'bold', - }, - metaName: { - fontSize: 14, - color: 'gray', - }, - metaDate: { - fontSize: 14, - color: 'gray', - }, postText: { - fontSize: 15, paddingBottom: 5, }, ctrls: { @@ -170,20 +157,7 @@ const styles = StyleSheet.create({ paddingLeft: 4, paddingRight: 4, }, - ctrlReplyIcon: { - marginRight: 5, - color: 'gray', - }, - ctrlRepostIcon: { - marginRight: 5, - color: 'gray', - }, - ctrlLikeIcon: { - marginRight: 5, - color: 'gray', - }, - ctrlShareIcon: { + ctrlIcon: { marginRight: 5, - color: 'gray', }, }) diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index bc6642e07..7bbad36be 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -62,7 +62,6 @@ export const PostThread = observer(function PostThread({ } return ( <View> - {view.isRefreshing && <ActivityIndicator />} {view.hasContent && ( <FlatList data={posts} diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 33857f48a..985d11dfa 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -13,6 +13,8 @@ import moment from 'moment' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {OnNavigateContent} from '../../routes/types' import {PostThreadViewPostModel} from '../../../state/models/post-thread-view' +import {s} from '../../lib/styles' +import {pluralize} from '../../lib/strings' const IMAGES: Record<string, ImageSourcePropType> = { 'alice.com': require('../../assets/alice.jpg'), @@ -20,6 +22,14 @@ const IMAGES: Record<string, ImageSourcePropType> = { 'carla.com': require('../../assets/carla.jpg'), } +function iter<T>(n: number, fn: (i: number) => T): Array<T> { + const arr: T[] = [] + for (let i = 0; i < n; i++) { + arr.push(fn(i)) + } + return arr +} + export const PostThreadItem = observer(function PostThreadItem({ item, // onNavigateContent, }: { @@ -27,12 +37,16 @@ export const PostThreadItem = observer(function PostThreadItem({ onNavigateContent: OnNavigateContent }) { const record = item.record as unknown as bsky.Post.Record + const hasEngagement = item.likeCount || item.repostCount const onPressOuter = () => { // TODO onNavigateContent } return ( <TouchableOpacity style={styles.outer} onPress={onPressOuter}> <View style={styles.layout}> + {iter(item._depth, () => ( + <View style={styles.replyBar} /> + ))} <View style={styles.layoutAvi}> <Image style={styles.avi} @@ -41,28 +55,58 @@ export const PostThreadItem = observer(function PostThreadItem({ </View> <View style={styles.layoutContent}> <View style={styles.meta}> - <Text style={[styles.metaItem, styles.metaDisplayName]}> + <Text style={[styles.metaItem, s.f15, s.bold]}> {item.author.displayName} </Text> - <Text style={[styles.metaItem, styles.metaName]}> + <Text style={[styles.metaItem, s.f14, s.gray]}> @{item.author.name} </Text> - <Text style={[styles.metaItem, styles.metaDate]}> + <Text style={[styles.metaItem, s.f14, s.gray]}> · {moment(item.indexedAt).fromNow(true)} </Text> </View> - <Text style={styles.postText}>{record.text}</Text> + <Text + style={[ + styles.postText, + ...(item._isHighlightedPost + ? [s.f16, s['lh16-1.3']] + : [s.f15, s['lh15-1.3']]), + ]}> + {record.text} + </Text> + {item._isHighlightedPost && hasEngagement ? ( + <View style={styles.expandedInfo}> + {item.repostCount ? ( + <Text style={[styles.expandedInfoItem, s.gray, s.semiBold]}> + <Text style={[s.bold, s.black]}>{item.repostCount}</Text>{' '} + {pluralize(item.repostCount, 'repost')} + </Text> + ) : ( + <></> + )} + {item.likeCount ? ( + <Text style={[styles.expandedInfoItem, s.gray, s.semiBold]}> + <Text style={[s.bold, s.black]}>{item.likeCount}</Text>{' '} + {pluralize(item.likeCount, 'like')} + </Text> + ) : ( + <></> + )} + </View> + ) : ( + <></> + )} <View style={styles.ctrls}> <View style={styles.ctrl}> <FontAwesomeIcon - style={styles.ctrlReplyIcon} + style={[styles.ctrlIcon, s.gray]} icon={['far', 'comment']} /> <Text>{item.replyCount}</Text> </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={styles.ctrlRepostIcon} + style={[styles.ctrlIcon, s.gray]} icon="retweet" size={22} /> @@ -70,14 +114,14 @@ export const PostThreadItem = observer(function PostThreadItem({ </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={styles.ctrlLikeIcon} + style={[styles.ctrlIcon, s.gray]} icon={['far', 'heart']} /> <Text>{item.likeCount}</Text> </View> <View style={styles.ctrl}> <FontAwesomeIcon - style={styles.ctrlShareIcon} + style={[styles.ctrlIcon, s.gray]} icon="share-from-square" /> </View> @@ -93,26 +137,20 @@ const styles = StyleSheet.create({ borderTopWidth: 1, borderTopColor: '#e8e8e8', backgroundColor: '#fff', - padding: 10, }, - repostedBy: { + layout: { flexDirection: 'row', - paddingLeft: 70, }, - repostedByIcon: { + replyBar: { + width: 5, + backgroundColor: '#d4f0ff', marginRight: 2, - color: 'gray', - }, - repostedByText: { - color: 'gray', - fontWeight: 'bold', - fontSize: 13, - }, - layout: { - flexDirection: 'row', }, layoutAvi: { - width: 70, + width: 80, + paddingLeft: 10, + paddingTop: 10, + paddingBottom: 10, }, avi: { width: 60, @@ -122,6 +160,9 @@ const styles = StyleSheet.create({ }, layoutContent: { flex: 1, + paddingRight: 10, + paddingTop: 10, + paddingBottom: 10, }, meta: { flexDirection: 'row', @@ -131,22 +172,21 @@ const styles = StyleSheet.create({ metaItem: { paddingRight: 5, }, - metaDisplayName: { - fontSize: 15, - fontWeight: 'bold', - }, - metaName: { - fontSize: 14, - color: 'gray', - }, - metaDate: { - fontSize: 14, - color: 'gray', - }, postText: { - fontSize: 15, paddingBottom: 5, }, + expandedInfo: { + flexDirection: 'row', + padding: 10, + borderColor: '#e8e8e8', + borderTopWidth: 1, + borderBottomWidth: 1, + marginTop: 5, + marginBottom: 10, + }, + expandedInfoItem: { + marginRight: 10, + }, ctrls: { flexDirection: 'row', }, @@ -157,20 +197,7 @@ const styles = StyleSheet.create({ paddingLeft: 4, paddingRight: 4, }, - ctrlReplyIcon: { - marginRight: 5, - color: 'gray', - }, - ctrlRepostIcon: { - marginRight: 5, - color: 'gray', - }, - ctrlLikeIcon: { - marginRight: 5, - color: 'gray', - }, - ctrlShareIcon: { + ctrlIcon: { marginRight: 5, - color: 'gray', }, }) diff --git a/src/view/lib/strings.ts b/src/view/lib/strings.ts new file mode 100644 index 000000000..1be1112b1 --- /dev/null +++ b/src/view/lib/strings.ts @@ -0,0 +1,9 @@ +export function pluralize(n: number, base: string, plural?: string): string { + if (n === 1) { + return base + } + if (plural) { + return plural + } + return base + 's' +} diff --git a/src/view/lib/styles.ts b/src/view/lib/styles.ts new file mode 100644 index 000000000..44ee2dba7 --- /dev/null +++ b/src/view/lib/styles.ts @@ -0,0 +1,37 @@ +import {StyleSheet} from 'react-native' + +export const s = StyleSheet.create({ + // font weights + fw600: {fontWeight: '600'}, + bold: {fontWeight: '600'}, + fw500: {fontWeight: '500'}, + semiBold: {fontWeight: '500'}, + fw400: {fontWeight: '400'}, + normal: {fontWeight: '400'}, + fw300: {fontWeight: '300'}, + light: {fontWeight: '300'}, + fw200: {fontWeight: '200'}, + + // font sizes + f13: {fontSize: 13}, + f14: {fontSize: 14}, + f15: {fontSize: 15}, + f16: {fontSize: 16}, + f18: {fontSize: 18}, + + // line heights + ['lh13-1']: {lineHeight: 13}, + ['lh13-1.3']: {lineHeight: 16.9}, // 1.3 of 13px + ['lh14-1']: {lineHeight: 14}, + ['lh14-1.3']: {lineHeight: 18.2}, // 1.3 of 14px + ['lh15-1']: {lineHeight: 15}, + ['lh15-1.3']: {lineHeight: 19.5}, // 1.3 of 15px + ['lh16-1']: {lineHeight: 16}, + ['lh16-1.3']: {lineHeight: 20.8}, // 1.3 of 16px + ['lh18-1']: {lineHeight: 18}, + ['lh18-1.3']: {lineHeight: 23.4}, // 1.3 of 18px + + // colors + black: {color: 'black'}, + gray: {color: 'gray'}, +}) |