Implement account muting

zio/stable
Paul Frazee 2023-01-02 13:40:14 -06:00
parent 3972706c54
commit 8cd2b4a721
12 changed files with 101 additions and 15 deletions

View File

@ -13,7 +13,7 @@
"postinstall": "patch-package" "postinstall": "patch-package"
}, },
"dependencies": { "dependencies": {
"@atproto/api": "^0.0.3", "@atproto/api": "^0.0.4",
"@bam.tech/react-native-image-resizer": "^3.0.4", "@bam.tech/react-native-image-resizer": "^3.0.4",
"@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1",

View File

@ -72,7 +72,6 @@ export async function resize(localUri: string, opts: ResizeOpts) {
undefined, undefined,
{mode: opts.mode}, {mode: opts.mode},
) )
console.log(quality, resizeRes)
if (resizeRes.size < opts.maxSize) { if (resizeRes.size < opts.maxSize) {
return resizeRes return resizeRes
} }

View File

@ -77,7 +77,7 @@ export async function getLinkMeta(
meta.image = httpResMeta.image meta.image = httpResMeta.image
} catch (e) { } catch (e) {
// failed // failed
console.log(e) console.error(e)
meta.error = 'Failed to fetch link' meta.error = 'Failed to fetch link'
} }

View File

@ -256,7 +256,7 @@ export function convertBskyAppUrlIfNeeded(url: string): string {
const urlp = new URL(url) const urlp = new URL(url)
return urlp.pathname return urlp.pathname
} catch (e) { } catch (e) {
console.log('Unexpected error in convertBskyAppUrlIfNeeded()', e) console.error('Unexpected error in convertBskyAppUrlIfNeeded()', e)
} }
} }
return url return url

View File

@ -593,6 +593,5 @@ function ts(item: FeedViewPost | FeedItemModel): string {
// @ts-ignore need better type checks // @ts-ignore need better type checks
return item.reason.indexedAt return item.reason.indexedAt
} }
console.log(item)
return item.post.indexedAt return item.post.indexedAt
} }

View File

