Move muted threads to new persistence + context (#1838)
parent
4afed4be28
commit
74f8390f1d
|
@ -22,6 +22,7 @@ import * as Toast from 'view/com/util/Toast'
|
||||||
import {queryClient} from 'lib/react-query'
|
import {queryClient} from 'lib/react-query'
|
||||||
import {TestCtrls} from 'view/com/testing/TestCtrls'
|
import {TestCtrls} from 'view/com/testing/TestCtrls'
|
||||||
import {Provider as ShellStateProvider} from 'state/shell'
|
import {Provider as ShellStateProvider} from 'state/shell'
|
||||||
|
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
||||||
|
|
||||||
SplashScreen.preventAutoHideAsync()
|
SplashScreen.preventAutoHideAsync()
|
||||||
|
|
||||||
|
@ -78,7 +79,9 @@ function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShellStateProvider>
|
<ShellStateProvider>
|
||||||
|
<MutedThreadsProvider>
|
||||||
<InnerApp />
|
<InnerApp />
|
||||||
|
</MutedThreadsProvider>
|
||||||
</ShellStateProvider>
|
</ShellStateProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {ToastContainer} from 'view/com/util/Toast.web'
|
||||||
import {ThemeProvider} from 'lib/ThemeContext'
|
import {ThemeProvider} from 'lib/ThemeContext'
|
||||||
import {queryClient} from 'lib/react-query'
|
import {queryClient} from 'lib/react-query'
|
||||||
import {Provider as ShellStateProvider} from 'state/shell'
|
import {Provider as ShellStateProvider} from 'state/shell'
|
||||||
|
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
|
||||||
|
|
||||||
const InnerApp = observer(function AppImpl() {
|
const InnerApp = observer(function AppImpl() {
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
@ -68,7 +69,9 @@ function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShellStateProvider>
|
<ShellStateProvider>
|
||||||
|
<MutedThreadsProvider>
|
||||||
<InnerApp />
|
<InnerApp />
|
||||||
|
</MutedThreadsProvider>
|
||||||
</ShellStateProvider>
|
</ShellStateProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,10 +63,6 @@ export class PostThreadItemModel {
|
||||||
return this.post.uri
|
return this.post.uri
|
||||||
}
|
}
|
||||||
|
|
||||||
get isThreadMuted() {
|
|
||||||
return this.data.isThreadMuted
|
|
||||||
}
|
|
||||||
|
|
||||||
get moderation(): PostModeration {
|
get moderation(): PostModeration {
|
||||||
return this.data.moderation
|
return this.data.moderation
|
||||||
}
|
}
|
||||||
|
@ -129,10 +125,6 @@ export class PostThreadItemModel {
|
||||||
this.data.toggleRepost()
|
this.data.toggleRepost()
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleThreadMute() {
|
|
||||||
this.data.toggleThreadMute()
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete() {
|
async delete() {
|
||||||
this.data.delete()
|
this.data.delete()
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,10 +74,6 @@ export class PostThreadModel {
|
||||||
return this.resolvedUri
|
return this.resolvedUri
|
||||||
}
|
}
|
||||||
|
|
||||||
get isThreadMuted() {
|
|
||||||
return this.rootStore.mutedThreads.uris.has(this.rootUri)
|
|
||||||
}
|
|
||||||
|
|
||||||
get isCachedPostAReply() {
|
get isCachedPostAReply() {
|
||||||
if (AppBskyFeedPost.isRecord(this.thread?.post.record)) {
|
if (AppBskyFeedPost.isRecord(this.thread?.post.record)) {
|
||||||
return !!this.thread?.post.record.reply
|
return !!this.thread?.post.record.reply
|
||||||
|
@ -140,14 +136,6 @@ export class PostThreadModel {
|
||||||
this.refresh()
|
this.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleThreadMute() {
|
|
||||||
if (this.isThreadMuted) {
|
|
||||||
this.rootStore.mutedThreads.uris.delete(this.rootUri)
|
|
||||||
} else {
|
|
||||||
this.rootStore.mutedThreads.uris.add(this.rootUri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// state transitions
|
// state transitions
|
||||||
// =
|
// =
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {RootStoreModel} from '../root-store'
|
||||||
import {PostThreadModel} from '../content/post-thread'
|
import {PostThreadModel} from '../content/post-thread'
|
||||||
import {cleanError} from 'lib/strings/errors'
|
import {cleanError} from 'lib/strings/errors'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {isThreadMuted} from '#/state/muted-threads'
|
||||||
|
|
||||||
const GROUPABLE_REASONS = ['like', 'repost', 'follow']
|
const GROUPABLE_REASONS = ['like', 'repost', 'follow']
|
||||||
const PAGE_SIZE = 30
|
const PAGE_SIZE = 30
|
||||||
|
@ -550,8 +551,7 @@ export class NotificationsFeedModel {
|
||||||
.filter(item => {
|
.filter(item => {
|
||||||
const hideByLabel = item.shouldFilter
|
const hideByLabel = item.shouldFilter
|
||||||
let mutedThread = !!(
|
let mutedThread = !!(
|
||||||
item.reasonSubjectRootUri &&
|
item.reasonSubjectRootUri && isThreadMuted(item.reasonSubjectRootUri)
|
||||||
this.rootStore.mutedThreads.uris.has(item.reasonSubjectRootUri)
|
|
||||||
)
|
)
|
||||||
return !hideByLabel && !mutedThread
|
return !hideByLabel && !mutedThread
|
||||||
})
|
})
|
||||||
|
|
|
@ -75,10 +75,6 @@ export class PostsFeedItemModel {
|
||||||
return this.post.uri
|
return this.post.uri
|
||||||
}
|
}
|
||||||
|
|
||||||
get isThreadMuted() {
|
|
||||||
return this.rootStore.mutedThreads.uris.has(this.rootUri)
|
|
||||||
}
|
|
||||||
|
|
||||||
get moderation(): PostModeration {
|
get moderation(): PostModeration {
|
||||||
return moderatePost(this.post, this.rootStore.preferences.moderationOpts)
|
return moderatePost(this.post, this.rootStore.preferences.moderationOpts)
|
||||||
}
|
}
|
||||||
|
@ -172,20 +168,6 @@ export class PostsFeedItemModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleThreadMute() {
|
|
||||||
try {
|
|
||||||
if (this.isThreadMuted) {
|
|
||||||
this.rootStore.mutedThreads.uris.delete(this.rootUri)
|
|
||||||
track('Post:ThreadUnmute')
|
|
||||||
} else {
|
|
||||||
this.rootStore.mutedThreads.uris.add(this.rootUri)
|
|
||||||
track('Post:ThreadMute')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Failed to toggle thread mute', {error})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete() {
|
async delete() {
|
||||||
try {
|
try {
|
||||||
await this.rootStore.agent.deletePost(this.post.uri)
|
await this.rootStore.agent.deletePost(this.post.uri)
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
/**
|
|
||||||
* This is a temporary client-side system for storing muted threads
|
|
||||||
* When the system lands on prod we should switch to that
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {makeAutoObservable} from 'mobx'
|
|
||||||
import {isObj, hasProp, isStrArray} from 'lib/type-guards'
|
|
||||||
|
|
||||||
export class MutedThreads {
|
|
||||||
uris: Set<string> = new Set()
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
makeAutoObservable(
|
|
||||||
this,
|
|
||||||
{serialize: false, hydrate: false},
|
|
||||||
{autoBind: true},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize() {
|
|
||||||
return {uris: Array.from(this.uris)}
|
|
||||||
}
|
|
||||||
|
|
||||||
hydrate(v: unknown) {
|
|
||||||
if (isObj(v) && hasProp(v, 'uris') && isStrArray(v.uris)) {
|
|
||||||
this.uris = new Set(v.uris)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,7 +19,6 @@ import {InvitedUsers} from './invited-users'
|
||||||
import {PreferencesModel} from './ui/preferences'
|
import {PreferencesModel} from './ui/preferences'
|
||||||
import {resetToTab} from '../../Navigation'
|
import {resetToTab} from '../../Navigation'
|
||||||
import {ImageSizesCache} from './cache/image-sizes'
|
import {ImageSizesCache} from './cache/image-sizes'
|
||||||
import {MutedThreads} from './muted-threads'
|
|
||||||
import {reset as resetNavigation} from '../../Navigation'
|
import {reset as resetNavigation} from '../../Navigation'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
|
||||||
|
@ -49,7 +48,6 @@ export class RootStoreModel {
|
||||||
posts = new PostsCache(this)
|
posts = new PostsCache(this)
|
||||||
linkMetas = new LinkMetasCache(this)
|
linkMetas = new LinkMetasCache(this)
|
||||||
imageSizes = new ImageSizesCache()
|
imageSizes = new ImageSizesCache()
|
||||||
mutedThreads = new MutedThreads()
|
|
||||||
|
|
||||||
constructor(agent: BskyAgent) {
|
constructor(agent: BskyAgent) {
|
||||||
this.agent = agent
|
this.agent = agent
|
||||||
|
@ -71,7 +69,6 @@ export class RootStoreModel {
|
||||||
me: this.me.serialize(),
|
me: this.me.serialize(),
|
||||||
preferences: this.preferences.serialize(),
|
preferences: this.preferences.serialize(),
|
||||||
invitedUsers: this.invitedUsers.serialize(),
|
invitedUsers: this.invitedUsers.serialize(),
|
||||||
mutedThreads: this.mutedThreads.serialize(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,9 +92,6 @@ export class RootStoreModel {
|
||||||
if (hasProp(v, 'invitedUsers')) {
|
if (hasProp(v, 'invitedUsers')) {
|
||||||
this.invitedUsers.hydrate(v.invitedUsers)
|
this.invitedUsers.hydrate(v.invitedUsers)
|
||||||
}
|
}
|
||||||
if (hasProp(v, 'mutedThreads')) {
|
|
||||||
this.mutedThreads.hydrate(v.mutedThreads)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import React from 'react'
|
||||||
|
import * as persisted from '#/state/persisted'
|
||||||
|
|
||||||
|
type StateContext = persisted.Schema['mutedThreads']
|
||||||
|
type ToggleContext = (uri: string) => boolean
|
||||||
|
|
||||||
|
const stateContext = React.createContext<StateContext>(
|
||||||
|
persisted.defaults.mutedThreads,
|
||||||
|
)
|
||||||
|
const toggleContext = React.createContext<ToggleContext>((_: string) => false)
|
||||||
|
|
||||||
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
const [state, setState] = React.useState(persisted.get('mutedThreads'))
|
||||||
|
|
||||||
|
const toggleThreadMute = React.useCallback(
|
||||||
|
(uri: string) => {
|
||||||
|
let muted = false
|
||||||
|
setState((arr: string[]) => {
|
||||||
|
if (arr.includes(uri)) {
|
||||||
|
arr = arr.filter(v => v !== uri)
|
||||||
|
muted = false
|
||||||
|
} else {
|
||||||
|
arr = arr.concat([uri])
|
||||||
|
muted = true
|
||||||
|
}
|
||||||
|
persisted.write('mutedThreads', arr)
|
||||||
|
return arr
|
||||||
|
})
|
||||||
|
return muted
|
||||||
|
},
|
||||||
|
[setState],
|
||||||
|
)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
return persisted.onUpdate(() => {
|
||||||
|
setState(persisted.get('mutedThreads'))
|
||||||
|
})
|
||||||
|
}, [setState])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<stateContext.Provider value={state}>
|
||||||
|
<toggleContext.Provider value={toggleThreadMute}>
|
||||||
|
{children}
|
||||||
|
</toggleContext.Provider>
|
||||||
|
</stateContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMutedThreads() {
|
||||||
|
return React.useContext(stateContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useToggleThreadMute() {
|
||||||
|
return React.useContext(toggleContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isThreadMuted(uri: string) {
|
||||||
|
return persisted.get('mutedThreads').includes(uri)
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {MAX_POST_LINES} from 'lib/constants'
|
import {MAX_POST_LINES} from 'lib/constants'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
|
||||||
|
|
||||||
export const PostThreadItem = observer(function PostThreadItem({
|
export const PostThreadItem = observer(function PostThreadItem({
|
||||||
item,
|
item,
|
||||||
|
@ -51,6 +52,8 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
const mutedThreads = useMutedThreads()
|
||||||
|
const toggleThreadMute = useToggleThreadMute()
|
||||||
const [deleted, setDeleted] = React.useState(false)
|
const [deleted, setDeleted] = React.useState(false)
|
||||||
const [limitLines, setLimitLines] = React.useState(
|
const [limitLines, setLimitLines] = React.useState(
|
||||||
countLines(item.richText?.text) >= MAX_POST_LINES,
|
countLines(item.richText?.text) >= MAX_POST_LINES,
|
||||||
|
@ -130,10 +133,10 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
Linking.openURL(translatorUrl)
|
Linking.openURL(translatorUrl)
|
||||||
}, [translatorUrl])
|
}, [translatorUrl])
|
||||||
|
|
||||||
const onToggleThreadMute = React.useCallback(async () => {
|
const onToggleThreadMute = React.useCallback(() => {
|
||||||
try {
|
try {
|
||||||
await item.toggleThreadMute()
|
const muted = toggleThreadMute(item.data.rootUri)
|
||||||
if (item.isThreadMuted) {
|
if (muted) {
|
||||||
Toast.show('You will no longer receive notifications for this thread')
|
Toast.show('You will no longer receive notifications for this thread')
|
||||||
} else {
|
} else {
|
||||||
Toast.show('You will now receive notifications for this thread')
|
Toast.show('You will now receive notifications for this thread')
|
||||||
|
@ -141,7 +144,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Failed to toggle thread mute', {error: e})
|
logger.error('Failed to toggle thread mute', {error: e})
|
||||||
}
|
}
|
||||||
}, [item])
|
}, [item, toggleThreadMute])
|
||||||
|
|
||||||
const onDeletePost = React.useCallback(() => {
|
const onDeletePost = React.useCallback(() => {
|
||||||
item.delete().then(
|
item.delete().then(
|
||||||
|
@ -284,7 +287,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
itemHref={itemHref}
|
itemHref={itemHref}
|
||||||
itemTitle={itemTitle}
|
itemTitle={itemTitle}
|
||||||
isAuthor={item.post.author.did === store.me.did}
|
isAuthor={item.post.author.did === store.me.did}
|
||||||
isThreadMuted={item.isThreadMuted}
|
isThreadMuted={mutedThreads.includes(item.data.rootUri)}
|
||||||
onCopyPostText={onCopyPostText}
|
onCopyPostText={onCopyPostText}
|
||||||
onOpenTranslate={onOpenTranslate}
|
onOpenTranslate={onOpenTranslate}
|
||||||
onToggleThreadMute={onToggleThreadMute}
|
onToggleThreadMute={onToggleThreadMute}
|
||||||
|
@ -391,7 +394,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
isAuthor={item.post.author.did === store.me.did}
|
isAuthor={item.post.author.did === store.me.did}
|
||||||
isReposted={!!item.post.viewer?.repost}
|
isReposted={!!item.post.viewer?.repost}
|
||||||
isLiked={!!item.post.viewer?.like}
|
isLiked={!!item.post.viewer?.like}
|
||||||
isThreadMuted={item.isThreadMuted}
|
isThreadMuted={mutedThreads.includes(item.data.rootUri)}
|
||||||
onPressReply={onPressReply}
|
onPressReply={onPressReply}
|
||||||
onPressToggleRepost={onPressToggleRepost}
|
onPressToggleRepost={onPressToggleRepost}
|
||||||
onPressToggleLike={onPressToggleLike}
|
onPressToggleLike={onPressToggleLike}
|
||||||
|
@ -534,7 +537,7 @@ export const PostThreadItem = observer(function PostThreadItem({
|
||||||
likeCount={item.post.likeCount}
|
likeCount={item.post.likeCount}
|
||||||
isReposted={!!item.post.viewer?.repost}
|
isReposted={!!item.post.viewer?.repost}
|
||||||
isLiked={!!item.post.viewer?.like}
|
isLiked={!!item.post.viewer?.like}
|
||||||
isThreadMuted={item.isThreadMuted}
|
isThreadMuted={mutedThreads.includes(item.data.rootUri)}
|
||||||
onPressReply={onPressReply}
|
onPressReply={onPressReply}
|
||||||
onPressToggleRepost={onPressToggleRepost}
|
onPressToggleRepost={onPressToggleRepost}
|
||||||
onPressToggleLike={onPressToggleLike}
|
onPressToggleLike={onPressToggleLike}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {MAX_POST_LINES} from 'lib/constants'
|
import {MAX_POST_LINES} from 'lib/constants'
|
||||||
import {countLines} from 'lib/strings/helpers'
|
import {countLines} from 'lib/strings/helpers'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
|
||||||
|
|
||||||
export const Post = observer(function PostImpl({
|
export const Post = observer(function PostImpl({
|
||||||
view,
|
view,
|
||||||
|
@ -106,6 +107,8 @@ const PostLoaded = observer(function PostLoadedImpl({
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
const mutedThreads = useMutedThreads()
|
||||||
|
const toggleThreadMute = useToggleThreadMute()
|
||||||
const [limitLines, setLimitLines] = React.useState(
|
const [limitLines, setLimitLines] = React.useState(
|
||||||
countLines(item.richText?.text) >= MAX_POST_LINES,
|
countLines(item.richText?.text) >= MAX_POST_LINES,
|
||||||
)
|
)
|
||||||
|
@ -161,10 +164,10 @@ const PostLoaded = observer(function PostLoadedImpl({
|
||||||
Linking.openURL(translatorUrl)
|
Linking.openURL(translatorUrl)
|
||||||
}, [translatorUrl])
|
}, [translatorUrl])
|
||||||
|
|
||||||
const onToggleThreadMute = React.useCallback(async () => {
|
const onToggleThreadMute = React.useCallback(() => {
|
||||||
try {
|
try {
|
||||||
await item.toggleThreadMute()
|
const muted = toggleThreadMute(item.data.rootUri)
|
||||||
if (item.isThreadMuted) {
|
if (muted) {
|
||||||
Toast.show('You will no longer receive notifications for this thread')
|
Toast.show('You will no longer receive notifications for this thread')
|
||||||
} else {
|
} else {
|
||||||
Toast.show('You will now receive notifications for this thread')
|
Toast.show('You will now receive notifications for this thread')
|
||||||
|
@ -172,7 +175,7 @@ const PostLoaded = observer(function PostLoadedImpl({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Failed to toggle thread mute', {error: e})
|
logger.error('Failed to toggle thread mute', {error: e})
|
||||||
}
|
}
|
||||||
}, [item])
|
}, [item, toggleThreadMute])
|
||||||
|
|
||||||
const onDeletePost = React.useCallback(() => {
|
const onDeletePost = React.useCallback(() => {
|
||||||
item.delete().then(
|
item.delete().then(
|
||||||
|
@ -286,7 +289,7 @@ const PostLoaded = observer(function PostLoadedImpl({
|
||||||
likeCount={item.post.likeCount}
|
likeCount={item.post.likeCount}
|
||||||
isReposted={!!item.post.viewer?.repost}
|
isReposted={!!item.post.viewer?.repost}
|
||||||
isLiked={!!item.post.viewer?.like}
|
isLiked={!!item.post.viewer?.like}
|
||||||
isThreadMuted={item.isThreadMuted}
|
isThreadMuted={mutedThreads.includes(item.data.rootUri)}
|
||||||
onPressReply={onPressReply}
|
onPressReply={onPressReply}
|
||||||
onPressToggleRepost={onPressToggleRepost}
|
onPressToggleRepost={onPressToggleRepost}
|
||||||
onPressToggleLike={onPressToggleLike}
|
onPressToggleLike={onPressToggleLike}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import {isEmbedByEmbedder} from 'lib/embeds'
|
||||||
import {MAX_POST_LINES} from 'lib/constants'
|
import {MAX_POST_LINES} from 'lib/constants'
|
||||||
import {countLines} from 'lib/strings/helpers'
|
import {countLines} from 'lib/strings/helpers'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
|
||||||
|
|
||||||
export const FeedItem = observer(function FeedItemImpl({
|
export const FeedItem = observer(function FeedItemImpl({
|
||||||
item,
|
item,
|
||||||
|
@ -50,6 +51,8 @@ export const FeedItem = observer(function FeedItemImpl({
|
||||||
}) {
|
}) {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
const mutedThreads = useMutedThreads()
|
||||||
|
const toggleThreadMute = useToggleThreadMute()
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
const [deleted, setDeleted] = useState(false)
|
const [deleted, setDeleted] = useState(false)
|
||||||
const [limitLines, setLimitLines] = useState(
|
const [limitLines, setLimitLines] = useState(
|
||||||
|
@ -114,11 +117,11 @@ export const FeedItem = observer(function FeedItemImpl({
|
||||||
Linking.openURL(translatorUrl)
|
Linking.openURL(translatorUrl)
|
||||||
}, [translatorUrl])
|
}, [translatorUrl])
|
||||||
|
|
||||||
const onToggleThreadMute = React.useCallback(async () => {
|
const onToggleThreadMute = React.useCallback(() => {
|
||||||
track('FeedItem:ThreadMute')
|
track('FeedItem:ThreadMute')
|
||||||
try {
|
try {
|
||||||
await item.toggleThreadMute()
|
const muted = toggleThreadMute(item.rootUri)
|
||||||
if (item.isThreadMuted) {
|
if (muted) {
|
||||||
Toast.show('You will no longer receive notifications for this thread')
|
Toast.show('You will no longer receive notifications for this thread')
|
||||||
} else {
|
} else {
|
||||||
Toast.show('You will now receive notifications for this thread')
|
Toast.show('You will now receive notifications for this thread')
|
||||||
|
@ -126,7 +129,7 @@ export const FeedItem = observer(function FeedItemImpl({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Failed to toggle thread mute', {error: e})
|
logger.error('Failed to toggle thread mute', {error: e})
|
||||||
}
|
}
|
||||||
}, [track, item])
|
}, [track, toggleThreadMute, item])
|
||||||
|
|
||||||
const onDeletePost = React.useCallback(() => {
|
const onDeletePost = React.useCallback(() => {
|
||||||
track('FeedItem:PostDelete')
|
track('FeedItem:PostDelete')
|
||||||
|
@ -360,7 +363,7 @@ export const FeedItem = observer(function FeedItemImpl({
|
||||||
likeCount={item.post.likeCount}
|
likeCount={item.post.likeCount}
|
||||||
isReposted={!!item.post.viewer?.repost}
|
isReposted={!!item.post.viewer?.repost}
|
||||||
isLiked={!!item.post.viewer?.like}
|
isLiked={!!item.post.viewer?.like}
|
||||||
isThreadMuted={item.isThreadMuted}
|
isThreadMuted={mutedThreads.includes(item.rootUri)}
|
||||||
onPressReply={onPressReply}
|
onPressReply={onPressReply}
|
||||||
onPressToggleRepost={onPressToggleRepost}
|
onPressToggleRepost={onPressToggleRepost}
|
||||||
onPressToggleLike={onPressToggleLike}
|
onPressToggleLike={onPressToggleLike}
|
||||||
|
|
Loading…
Reference in New Issue