diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/api/feed-manip.ts | 23 | ||||
-rw-r--r-- | src/state/models/feed-view.ts | 3 | ||||
-rw-r--r-- | src/view/com/posts/FeedItem.tsx | 37 | ||||
-rw-r--r-- | src/view/com/posts/FeedSlice.tsx | 83 |
4 files changed, 103 insertions, 43 deletions
diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts index 67ca8f952..f6b54a175 100644 --- a/src/lib/api/feed-manip.ts +++ b/src/lib/api/feed-manip.ts @@ -28,6 +28,7 @@ export class FeedViewPostsSlice { get isThread() { return ( this.items.length > 1 && + !this.items[0].reply && this.items.every( item => item.post.author.did === this.items[0].post.author.did, ) @@ -35,7 +36,7 @@ export class FeedViewPostsSlice { } get isReply() { - return this.items.length === 2 && !this.isThread + return this.items.length > 1 && !this.isThread } get rootItem() { @@ -49,6 +50,10 @@ export class FeedViewPostsSlice { return !!this.items.find(item => item.post.uri === uri) } + isNextInThread(uri: string) { + return this.items[this.items.length - 1].post.uri === uri + } + insert(item: FeedViewPost) { const selfReplyUri = getSelfReplyUri(item) const i = this.items.findIndex(item2 => item2.post.uri === selfReplyUri) @@ -102,7 +107,7 @@ export class FeedTuner { const selfReplyUri = getSelfReplyUri(item) if (selfReplyUri) { - const parent = slices.find(item2 => item2.containsUri(selfReplyUri)) + const parent = slices.find(item2 => item2.isNextInThread(selfReplyUri)) if (parent) { parent.insert(item) continue @@ -112,9 +117,14 @@ export class FeedTuner { } // remove any items already "seen" + const soonToBeSeenUris: Set<string> = new Set() for (let i = slices.length - 1; i >= 0; i--) { if (this.seenUris.has(slices[i].uri)) { slices.splice(i, 1) + } else { + for (const item of slices[i].items) { + soonToBeSeenUris.add(item.post.uri) + } } } @@ -124,9 +134,12 @@ export class FeedTuner { !slice.isThread && !slice.items[0].reason && slice.items[0].reply?.parent && - !this.seenUris.has(slice.items[0].reply?.parent.uri) + !this.seenUris.has(slice.items[0].reply?.parent.uri) && + !soonToBeSeenUris.has(slice.items[0].reply?.parent.uri) ) { + const uri = slice.items[0].reply?.parent.uri slice.flattenReplyParent() + soonToBeSeenUris.add(uri) } } @@ -168,14 +181,14 @@ export class FeedTuner { } static likedRepliesOnly(tuner: FeedTuner, slices: FeedViewPostsSlice[]) { - // remove any replies without any likes + // remove any replies without at least 2 likes for (let i = slices.length - 1; i >= 0; i--) { if (slices[i].isThread) { continue } const item = slices[i].rootItem const isRepost = Boolean(item.reason) - if (item.reply && !isRepost && item.post.upvoteCount === 0) { + if (item.reply && !isRepost && item.post.upvoteCount < 2) { slices.splice(i, 1) } } diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts index c412065dd..0fbfa515a 100644 --- a/src/state/models/feed-view.ts +++ b/src/state/models/feed-view.ts @@ -200,6 +200,7 @@ export class FeedSliceModel { get isThread() { return ( this.items.length > 1 && + !this.items[0].reply && this.items.every( item => item.post.author.did === this.items[0].post.author.did, ) @@ -207,7 +208,7 @@ export class FeedSliceModel { } get isReply() { - return this.items.length === 2 && !this.isThread + return this.items.length > 1 && !this.isThread } get rootItem() { diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 35a917591..573b92fd3 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -2,7 +2,6 @@ import React, {useMemo, useState} from 'react' import {observer} from 'mobx-react-lite' import {Linking, StyleSheet, View} from 'react-native' import Clipboard from '@react-native-clipboard/clipboard' -import Svg, {Circle, Line} from 'react-native-svg' import {AtUri} from '../../../third-party/uri' import { FontAwesomeIcon, @@ -253,32 +252,6 @@ export const FeedItem = observer(function ({ </View> </View> </Link> - {false /*isThreadChildElided*/ ? ( - <Link - style={[pal.view, styles.viewFullThread]} - href={itemHref} - title={itemTitle} - noFeedback> - <View style={styles.viewFullThreadDots}> - <Svg width="4" height="30"> - <Line - x1="2" - y1="0" - x2="2" - y2="8" - stroke={pal.colors.replyLine} - strokeWidth="2" - /> - <Circle x="2" y="14" r="1.5" fill={pal.colors.replyLineDot} /> - <Circle x="2" y="20" r="1.5" fill={pal.colors.replyLineDot} /> - <Circle x="2" y="26" r="1.5" fill={pal.colors.replyLineDot} /> - </Svg> - </View> - <Text type="md" style={pal.link}> - View full thread - </Text> - </Link> - ) : undefined} </PostMutedWrapper> ) }) @@ -348,14 +321,4 @@ const styles = StyleSheet.create({ ctrls: { marginTop: 4, }, - viewFullThread: { - paddingTop: 12, - paddingBottom: 2, - paddingLeft: 80, - }, - viewFullThreadDots: { - position: 'absolute', - left: 41, - top: 0, - }, }) diff --git a/src/view/com/posts/FeedSlice.tsx b/src/view/com/posts/FeedSlice.tsx index 1dba8ac93..4df6d4f54 100644 --- a/src/view/com/posts/FeedSlice.tsx +++ b/src/view/com/posts/FeedSlice.tsx @@ -1,6 +1,12 @@ import React from 'react' +import {StyleSheet, View} from 'react-native' import {FeedSliceModel} from 'state/models/feed-view' +import {AtUri} from '../../../third-party/uri' +import {Link} from '../util/Link' +import {Text} from '../util/text/Text' +import Svg, {Circle, Line} from 'react-native-svg' import {FeedItem} from './FeedItem' +import {usePalette} from 'lib/hooks/usePalette' export function FeedSlice({ slice, @@ -11,6 +17,39 @@ export function FeedSlice({ showFollowBtn?: boolean ignoreMuteFor?: string }) { + if (slice.isThread && slice.items.length > 3) { + const last = slice.items.length - 1 + return ( + <> + <FeedItem + key={slice.items[0]._reactKey} + item={slice.items[0]} + isThreadParent={slice.isThreadParentAt(0)} + isThreadChild={slice.isThreadChildAt(0)} + showFollowBtn={showFollowBtn} + ignoreMuteFor={ignoreMuteFor} + /> + <FeedItem + key={slice.items[1]._reactKey} + item={slice.items[1]} + isThreadParent={slice.isThreadParentAt(1)} + isThreadChild={slice.isThreadChildAt(1)} + showFollowBtn={showFollowBtn} + ignoreMuteFor={ignoreMuteFor} + /> + <ViewFullThread slice={slice} /> + <FeedItem + key={slice.items[last]._reactKey} + item={slice.items[last]} + isThreadParent={slice.isThreadParentAt(last)} + isThreadChild={slice.isThreadChildAt(last)} + showFollowBtn={showFollowBtn} + ignoreMuteFor={ignoreMuteFor} + /> + </> + ) + } + return ( <> {slice.items.map((item, i) => ( @@ -26,3 +65,47 @@ export function FeedSlice({ </> ) } + +function ViewFullThread({slice}: {slice: FeedSliceModel}) { + const pal = usePalette('default') + const itemHref = React.useMemo(() => { + const urip = new AtUri(slice.rootItem.post.uri) + return `/profile/${slice.rootItem.post.author.handle}/post/${urip.rkey}` + }, [slice.rootItem.post.uri, slice.rootItem.post.author.handle]) + + return ( + <Link style={[pal.view, styles.viewFullThread]} href={itemHref} noFeedback> + <View style={styles.viewFullThreadDots}> + <Svg width="4" height="30"> + <Line + x1="2" + y1="0" + x2="2" + y2="8" + stroke={pal.colors.replyLine} + strokeWidth="2" + /> + <Circle x="2" y="16" r="1.5" fill={pal.colors.replyLineDot} /> + <Circle x="2" y="22" r="1.5" fill={pal.colors.replyLineDot} /> + <Circle x="2" y="28" r="1.5" fill={pal.colors.replyLineDot} /> + </Svg> + </View> + <Text type="md" style={pal.link}> + View full thread + </Text> + </Link> + ) +} + +const styles = StyleSheet.create({ + viewFullThread: { + paddingTop: 14, + paddingBottom: 6, + paddingLeft: 80, + }, + viewFullThreadDots: { + position: 'absolute', + left: 41, + top: 0, + }, +}) |