@ -18,6 +18,7 @@ export const ACTOR_TYPE_SCENE = 'app.bsky.system.actorScene'
export class ProfileViewMyStateModel { export class ProfileViewMyStateModel {
follow?: string follow?: string
member?: string member?: string
muted?: boolean
constructor() { constructor() {
makeAutoObservable(this) makeAutoObservable(this)
@ -156,6 +157,18 @@ export class ProfileViewModel {
await this.refresh() await this.refresh()
} }
async muteAccount() {
await this.rootStore.api.app.bsky.graph.mute({user: this.did})
this.myState.muted = true
await this.refresh()
}
async unmuteAccount() {
await this.rootStore.api.app.bsky.graph.unmute({user: this.did})
this.myState.muted = false
await this.refresh()
}
// state transitions // state transitions
// = // =

View File

@ -282,7 +282,12 @@ export const PostThreadItem = observer(function PostThreadItem({
onCopyPostText={onCopyPostText} onCopyPostText={onCopyPostText}
onDeletePost={onDeletePost} onDeletePost={onDeletePost}
/> />
{record.text ? ( {item.post.author.viewer?.muted ? (
<View style={[styles.mutedWarning, pal.btn]}>
<FontAwesomeIcon icon={['far', 'eye-slash']} style={s.mr2} />
<Text type="body2">This post is by a muted account.</Text>
</View>
) : record.text ? (
<View style={styles.postTextContainer}> <View style={styles.postTextContainer}>
<RichText <RichText
text={record.text} text={record.text}
@ -367,6 +372,14 @@ const styles = StyleSheet.create({
paddingRight: 5, paddingRight: 5,
maxWidth: 240, maxWidth: 240,
}, },
mutedWarning: {
flexDirection: 'row',
alignItems: 'center',
padding: 10,
marginTop: 2,
marginBottom: 6,
borderRadius: 2,
},
postTextContainer: { postTextContainer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',

View File

@ -184,7 +184,12 @@ export const Post = observer(function Post({
</Link> </Link>
</View> </View>
)} )}
{record.text ? ( {item.post.author.viewer?.muted ? (
<View style={[styles.mutedWarning, pal.btn]}>
<FontAwesomeIcon icon={['far', 'eye-slash']} style={s.mr2} />
<Text type="body2">This post is by a muted account.</Text>
</View>
) : record.text ? (
<View style={styles.postTextContainer}> <View style={styles.postTextContainer}>
<RichText text={record.text} entities={record.entities} /> <RichText text={record.text} entities={record.entities} />
</View> </View>
@ -222,6 +227,14 @@ const styles = StyleSheet.create({
layoutContent: { layoutContent: {
flex: 1, flex: 1,
}, },
mutedWarning: {
flexDirection: 'row',
alignItems: 'center',
padding: 10,
marginTop: 2,
marginBottom: 6,
borderRadius: 2,
},
postTextContainer: { postTextContainer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',

View File

@ -106,6 +106,7 @@ export const FeedItem = observer(function ({
isNoTop ? styles.outerNoTop : undefined, isNoTop ? styles.outerNoTop : undefined,
item._isThreadParent ? styles.outerNoBottom : undefined, item._isThreadParent ? styles.outerNoBottom : undefined,
] ]
return ( return (
<> <>
{isChild && !item._isThreadChild && item.replyParent ? ( {isChild && !item._isThreadChild && item.replyParent ? (
@ -200,7 +201,12 @@ export const FeedItem = observer(function ({
</Link> </Link>
</View> </View>
)} )}
{record.text ? ( {item.post.author.viewer?.muted ? (
<View style={[styles.mutedWarning, pal.btn]}>
<FontAwesomeIcon icon={['far', 'eye-slash']} style={s.mr2} />
<Text type="body2">This post is by a muted account.</Text>
</View>
) : record.text ? (
<View style={styles.postTextContainer}> <View style={styles.postTextContainer}>
<RichText <RichText
type="body1" type="body1"
@ -303,6 +309,14 @@ const styles = StyleSheet.create({
layoutContent: { layoutContent: {
flex: 1, flex: 1,
}, },
mutedWarning: {
flexDirection: 'row',
alignItems: 'center',
padding: 10,
marginTop: 2,
marginBottom: 6,
borderRadius: 2,
},
postTextContainer: { postTextContainer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',

View File

@ -89,6 +89,24 @@ export const ProfileHeader = observer(function ProfileHeader({
} }
onRefreshAll() onRefreshAll()
} }
const onPressMuteAccount = async () => {
try {
await view.muteAccount()
Toast.show('Account muted')
} catch (e: any) {
console.error(e)
Toast.show(`There was an issue! ${e.toString()}`)
}
}
const onPressUnmuteAccount = async () => {
try {
await view.unmuteAccount()
Toast.show('Account unmuted')
} catch (e: any) {
console.error(e)
Toast.show(`There was an issue! ${e.toString()}`)
}
}
const onPressReportAccount = () => { const onPressReportAccount = () => {
store.shell.openModal(new ReportAccountModal(view.did)) store.shell.openModal(new ReportAccountModal(view.did))
} }
@ -143,6 +161,10 @@ export const ProfileHeader = observer(function ProfileHeader({
let dropdownItems: DropdownItem[] | undefined let dropdownItems: DropdownItem[] | undefined
if (!isMe) { if (!isMe) {
dropdownItems = dropdownItems || [] dropdownItems = dropdownItems || []
dropdownItems.push({
label: view.myState.muted ? 'Unmute Account' : 'Mute Account',
onPress: view.myState.muted ? onPressUnmuteAccount : onPressMuteAccount,
})
dropdownItems.push({ dropdownItems.push({
label: 'Report Account', label: 'Report Account',
onPress: onPressReportAccount, onPress: onPressReportAccount,
@ -286,7 +308,7 @@ export const ProfileHeader = observer(function ProfileHeader({
/> />
) : undefined} ) : undefined}
{view.isScene && view.creator ? ( {view.isScene && view.creator ? (
<View style={styles.relationshipsLine}> <View style={styles.detailLine}>
<FontAwesomeIcon <FontAwesomeIcon
icon={['far', 'user']} icon={['far', 'user']}
style={[pal.textLight, s.mr5]} style={[pal.textLight, s.mr5]}
@ -304,7 +326,7 @@ export const ProfileHeader = observer(function ProfileHeader({
</View> </View>
) : undefined} ) : undefined}
{view.isScene && view.myState.member ? ( {view.isScene && view.myState.member ? (
<View style={styles.relationshipsLine}> <View style={styles.detailLine}>
<FontAwesomeIcon <FontAwesomeIcon
icon={['far', 'circle-check']} icon={['far', 'circle-check']}
style={[pal.textLight, s.mr5]} style={[pal.textLight, s.mr5]}
@ -314,6 +336,17 @@ export const ProfileHeader = observer(function ProfileHeader({
</Text> </Text>
</View> </View>
) : undefined} ) : undefined}
{view.myState.muted ? (
<View style={[styles.detailLine, pal.btn, s.p5]}>
<FontAwesomeIcon
icon={['far', 'eye-slash']}
style={[pal.text, s.mr5]}
/>
<Text type="body2" style={[s.mr2, pal.text]}>
Account muted.
</Text>
</View>
) : undefined}
</View> </View>
{view.isScene && view.creator === store.me.did ? ( {view.isScene && view.creator === store.me.did ? (
<View style={[styles.sceneAdminContainer, pal.border]}> <View style={[styles.sceneAdminContainer, pal.border]}>
@ -421,7 +454,7 @@ const styles = StyleSheet.create({
marginBottom: 8, marginBottom: 8,
}, },
relationshipsLine: { detailLine: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginBottom: 5, marginBottom: 5,

View File

@ -30,6 +30,7 @@ import {faCompass} from '@fortawesome/free-regular-svg-icons/faCompass'
import {faEllipsis} from '@fortawesome/free-solid-svg-icons/faEllipsis' import {faEllipsis} from '@fortawesome/free-solid-svg-icons/faEllipsis'
import {faEnvelope} from '@fortawesome/free-solid-svg-icons/faEnvelope' import {faEnvelope} from '@fortawesome/free-solid-svg-icons/faEnvelope'
import {faExclamation} from '@fortawesome/free-solid-svg-icons/faExclamation' import {faExclamation} from '@fortawesome/free-solid-svg-icons/faExclamation'
import {faEyeSlash as farEyeSlash} from '@fortawesome/free-regular-svg-icons/faEyeSlash'
import {faGear} from '@fortawesome/free-solid-svg-icons/faGear' import {faGear} from '@fortawesome/free-solid-svg-icons/faGear'
import {faGlobe} from '@fortawesome/free-solid-svg-icons/faGlobe' import {faGlobe} from '@fortawesome/free-solid-svg-icons/faGlobe'
import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart' import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart'
@ -96,6 +97,7 @@ export function setup() {
faEllipsis, faEllipsis,
faEnvelope, faEnvelope,
faExclamation, faExclamation,
farEyeSlash,
faGear, faGear,
faGlobe, faGlobe,
faHeart, faHeart,

View File

@ -19,10 +19,10 @@
jsonpointer "^5.0.0" jsonpointer "^5.0.0"
leven "^3.1.0" leven "^3.1.0"
"@atproto/api@^0.0.2": "@atproto/api@^0.0.4":
version "0.0.2" version "0.0.4"
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.0.2.tgz#b6c4f5b670a04e5e79889da792518c8fb84c95bf" resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.0.4.tgz#f2cb17f234ea1360ebe719be244cabc28724d992"
integrity sha512-0ryu3M8kXCmVnRO9eb/PJ8dtwahP28tlMt0SetFQVcjUHZXkZwJGnscJOAznVP7OU1TlyUkjeDXRkUYx9hi4Lg== integrity sha512-lSaww8M2R7pRi1p1CUoidiYRNgIcUrEbhk4SZ7dGYhp9M2BKXYr9PzwWDZmB/tmFLdRPzAslmg9QWKbc0mqeUQ==
dependencies: dependencies:
"@atproto/xrpc" "*" "@atproto/xrpc" "*"
typed-emitter "^2.1.0" typed-emitter "^2.1.0"