diff --git a/package.json b/package.json index abc02683..6f70f08b 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "react-native-progress": "^5.0.0", "react-native-reanimated": "^2.9.1", "react-native-root-siblings": "^4.1.1", - "react-native-root-toast": "^3.4.0", "react-native-safe-area-context": "^4.4.1", "react-native-screens": "^3.13.1", "react-native-splash-screen": "^3.3.0", diff --git a/src/lib/styles.ts b/src/lib/styles.ts index a8c38761..dbce3917 100644 --- a/src/lib/styles.ts +++ b/src/lib/styles.ts @@ -15,7 +15,7 @@ export const colors = { gray5: '#545664', gray6: '#373942', gray7: '#26272D', - gray8: '#101013', + gray8: '#141417', blue0: '#bfe1ff', blue1: '#8bc7fd', @@ -24,6 +24,7 @@ export const colors = { blue4: '#0062bd', blue5: '#034581', blue6: '#012561', + blue7: '#001040', red1: '#ffe6f2', red2: '#fba2ce', @@ -64,6 +65,7 @@ export const s = StyleSheet.create({ // helpers footerSpacer: {height: 100}, contentContainer: {paddingBottom: 200}, + contentContainerExtra: {paddingBottom: 300}, border1: {borderWidth: 1}, borderTop1: {borderTopWidth: 1}, borderRight1: {borderRightWidth: 1}, diff --git a/src/lib/themes.ts b/src/lib/themes.ts index aa166e32..d7043ad2 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -21,6 +21,7 @@ export const defaultTheme: Theme = { replyLine: colors.gray2, replyLineDot: colors.gray3, unreadNotifBg: '#ebf6ff', + unreadNotifBorder: colors.blue1, postCtrl: '#71768A', brandText: '#0066FF', emptyStateIcon: '#B6B6C9', @@ -296,15 +297,16 @@ export const darkTheme: Theme = { textLight: colors.gray3, textInverted: colors.black, link: colors.blue3, - border: colors.gray6, - borderDark: colors.gray5, + border: colors.black, + borderDark: colors.gray6, icon: colors.gray4, // non-standard textVeryLight: colors.gray4, replyLine: colors.gray5, replyLineDot: colors.gray6, - unreadNotifBg: colors.blue5, + unreadNotifBg: colors.blue7, + unreadNotifBorder: colors.blue6, postCtrl: '#61657A', brandText: '#0085ff', emptyStateIcon: colors.gray4, diff --git a/src/state/models/me.ts b/src/state/models/me.ts index ea35cd02..077c6559 100644 --- a/src/state/models/me.ts +++ b/src/state/models/me.ts @@ -11,6 +11,8 @@ export class MeModel { displayName: string = '' description: string = '' avatar: string = '' + followsCount: number | undefined + followersCount: number | undefined mainFeed: FeedModel notifications: NotificationsViewModel follows: MyFollowsModel @@ -90,10 +92,14 @@ export class MeModel { this.displayName = profile.data.displayName || '' this.description = profile.data.description || '' this.avatar = profile.data.avatar || '' + this.followsCount = profile.data.followsCount + this.followersCount = profile.data.followersCount } else { this.displayName = '' this.description = '' this.avatar = '' + this.followsCount = profile.data.followsCount + this.followersCount = undefined } }) this.mainFeed.clear() diff --git a/src/state/models/post-thread-view.ts b/src/state/models/post-thread-view.ts index ad989cc5..d58ee691 100644 --- a/src/state/models/post-thread-view.ts +++ b/src/state/models/post-thread-view.ts @@ -21,6 +21,8 @@ export class PostThreadViewPostModel { _reactKey: string = '' _depth = 0 _isHighlightedPost = false + _showParentReplyLine = false + _showChildReplyLine = false _hasMore = false // data @@ -30,6 +32,14 @@ export class PostThreadViewPostModel { replies?: (PostThreadViewPostModel | GetPostThread.NotFoundPost)[] richText?: RichText + get uri() { + return this.post.uri + } + + get parentUri() { + return this.postRecord?.reply?.parent.uri + } + constructor( public rootStore: RootStoreModel, reactKey: string, @@ -65,6 +75,7 @@ export class PostThreadViewPostModel { assignTreeModels( keyGen: Generator, v: GetPostThread.ThreadViewPost, + higlightedPostUri: string, includeParent = true, includeChildren = true, ) { @@ -77,8 +88,16 @@ export class PostThreadViewPostModel { v.parent, ) parentModel._depth = this._depth - 1 + parentModel._showChildReplyLine = true if (v.parent.parent) { - parentModel.assignTreeModels(keyGen, v.parent, true, false) + parentModel._showParentReplyLine = true //parentModel.uri !== higlightedPostUri + parentModel.assignTreeModels( + keyGen, + v.parent, + higlightedPostUri, + true, + false, + ) } this.parent = parentModel } else if (GetPostThread.isNotFoundPost(v.parent)) { @@ -96,8 +115,17 @@ export class PostThreadViewPostModel { item, ) itemModel._depth = this._depth + 1 - if (item.replies) { - itemModel.assignTreeModels(keyGen, item, false, true) + itemModel._showParentReplyLine = + itemModel.parentUri !== higlightedPostUri + if (item.replies?.length) { + itemModel._showChildReplyLine = true + itemModel.assignTreeModels( + keyGen, + item, + higlightedPostUri, + false, + true, + ) } replies.push(itemModel) } else if (GetPostThread.isNotFoundPost(item)) { @@ -333,6 +361,7 @@ export class PostThreadViewModel { thread.assignTreeModels( keyGen, res.data.thread as GetPostThread.ThreadViewPost, + thread.uri, ) this.thread = thread } diff --git a/src/view/com/composer/Prompt.tsx b/src/view/com/composer/Prompt.tsx index 46a0cec6..88d5de2b 100644 --- a/src/view/com/composer/Prompt.tsx +++ b/src/view/com/composer/Prompt.tsx @@ -1,91 +1,45 @@ import React from 'react' -import {StyleSheet, TouchableOpacity, View} from 'react-native' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {StyleSheet, TouchableOpacity} from 'react-native' +import {UserAvatar} from '../util/UserAvatar' import {Text} from '../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' +import {useStores} from 'state/index' export function ComposePrompt({ - text = "What's up?", - btn = 'Post', - isReply = false, onPressCompose, }: { - text?: string - btn?: string - isReply?: boolean onPressCompose: (imagesOpen?: boolean) => void }) { + const store = useStores() const pal = usePalette('default') return ( onPressCompose()}> - {!isReply && ( - - )} - - - {text} - - - {isReply ? ( - - - {btn} - - - ) : ( - onPressCompose(true)}> - - - )} + + + Write your reply + ) } const styles = StyleSheet.create({ - iconLeft: { - marginLeft: 22, - marginRight: 2, - }, - iconRight: { - marginRight: 20, - }, - container: { - paddingVertical: 16, + prompt: { + paddingHorizontal: 20, + paddingTop: 10, + paddingBottom: 10, flexDirection: 'row', alignItems: 'center', borderTopWidth: 1, }, - containerReply: { - paddingVertical: 14, - paddingHorizontal: 10, - }, - avatar: { - width: 50, - }, - textContainer: { - marginLeft: 10, - flex: 1, - }, - btn: { - paddingVertical: 6, - paddingHorizontal: 14, - borderRadius: 30, + label: { + paddingLeft: 12, }, }) diff --git a/src/view/com/notifications/FeedItem.tsx b/src/view/com/notifications/FeedItem.tsx index 68f12b72..acd00a67 100644 --- a/src/view/com/notifications/FeedItem.tsx +++ b/src/view/com/notifications/FeedItem.tsx @@ -90,10 +90,10 @@ export const FeedItem = observer(function FeedItem({ style={ item.isRead ? undefined - : [ - styles.outerUnread, - {backgroundColor: pal.colors.unreadNotifBg}, - ] + : { + backgroundColor: pal.colors.unreadNotifBg, + borderColor: pal.colors.unreadNotifBorder, + } } /> @@ -152,7 +152,10 @@ export const FeedItem = observer(function FeedItem({ pal.border, item.isRead ? undefined - : [styles.outerUnread, {backgroundColor: pal.colors.unreadNotifBg}], + : { + backgroundColor: pal.colors.unreadNotifBg, + borderColor: pal.colors.unreadNotifBorder, + }, ]} href={itemHref} title={itemTitle} @@ -391,9 +394,6 @@ const styles = StyleSheet.create({ paddingRight: 15, borderTopWidth: 1, }, - outerUnread: { - borderColor: colors.blue1, - }, layout: { flexDirection: 'row', }, diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index a417012b..646d4b27 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -96,7 +96,7 @@ export const PostThread = observer(function PostThread({ onLayout={onLayout} onScrollToIndexFailed={onScrollToIndexFailed} style={s.hContentRegion} - contentContainerStyle={s.contentContainer} + contentContainerStyle={s.contentContainerExtra} /> ) }) diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 8eda0962..1413148a 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -21,8 +21,8 @@ import {useStores} from 'state/index' import {PostMeta} from '../util/PostMeta' import {PostEmbeds} from '../util/PostEmbeds' import {PostCtrls} from '../util/PostCtrls' +import {PostMutedWrapper} from '../util/PostMuted' import {ErrorMessage} from '../util/error/ErrorMessage' -import {ComposePrompt} from '../composer/Prompt' import {usePalette} from 'lib/hooks/usePalette' const PARENT_REPLY_LINE_LENGTH = 8 @@ -271,23 +271,17 @@ export const PostThreadItem = observer(function PostThreadItem({ - ) } else { return ( - <> + - {record.reply && ( + {item._showParentReplyLine && ( )} - {item.replies?.length !== 0 && ( + {item._showChildReplyLine && ( - {item.post.author.viewer?.muted ? ( - - - This post is by a muted account. - - ) : item.richText?.text ? ( + {item.richText?.text ? ( ) : undefined} - + ) } }) @@ -441,14 +430,6 @@ const styles = StyleSheet.create({ paddingRight: 5, maxWidth: 240, }, - mutedWarning: { - flexDirection: 'row', - alignItems: 'center', - padding: 10, - marginTop: 2, - marginBottom: 6, - borderRadius: 2, - }, postTextContainer: { flexDirection: 'row', alignItems: 'center', diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx index bf8dfed0..7b4161af 100644 --- a/src/view/com/post/Post.tsx +++ b/src/view/com/post/Post.tsx @@ -17,6 +17,7 @@ import {UserInfoText} from '../util/UserInfoText' import {PostMeta} from '../util/PostMeta' import {PostEmbeds} from '../util/PostEmbeds' import {PostCtrls} from '../util/PostCtrls' +import {PostMutedWrapper} from '../util/PostMuted' import {Text} from '../util/text/Text' import {RichText} from '../util/text/RichText' import * as Toast from '../util/Toast' @@ -140,92 +141,89 @@ export const Post = observer(function Post({ } return ( - - {showReplyLine && } - - - - + + {showReplyLine && } + + + + + + + + - + {replyAuthorDid !== '' && ( + + + + Reply to + + + + )} + {item.richText?.text ? ( + + + + ) : undefined} + + + - - - {replyAuthorDid !== '' && ( - - - - Reply to - - - - )} - {item.post.author.viewer?.muted ? ( - - - This post is by a muted account. - - ) : item.richText?.text ? ( - - - - ) : undefined} - - - - - + + ) }) @@ -245,14 +243,6 @@ const styles = StyleSheet.create({ layoutContent: { flex: 1, }, - mutedWarning: { - flexDirection: 'row', - alignItems: 'center', - padding: 10, - marginTop: 2, - marginBottom: 6, - borderRadius: 2, - }, postTextContainer: { flexDirection: 'row', alignItems: 'center', diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 1006645a..8b9a6eb2 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -15,6 +15,7 @@ import {UserInfoText} from '../util/UserInfoText' import {PostMeta} from '../util/PostMeta' import {PostCtrls} from '../util/PostCtrls' import {PostEmbeds} from '../util/PostEmbeds' +import {PostMutedWrapper} from '../util/PostMuted' import {RichText} from '../util/text/RichText' import * as Toast from '../util/Toast' import {UserAvatar} from '../util/UserAvatar' @@ -113,6 +114,8 @@ export const FeedItem = observer(function ({ item._isThreadChild || (!item.reason && !item._hideParent && item.reply) const isSmallTop = isChild && item._isThreadChild const isNoTop = isChild && !item._isThreadChild + const isMuted = + item.post.author.viewer?.muted && ignoreMuteFor !== item.post.author.did const outerStyles = [ styles.outer, pal.view, @@ -123,7 +126,7 @@ export const FeedItem = observer(function ({ ] return ( - <> + {isChild && !item._isThreadChild && item.replyParent ? ( - + Reposted by{' '} {item.reasonRepost.by.displayName || item.reasonRepost.by.handle} @@ -207,13 +214,7 @@ export const FeedItem = observer(function ({ /> )} - {item.post.author.viewer?.muted && - ignoreMuteFor !== item.post.author.did ? ( - - - This post is by a muted account. - - ) : item.richText?.text ? ( + {item.richText?.text ? ( ) : undefined} - {item.post.embed ? ( - - ) : null} + ) : undefined} - + ) }) @@ -319,6 +318,7 @@ const styles = StyleSheet.create({ includeReason: { flexDirection: 'row', paddingLeft: 50, + paddingRight: 20, marginTop: 2, marginBottom: 2, }, @@ -336,14 +336,6 @@ const styles = StyleSheet.create({ layoutContent: { flex: 1, }, - mutedWarning: { - flexDirection: 'row', - alignItems: 'center', - padding: 10, - marginTop: 2, - marginBottom: 6, - borderRadius: 2, - }, postTextContainer: { flexDirection: 'row', alignItems: 'center', diff --git a/src/view/com/util/PostMuted.tsx b/src/view/com/util/PostMuted.tsx new file mode 100644 index 00000000..d8573bd5 --- /dev/null +++ b/src/view/com/util/PostMuted.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import {StyleSheet, TouchableOpacity, View} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {usePalette} from 'lib/hooks/usePalette' +import {Text} from './text/Text' + +export function PostMutedWrapper({ + isMuted, + children, +}: React.PropsWithChildren<{isMuted: boolean}>) { + const pal = usePalette('default') + const [override, setOverride] = React.useState(false) + if (!isMuted || override) { + return <>{children} + } + return ( + + + + Post from an account you muted. + + setOverride(true)}> + + Show post + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 14, + paddingHorizontal: 18, + borderTopWidth: 1, + }, + icon: { + marginRight: 10, + }, + showBtn: { + marginLeft: 'auto', + }, +}) diff --git a/src/view/com/util/Toast.tsx b/src/view/com/util/Toast.tsx index 197f4742..34a461f8 100644 --- a/src/view/com/util/Toast.tsx +++ b/src/view/com/util/Toast.tsx @@ -1,11 +1,81 @@ -import Toast from 'react-native-root-toast' +import RootSiblings from 'react-native-root-siblings' +import React from 'react' +import {Animated, StyleSheet, View} from 'react-native' +import {Text} from './text/Text' +import {colors} from 'lib/styles' +import {useTheme} from 'lib/ThemeContext' +import {usePalette} from 'lib/hooks/usePalette' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' + +const TIMEOUT = 4e3 export function show(message: string) { - Toast.show(message, { - duration: Toast.durations.LONG, - position: 50, - shadow: true, - animation: true, - hideOnPress: true, - }) + const item = new RootSiblings() + setTimeout(() => { + item.destroy() + }, TIMEOUT) } + +function Toast({message}: {message: string}) { + const theme = useTheme() + const pal = usePalette('default') + const interp = useAnimatedValue(0) + + React.useEffect(() => { + Animated.sequence([ + Animated.timing(interp, { + toValue: 1, + duration: 150, + useNativeDriver: true, + }), + Animated.delay(3700), + Animated.timing(interp, { + toValue: 0, + duration: 150, + useNativeDriver: true, + }), + ]).start() + }) + + const opacityStyle = {opacity: interp} + return ( + + + + {message} + + + + ) +} + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + top: 60, + left: 0, + right: 0, + alignItems: 'center', + }, + toast: { + paddingHorizontal: 18, + paddingVertical: 10, + borderRadius: 24, + borderWidth: 1, + shadowColor: '#000', + shadowOpacity: 0.1, + shadowOffset: {width: 0, height: 4}, + marginHorizontal: 6, + }, + toastDark: { + backgroundColor: colors.gray6, + shadowOpacity: 0.5, + }, +}) diff --git a/src/view/com/util/UserInfoText.tsx b/src/view/com/util/UserInfoText.tsx index 2655232f..84170b3b 100644 --- a/src/view/com/util/UserInfoText.tsx +++ b/src/view/com/util/UserInfoText.tsx @@ -58,15 +58,15 @@ export function UserInfoText({ let inner if (didFail) { inner = ( - + {failed} ) } else if (profile) { inner = ( - {`${prefix || ''}${ - profile[attr] || profile.handle - }`} + {`${ + prefix || '' + }${profile[attr] || profile.handle}`} ) } else { inner = ( diff --git a/src/view/screens/Debug.tsx b/src/view/screens/Debug.tsx index f2349195..eb5ffe20 100644 --- a/src/view/screens/Debug.tsx +++ b/src/view/screens/Debug.tsx @@ -5,6 +5,7 @@ import {ThemeProvider, PaletteColorName} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {displayNotification} from 'lib/notifee' +import * as Toast from 'view/com/util/Toast' import {Text} from '../com/util/text/Text' import {ViewSelector} from '../com/util/ViewSelector' @@ -171,16 +172,24 @@ function ErrorView() { } function NotifsView() { - const trigger = () => { + const triggerPush = () => { displayNotification( 'Paul Frazee liked your post', "Hello world! This is a test of the notifications card. The text is long to see how that's handled.", ) } + const triggerToast = () => { + Toast.show('The task has been completed') + } + const triggerToast2 = () => { + Toast.show('The task has been completed successfully and with no problems') + } return ( -