Labeling & moderation updates [DRAFT] (#1057)

* First pass moving to the new labeling sdk (it compiles)

* Correct behaviors around interpreting label moderation

* Improve moderation state rendering

* Improve hiders and alerts

* Improve handling of mutes

* Improve profile warnings

* Add profile blurring to profile header

* Add blocks to test cases

* Render labels on profile cards, do not filter

* Filter profiles from suggestions using moderation

* Apply profile blurring to ProfileCard

* Handle blocked and deleted quote posts

* Temporarily translate content filtering settings to new labels

* Fix types

* Tune ContentHider & PostHider click targets

* Put a warning on profilecard label pills

* Fix screenhider learnmore link on mobile

* Enforce no-override on user avatar

* Dont enumerate profile blur-media labels in alerts

* Fixes to muted posts (esp quotes of muted users)

* Fixes to account/profile warnings

* Bump @atproto/api@0.5.0

* Bump @atproto/api@0.5.1

* Fix tests

* 1.43

* Remove log

* Bump @atproto/api@0.5.2
This commit is contained in:
Paul Frazee 2023-08-03 22:08:30 -07:00 committed by GitHub
parent 3ae5a6b631
commit b154d3ea21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 1193 additions and 717 deletions

View file

@ -3,9 +3,9 @@ import {
AppBskyFeedPost as FeedPost,
AppBskyFeedDefs,
RichText,
PostModeration,
} from '@atproto/api'
import {RootStoreModel} from '../root-store'
import {PostLabelInfo, PostModeration} from 'lib/labeling/types'
import {PostsFeedItemModel} from '../feeds/post'
type PostView = AppBskyFeedDefs.PostView
@ -67,10 +67,6 @@ export class PostThreadItemModel {
return this.data.isThreadMuted
}
get labelInfo(): PostLabelInfo {
return this.data.labelInfo
}
get moderation(): PostModeration {
return this.data.moderation
}

View file

@ -2,6 +2,7 @@ import {makeAutoObservable, runInAction} from 'mobx'
import {
AppBskyFeedGetPostThread as GetPostThread,
AppBskyFeedDefs,
PostModeration,
} from '@atproto/api'
import {AtUri} from '@atproto/api'
import {RootStoreModel} from '../root-store'
@ -231,7 +232,6 @@ export class PostThreadModel {
return
}
pruneReplies(res.data.thread)
sortThread(res.data.thread)
const thread = new PostThreadItemModel(
this.rootStore,
res.data.thread as AppBskyFeedDefs.ThreadViewPost,
@ -241,6 +241,7 @@ export class PostThreadModel {
res.data.thread as AppBskyFeedDefs.ThreadViewPost,
thread.uri,
)
sortThread(thread)
this.thread = thread
}
}
@ -262,24 +263,28 @@ function pruneReplies(post: MaybePost) {
}
}
function sortThread(post: MaybePost) {
if (post.notFound) {
type MaybeThreadItem =
| PostThreadItemModel
| AppBskyFeedDefs.NotFoundPost
| AppBskyFeedDefs.BlockedPost
function sortThread(item: MaybeThreadItem) {
if ('notFound' in item) {
return
}
post = post as AppBskyFeedDefs.ThreadViewPost
if (post.replies) {
post.replies.sort((a: MaybePost, b: MaybePost) => {
post = post as AppBskyFeedDefs.ThreadViewPost
if (a.notFound) {
item = item as PostThreadItemModel
if (item.replies) {
item.replies.sort((a: MaybeThreadItem, b: MaybeThreadItem) => {
if ('notFound' in a && a.notFound) {
return 1
}
if (b.notFound) {
if ('notFound' in b && b.notFound) {
return -1
}
a = a as AppBskyFeedDefs.ThreadViewPost
b = b as AppBskyFeedDefs.ThreadViewPost
const aIsByOp = a.post.author.did === post.post.author.did
const bIsByOp = b.post.author.did === post.post.author.did
item = item as PostThreadItemModel
a = a as PostThreadItemModel
b = b as PostThreadItemModel
const aIsByOp = a.post.author.did === item.post.author.did
const bIsByOp = b.post.author.did === item.post.author.did
if (aIsByOp && bIsByOp) {
return a.post.indexedAt.localeCompare(b.post.indexedAt) // oldest
} else if (aIsByOp) {
@ -287,8 +292,31 @@ function sortThread(post: MaybePost) {
} else if (bIsByOp) {
return 1 // op's own reply
}
// put moderated content down at the bottom
if (modScore(a.moderation) !== modScore(b.moderation)) {
return modScore(a.moderation) - modScore(b.moderation)
}
return b.post.indexedAt.localeCompare(a.post.indexedAt) // newest
})
post.replies.forEach(reply => sortThread(reply))
item.replies.forEach(reply => sortThread(reply))
}
}
function modScore(mod: PostModeration): number {
if (mod.content.blur && mod.content.noOverride) {
return 5
}
if (mod.content.blur) {
return 4
}
if (mod.content.alert) {
return 3
}
if (mod.embed.blur && mod.embed.noOverride) {
return 2
}
if (mod.embed.blur) {
return 1
}
return 0
}

View file

@ -6,18 +6,14 @@ import {
AppBskyActorGetProfile as GetProfile,
AppBskyActorProfile,
RichText,
moderateProfile,
ProfileModeration,
} from '@atproto/api'
import {RootStoreModel} from '../root-store'
import * as apilib from 'lib/api/index'
import {cleanError} from 'lib/strings/errors'
import {FollowState} from '../cache/my-follows'
import {Image as RNImage} from 'react-native-image-crop-picker'
import {ProfileLabelInfo, ProfileModeration} from 'lib/labeling/types'
import {
getProfileModeration,
filterAccountLabels,
filterProfileLabels,
} from 'lib/labeling/helpers'
import {track} from 'lib/analytics/analytics'
export class ProfileViewerModel {
@ -26,7 +22,8 @@ export class ProfileViewerModel {
following?: string
followedBy?: string
blockedBy?: boolean
blocking?: string
blocking?: string;
[key: string]: unknown
constructor() {
makeAutoObservable(this)
@ -53,7 +50,8 @@ export class ProfileModel {
followsCount: number = 0
postsCount: number = 0
labels?: ComAtprotoLabelDefs.Label[] = undefined
viewer = new ProfileViewerModel()
viewer = new ProfileViewerModel();
[key: string]: unknown
// added data
descriptionRichText?: RichText = new RichText({text: ''})
@ -85,18 +83,8 @@ export class ProfileModel {
return this.hasLoaded && !this.hasContent
}
get labelInfo(): ProfileLabelInfo {
return {
accountLabels: filterAccountLabels(this.labels),
profileLabels: filterProfileLabels(this.labels),
isMuted: this.viewer?.muted || false,
isBlocking: !!this.viewer?.blocking || false,
isBlockedBy: !!this.viewer?.blockedBy || false,
}
}
get moderation(): ProfileModeration {
return getProfileModeration(this.rootStore, this.labelInfo)
return moderateProfile(this, this.rootStore.preferences.moderationOpts)
}
// public api