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:
parent
3ae5a6b631
commit
b154d3ea21
43 changed files with 1193 additions and 717 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue