@atproto/bsky 0.0.150 → 0.0.152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.d.ts +4 -0
- package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.js +76 -0
- package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.js.map +1 -0
- package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts +4 -0
- package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/getPostThreadV2.js +86 -0
- package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +4 -0
- package/dist/api/index.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -0
- package/dist/config.js.map +1 -1
- package/dist/data-plane/server/db/migrations/20250526T023712742Z-like-repost-via.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20250526T023712742Z-like-repost-via.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20250526T023712742Z-like-repost-via.js +17 -0
- package/dist/data-plane/server/db/migrations/20250526T023712742Z-like-repost-via.js.map +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/index.js +2 -1
- package/dist/data-plane/server/db/migrations/index.js.map +1 -1
- package/dist/data-plane/server/db/tables/like.d.ts +2 -0
- package/dist/data-plane/server/db/tables/like.d.ts.map +1 -1
- package/dist/data-plane/server/db/tables/repost.d.ts +2 -0
- package/dist/data-plane/server/db/tables/repost.d.ts.map +1 -1
- package/dist/data-plane/server/indexing/plugins/like.d.ts.map +1 -1
- package/dist/data-plane/server/indexing/plugins/like.js +32 -9
- package/dist/data-plane/server/indexing/plugins/like.js.map +1 -1
- package/dist/data-plane/server/indexing/plugins/repost.d.ts.map +1 -1
- package/dist/data-plane/server/indexing/plugins/repost.js +32 -9
- package/dist/data-plane/server/indexing/plugins/repost.js.map +1 -1
- package/dist/hydration/feed.d.ts.map +1 -1
- package/dist/hydration/feed.js.map +1 -1
- package/dist/lexicon/index.d.ts +6 -2
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +12 -4
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +536 -88
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +279 -44
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/like.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/feed/like.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/like.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/repost.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/feed/repost.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/repost.js.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts +2 -2
- package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.d.ts +61 -0
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.js +25 -0
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.js.map +1 -0
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts +92 -0
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js +52 -0
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js.map +1 -0
- package/dist/views/index.d.ts +36 -1
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +468 -2
- package/dist/views/index.js.map +1 -1
- package/dist/views/threads-v2.d.ts +62 -0
- package/dist/views/threads-v2.d.ts.map +1 -0
- package/dist/views/threads-v2.js +180 -0
- package/dist/views/threads-v2.js.map +1 -0
- package/package.json +4 -4
- package/src/api/app/bsky/unspecced/getPostThreadHiddenV2.ts +116 -0
- package/src/api/app/bsky/unspecced/getPostThreadV2.ts +130 -0
- package/src/api/index.ts +4 -0
- package/src/config.ts +9 -0
- package/src/data-plane/server/db/migrations/20250526T023712742Z-like-repost-via.ts +17 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/like.ts +2 -0
- package/src/data-plane/server/db/tables/repost.ts +2 -0
- package/src/data-plane/server/indexing/plugins/like.ts +39 -9
- package/src/data-plane/server/indexing/plugins/repost.ts +38 -9
- package/src/hydration/feed.ts +1 -0
- package/src/lexicon/index.ts +33 -9
- package/src/lexicon/lexicons.ts +298 -45
- package/src/lexicon/types/app/bsky/feed/defs.ts +2 -0
- package/src/lexicon/types/app/bsky/feed/like.ts +1 -0
- package/src/lexicon/types/app/bsky/feed/repost.ts +1 -0
- package/src/lexicon/types/app/bsky/notification/listNotifications.ts +3 -1
- package/src/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.ts +93 -0
- package/src/lexicon/types/app/bsky/unspecced/getPostThreadV2.ts +160 -0
- package/src/views/index.ts +747 -2
- package/src/views/threads-v2.ts +351 -0
- package/tests/__snapshots__/feed-generation.test.ts.snap +2 -0
- package/tests/seed/thread-v2.ts +775 -0
- package/tests/seed/util.ts +52 -0
- package/tests/views/__snapshots__/author-feed.test.ts.snap +30 -24
- package/tests/views/__snapshots__/notifications.test.ts.snap +1164 -229
- package/tests/views/__snapshots__/thread-v2.test.ts.snap +1091 -0
- package/tests/views/__snapshots__/timeline.test.ts.snap +398 -376
- package/tests/views/notifications.test.ts +177 -0
- package/tests/views/thread-v2.test.ts +2009 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/dist/views/index.js
CHANGED
|
@@ -11,6 +11,7 @@ const threadgate_1 = require("../lexicon/types/app/bsky/feed/threadgate");
|
|
|
11
11
|
const service_1 = require("../lexicon/types/app/bsky/labeler/service");
|
|
12
12
|
const defs_2 = require("../lexicon/types/com/atproto/label/defs");
|
|
13
13
|
const uris_1 = require("../util/uris");
|
|
14
|
+
const threads_v2_1 = require("./threads-v2");
|
|
14
15
|
const types_1 = require("./types");
|
|
15
16
|
const util_1 = require("./util");
|
|
16
17
|
const notificationDeletedRecord = {
|
|
@@ -67,6 +68,10 @@ class Views {
|
|
|
67
68
|
return true;
|
|
68
69
|
return false;
|
|
69
70
|
}
|
|
71
|
+
noUnauthenticatedPost(state, post) {
|
|
72
|
+
const isNoUnauthenticated = post.author.labels?.some((l) => l.val === '!no-unauthenticated');
|
|
73
|
+
return !state.ctx?.viewer && !!isNoUnauthenticated;
|
|
74
|
+
}
|
|
70
75
|
viewerBlockExists(did, state) {
|
|
71
76
|
const viewer = state.profileViewers?.get(did);
|
|
72
77
|
if (!viewer)
|
|
@@ -649,7 +654,7 @@ class Views {
|
|
|
649
654
|
return;
|
|
650
655
|
if (repost.record.subject.uri !== item.post.uri)
|
|
651
656
|
return;
|
|
652
|
-
reason = this.reasonRepost(
|
|
657
|
+
reason = this.reasonRepost(item.repost.uri, repost, state);
|
|
653
658
|
if (!reason)
|
|
654
659
|
return;
|
|
655
660
|
}
|
|
@@ -728,13 +733,16 @@ class Views {
|
|
|
728
733
|
notFound: true,
|
|
729
734
|
};
|
|
730
735
|
}
|
|
731
|
-
reasonRepost(
|
|
736
|
+
reasonRepost(uri, repost, state) {
|
|
737
|
+
const creatorDid = (0, uris_1.uriToDid)(uri);
|
|
732
738
|
const creator = this.profileBasic(creatorDid, state);
|
|
733
739
|
if (!creator)
|
|
734
740
|
return;
|
|
735
741
|
return {
|
|
736
742
|
$type: 'app.bsky.feed.defs#reasonRepost',
|
|
737
743
|
by: creator,
|
|
744
|
+
uri,
|
|
745
|
+
cid: repost.cid,
|
|
738
746
|
indexedAt: this.indexedAt(repost).toISOString(),
|
|
739
747
|
};
|
|
740
748
|
}
|
|
@@ -848,6 +856,464 @@ class Views {
|
|
|
848
856
|
};
|
|
849
857
|
});
|
|
850
858
|
}
|
|
859
|
+
// Threads V2
|
|
860
|
+
// ------------
|
|
861
|
+
threadV2(skeleton, state, { above, below, branchingFactor, prioritizeFollowedUsers, sort, }) {
|
|
862
|
+
const { anchor: anchorUri, uris } = skeleton;
|
|
863
|
+
// Not found.
|
|
864
|
+
const postView = this.post(anchorUri, state);
|
|
865
|
+
const post = state.posts?.get(anchorUri);
|
|
866
|
+
if (!post || !postView) {
|
|
867
|
+
return {
|
|
868
|
+
hasHiddenReplies: false,
|
|
869
|
+
thread: [
|
|
870
|
+
this.threadV2ItemNotFound({
|
|
871
|
+
uri: anchorUri,
|
|
872
|
+
depth: 0,
|
|
873
|
+
}),
|
|
874
|
+
],
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
// Blocked (only 1p for anchor).
|
|
878
|
+
if (this.viewerBlockExists(postView.author.did, state)) {
|
|
879
|
+
return {
|
|
880
|
+
hasHiddenReplies: false,
|
|
881
|
+
thread: [
|
|
882
|
+
this.threadV2ItemBlocked({
|
|
883
|
+
uri: anchorUri,
|
|
884
|
+
depth: 0,
|
|
885
|
+
authorDid: postView.author.did,
|
|
886
|
+
state,
|
|
887
|
+
}),
|
|
888
|
+
],
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
const childrenByParentUri = this.groupThreadChildrenByParent(anchorUri, uris, state);
|
|
892
|
+
const rootUri = getRootUri(anchorUri, post);
|
|
893
|
+
const opDid = (0, uris_1.uriToDid)(rootUri);
|
|
894
|
+
const authorDid = postView.author.did;
|
|
895
|
+
const isOPPost = authorDid === opDid;
|
|
896
|
+
const anchorViolatesThreadGate = post.violatesThreadGate;
|
|
897
|
+
// Builds the parent tree, and whether it is a contiguous OP thread.
|
|
898
|
+
const parentTree = !anchorViolatesThreadGate
|
|
899
|
+
? this.threadV2Parent({
|
|
900
|
+
childUri: anchorUri,
|
|
901
|
+
opDid,
|
|
902
|
+
rootUri,
|
|
903
|
+
above,
|
|
904
|
+
depth: -1,
|
|
905
|
+
}, state)
|
|
906
|
+
: undefined;
|
|
907
|
+
const { tree: parent, isOPThread: isOPThreadFromRootToParent } = parentTree ?? { tree: undefined, isOPThread: false };
|
|
908
|
+
const isOPThread = parent
|
|
909
|
+
? isOPThreadFromRootToParent && isOPPost
|
|
910
|
+
: isOPPost;
|
|
911
|
+
const anchorDepth = 0; // The depth of the anchor post is always 0.
|
|
912
|
+
let anchorTree;
|
|
913
|
+
let hasHiddenReplies = false;
|
|
914
|
+
if (this.noUnauthenticatedPost(state, postView)) {
|
|
915
|
+
anchorTree = {
|
|
916
|
+
type: 'noUnauthenticated',
|
|
917
|
+
item: this.threadV2ItemNoUnauthenticated({
|
|
918
|
+
uri: anchorUri,
|
|
919
|
+
depth: anchorDepth,
|
|
920
|
+
}),
|
|
921
|
+
parent,
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
else {
|
|
925
|
+
const { replies, hasHiddenReplies: hasHiddenRepliesShadow } = !anchorViolatesThreadGate
|
|
926
|
+
? this.threadV2Replies({
|
|
927
|
+
parentUri: anchorUri,
|
|
928
|
+
isOPThread,
|
|
929
|
+
opDid,
|
|
930
|
+
rootUri,
|
|
931
|
+
childrenByParentUri,
|
|
932
|
+
below,
|
|
933
|
+
depth: 1,
|
|
934
|
+
branchingFactor,
|
|
935
|
+
}, state)
|
|
936
|
+
: { replies: undefined, hasHiddenReplies: false };
|
|
937
|
+
hasHiddenReplies = hasHiddenRepliesShadow;
|
|
938
|
+
anchorTree = {
|
|
939
|
+
type: 'post',
|
|
940
|
+
item: this.threadV2ItemPost({
|
|
941
|
+
depth: anchorDepth,
|
|
942
|
+
isOPThread,
|
|
943
|
+
postView,
|
|
944
|
+
repliesAllowance: Infinity, // While we don't have pagination.
|
|
945
|
+
uri: anchorUri,
|
|
946
|
+
}),
|
|
947
|
+
hasOPLike: !!state.threadContexts?.get(postView.uri)?.like,
|
|
948
|
+
parent,
|
|
949
|
+
replies,
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
const thread = (0, threads_v2_1.sortTrimFlattenThreadTree)(anchorTree, {
|
|
953
|
+
opDid,
|
|
954
|
+
branchingFactor,
|
|
955
|
+
sort,
|
|
956
|
+
prioritizeFollowedUsers,
|
|
957
|
+
viewer: state.ctx?.viewer ?? null,
|
|
958
|
+
fetchedAt: Date.now(),
|
|
959
|
+
});
|
|
960
|
+
return {
|
|
961
|
+
hasHiddenReplies,
|
|
962
|
+
thread,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
threadV2Parent({ childUri, opDid, rootUri, above, depth, }, state) {
|
|
966
|
+
// Reached the `above` limit.
|
|
967
|
+
if (Math.abs(depth) > above) {
|
|
968
|
+
return undefined;
|
|
969
|
+
}
|
|
970
|
+
// Not found.
|
|
971
|
+
const uri = state.posts?.get(childUri)?.record.reply?.parent.uri;
|
|
972
|
+
if (!uri) {
|
|
973
|
+
return undefined;
|
|
974
|
+
}
|
|
975
|
+
const postView = this.post(uri, state);
|
|
976
|
+
const post = state.posts?.get(uri);
|
|
977
|
+
if (!post || !postView) {
|
|
978
|
+
return {
|
|
979
|
+
tree: {
|
|
980
|
+
type: 'notFound',
|
|
981
|
+
item: this.threadV2ItemNotFound({ uri, depth }),
|
|
982
|
+
},
|
|
983
|
+
isOPThread: false,
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
if (rootUri !== getRootUri(uri, post)) {
|
|
987
|
+
// Outside thread boundary.
|
|
988
|
+
return undefined;
|
|
989
|
+
}
|
|
990
|
+
// Blocked (1p and 3p for parent).
|
|
991
|
+
const authorDid = postView.author.did;
|
|
992
|
+
const has1pBlock = this.viewerBlockExists(authorDid, state);
|
|
993
|
+
const has3pBlock = !state.ctx?.include3pBlocks && state.postBlocks?.get(childUri)?.parent;
|
|
994
|
+
if (has1pBlock || has3pBlock) {
|
|
995
|
+
return {
|
|
996
|
+
tree: {
|
|
997
|
+
type: 'blocked',
|
|
998
|
+
item: this.threadV2ItemBlocked({
|
|
999
|
+
uri,
|
|
1000
|
+
depth,
|
|
1001
|
+
authorDid,
|
|
1002
|
+
state,
|
|
1003
|
+
}),
|
|
1004
|
+
},
|
|
1005
|
+
isOPThread: false,
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
// Recurse up.
|
|
1009
|
+
const parentTree = this.threadV2Parent({
|
|
1010
|
+
childUri: uri,
|
|
1011
|
+
opDid,
|
|
1012
|
+
rootUri,
|
|
1013
|
+
above,
|
|
1014
|
+
depth: depth - 1,
|
|
1015
|
+
}, state);
|
|
1016
|
+
const { tree: parent, isOPThread: isOPThreadFromRootToParent } = parentTree ?? { tree: undefined, isOPThread: false };
|
|
1017
|
+
const isOPPost = authorDid === opDid;
|
|
1018
|
+
const isOPThread = parent
|
|
1019
|
+
? isOPThreadFromRootToParent && isOPPost
|
|
1020
|
+
: isOPPost;
|
|
1021
|
+
if (this.noUnauthenticatedPost(state, postView)) {
|
|
1022
|
+
return {
|
|
1023
|
+
tree: {
|
|
1024
|
+
type: 'noUnauthenticated',
|
|
1025
|
+
item: this.threadV2ItemNoUnauthenticated({
|
|
1026
|
+
uri,
|
|
1027
|
+
depth,
|
|
1028
|
+
}),
|
|
1029
|
+
parent,
|
|
1030
|
+
},
|
|
1031
|
+
isOPThread,
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
const parentUri = post.record.reply?.parent.uri;
|
|
1035
|
+
const hasMoreParents = !!parentUri && !parent;
|
|
1036
|
+
return {
|
|
1037
|
+
tree: {
|
|
1038
|
+
type: 'post',
|
|
1039
|
+
item: this.threadV2ItemPost({
|
|
1040
|
+
depth,
|
|
1041
|
+
isOPThread,
|
|
1042
|
+
moreParents: hasMoreParents,
|
|
1043
|
+
postView,
|
|
1044
|
+
uri,
|
|
1045
|
+
}),
|
|
1046
|
+
hasOPLike: !!state.threadContexts?.get(postView.uri)?.like,
|
|
1047
|
+
parent,
|
|
1048
|
+
replies: undefined,
|
|
1049
|
+
},
|
|
1050
|
+
isOPThread,
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
threadV2Replies({ parentUri, isOPThread: isOPThreadFromRootToParent, opDid, rootUri, childrenByParentUri, below, depth, branchingFactor, }, state) {
|
|
1054
|
+
// Reached the `below` limit.
|
|
1055
|
+
if (depth > below) {
|
|
1056
|
+
return { replies: undefined, hasHiddenReplies: false };
|
|
1057
|
+
}
|
|
1058
|
+
const childrenUris = childrenByParentUri[parentUri] ?? [];
|
|
1059
|
+
let hasHiddenReplies = false;
|
|
1060
|
+
const replies = (0, common_1.mapDefined)(childrenUris, (uri) => {
|
|
1061
|
+
const replyInclusion = this.checkThreadV2ReplyInclusion(uri, rootUri, state);
|
|
1062
|
+
if (!replyInclusion) {
|
|
1063
|
+
return undefined;
|
|
1064
|
+
}
|
|
1065
|
+
const { authorDid, postView } = replyInclusion;
|
|
1066
|
+
// Hidden.
|
|
1067
|
+
const { hiddenByThreadgate, mutedByViewer } = this.isHiddenThreadPost({ rootUri, uri }, state);
|
|
1068
|
+
// Is hidden reply.
|
|
1069
|
+
if (hiddenByThreadgate || mutedByViewer) {
|
|
1070
|
+
// Only care about anchor replies
|
|
1071
|
+
if (depth === 1) {
|
|
1072
|
+
hasHiddenReplies = true;
|
|
1073
|
+
}
|
|
1074
|
+
return undefined;
|
|
1075
|
+
}
|
|
1076
|
+
// Recurse down.
|
|
1077
|
+
const isOPThread = isOPThreadFromRootToParent && authorDid === opDid;
|
|
1078
|
+
const { replies: nestedReplies } = this.threadV2Replies({
|
|
1079
|
+
parentUri: uri,
|
|
1080
|
+
isOPThread,
|
|
1081
|
+
opDid,
|
|
1082
|
+
rootUri,
|
|
1083
|
+
childrenByParentUri,
|
|
1084
|
+
below,
|
|
1085
|
+
depth: depth + 1,
|
|
1086
|
+
branchingFactor,
|
|
1087
|
+
}, state);
|
|
1088
|
+
const reachedDepth = depth === below;
|
|
1089
|
+
const repliesAllowance = reachedDepth ? 0 : branchingFactor;
|
|
1090
|
+
const tree = {
|
|
1091
|
+
type: 'post',
|
|
1092
|
+
item: this.threadV2ItemPost({
|
|
1093
|
+
depth,
|
|
1094
|
+
isOPThread,
|
|
1095
|
+
postView,
|
|
1096
|
+
repliesAllowance,
|
|
1097
|
+
uri,
|
|
1098
|
+
}),
|
|
1099
|
+
hasOPLike: !!state.threadContexts?.get(postView.uri)?.like,
|
|
1100
|
+
parent: undefined,
|
|
1101
|
+
replies: nestedReplies,
|
|
1102
|
+
};
|
|
1103
|
+
return tree;
|
|
1104
|
+
});
|
|
1105
|
+
return {
|
|
1106
|
+
replies,
|
|
1107
|
+
hasHiddenReplies,
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
threadV2ItemPost({ depth, isOPThread, moreParents, postView, repliesAllowance, uri, }) {
|
|
1111
|
+
const moreReplies = repliesAllowance === undefined
|
|
1112
|
+
? 0
|
|
1113
|
+
: Math.max((postView.replyCount ?? 0) - repliesAllowance, 0);
|
|
1114
|
+
return {
|
|
1115
|
+
uri,
|
|
1116
|
+
depth,
|
|
1117
|
+
value: {
|
|
1118
|
+
$type: 'app.bsky.unspecced.getPostThreadV2#threadItemPost',
|
|
1119
|
+
post: postView,
|
|
1120
|
+
moreParents: moreParents ?? false,
|
|
1121
|
+
moreReplies,
|
|
1122
|
+
opThread: isOPThread,
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
threadV2ItemNoUnauthenticated({ uri, depth, }) {
|
|
1127
|
+
return {
|
|
1128
|
+
uri,
|
|
1129
|
+
depth,
|
|
1130
|
+
value: {
|
|
1131
|
+
$type: 'app.bsky.unspecced.getPostThreadV2#threadItemNoUnauthenticated',
|
|
1132
|
+
},
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
threadV2ItemNotFound({ uri, depth, }) {
|
|
1136
|
+
return {
|
|
1137
|
+
uri,
|
|
1138
|
+
depth,
|
|
1139
|
+
value: {
|
|
1140
|
+
$type: 'app.bsky.unspecced.getPostThreadV2#threadItemNotFound',
|
|
1141
|
+
},
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
threadV2ItemBlocked({ uri, depth, authorDid, state, }) {
|
|
1145
|
+
return {
|
|
1146
|
+
uri,
|
|
1147
|
+
depth,
|
|
1148
|
+
value: {
|
|
1149
|
+
$type: 'app.bsky.unspecced.getPostThreadV2#threadItemBlocked',
|
|
1150
|
+
author: {
|
|
1151
|
+
did: authorDid,
|
|
1152
|
+
viewer: this.blockedProfileViewer(authorDid, state),
|
|
1153
|
+
},
|
|
1154
|
+
},
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
threadHiddenV2(skeleton, state, { below, branchingFactor, }) {
|
|
1158
|
+
const { anchor: anchorUri, uris } = skeleton;
|
|
1159
|
+
// Not found.
|
|
1160
|
+
const postView = this.post(anchorUri, state);
|
|
1161
|
+
const post = state.posts?.get(anchorUri);
|
|
1162
|
+
if (!post || !postView) {
|
|
1163
|
+
return [];
|
|
1164
|
+
}
|
|
1165
|
+
// Blocked (only 1p for anchor).
|
|
1166
|
+
if (this.viewerBlockExists(postView.author.did, state)) {
|
|
1167
|
+
return [];
|
|
1168
|
+
}
|
|
1169
|
+
const childrenByParentUri = this.groupThreadChildrenByParent(anchorUri, uris, state);
|
|
1170
|
+
const rootUri = getRootUri(anchorUri, post);
|
|
1171
|
+
const opDid = (0, uris_1.uriToDid)(rootUri);
|
|
1172
|
+
const anchorTree = {
|
|
1173
|
+
type: 'hiddenAnchor',
|
|
1174
|
+
item: this.threadHiddenV2ItemPostAnchor({ depth: 0, uri: anchorUri }),
|
|
1175
|
+
replies: this.threadHiddenV2Replies({
|
|
1176
|
+
parentUri: anchorUri,
|
|
1177
|
+
rootUri,
|
|
1178
|
+
childrenByParentUri,
|
|
1179
|
+
below,
|
|
1180
|
+
depth: 1,
|
|
1181
|
+
}, state),
|
|
1182
|
+
};
|
|
1183
|
+
return (0, threads_v2_1.sortTrimFlattenThreadTree)(anchorTree, {
|
|
1184
|
+
opDid,
|
|
1185
|
+
branchingFactor,
|
|
1186
|
+
prioritizeFollowedUsers: false,
|
|
1187
|
+
viewer: state.ctx?.viewer ?? null,
|
|
1188
|
+
fetchedAt: Date.now(),
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
threadHiddenV2Replies({ parentUri, rootUri, childrenByParentUri, below, depth, }, state) {
|
|
1192
|
+
// Reached the `below` limit.
|
|
1193
|
+
if (depth > below) {
|
|
1194
|
+
return undefined;
|
|
1195
|
+
}
|
|
1196
|
+
const childrenUris = childrenByParentUri[parentUri] ?? [];
|
|
1197
|
+
return (0, common_1.mapDefined)(childrenUris, (uri) => {
|
|
1198
|
+
const replyInclusion = this.checkThreadV2ReplyInclusion(uri, rootUri, state);
|
|
1199
|
+
if (!replyInclusion) {
|
|
1200
|
+
return undefined;
|
|
1201
|
+
}
|
|
1202
|
+
const { postView } = replyInclusion;
|
|
1203
|
+
// Hidden.
|
|
1204
|
+
const { hiddenByThreadgate, mutedByViewer } = this.isHiddenThreadPost({ rootUri, uri }, state);
|
|
1205
|
+
// Is hidden reply.
|
|
1206
|
+
if (hiddenByThreadgate || mutedByViewer) {
|
|
1207
|
+
// Only show hidden anchor replies, not all hidden.
|
|
1208
|
+
if (depth > 1) {
|
|
1209
|
+
return undefined;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
else if (depth === 1) {
|
|
1213
|
+
// Don't include non-hidden anchor replies.
|
|
1214
|
+
return undefined;
|
|
1215
|
+
}
|
|
1216
|
+
// Recurse down.
|
|
1217
|
+
const replies = this.threadHiddenV2Replies({
|
|
1218
|
+
parentUri: uri,
|
|
1219
|
+
rootUri,
|
|
1220
|
+
childrenByParentUri,
|
|
1221
|
+
below,
|
|
1222
|
+
depth: depth + 1,
|
|
1223
|
+
}, state);
|
|
1224
|
+
const item = this.threadHiddenV2ItemPost({
|
|
1225
|
+
depth,
|
|
1226
|
+
postView,
|
|
1227
|
+
rootUri,
|
|
1228
|
+
uri,
|
|
1229
|
+
}, state);
|
|
1230
|
+
const tree = {
|
|
1231
|
+
type: 'hiddenPost',
|
|
1232
|
+
item: item,
|
|
1233
|
+
replies,
|
|
1234
|
+
};
|
|
1235
|
+
return tree;
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
threadHiddenV2ItemPostAnchor({ depth, uri, }) {
|
|
1239
|
+
return {
|
|
1240
|
+
uri,
|
|
1241
|
+
depth,
|
|
1242
|
+
// In hidden replies, the anchor value is undefined, so it doesn't include the anchor in the result.
|
|
1243
|
+
// This is helpful so we can use the same internal structure for hidden and non-hidden, while omitting anchor for hidden.
|
|
1244
|
+
value: undefined,
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
threadHiddenV2ItemPost({ depth, postView, rootUri, uri, }, state) {
|
|
1248
|
+
const { hiddenByThreadgate, mutedByViewer } = this.isHiddenThreadPost({ rootUri, uri }, state);
|
|
1249
|
+
const base = this.threadHiddenV2ItemPostAnchor({ depth, uri });
|
|
1250
|
+
return {
|
|
1251
|
+
...base,
|
|
1252
|
+
value: {
|
|
1253
|
+
$type: 'app.bsky.unspecced.getPostThreadHiddenV2#threadHiddenItemPost',
|
|
1254
|
+
post: postView,
|
|
1255
|
+
hiddenByThreadgate,
|
|
1256
|
+
mutedByViewer,
|
|
1257
|
+
},
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
checkThreadV2ReplyInclusion(uri, rootUri, state) {
|
|
1261
|
+
// Not found.
|
|
1262
|
+
const post = state.posts?.get(uri);
|
|
1263
|
+
if (post?.violatesThreadGate) {
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
const postView = this.post(uri, state);
|
|
1267
|
+
if (!post || !postView) {
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
const authorDid = postView.author.did;
|
|
1271
|
+
if (rootUri !== getRootUri(uri, post)) {
|
|
1272
|
+
// outside thread boundary
|
|
1273
|
+
return null;
|
|
1274
|
+
}
|
|
1275
|
+
// Blocked (1p and 3p for replies).
|
|
1276
|
+
const has1pBlock = this.viewerBlockExists(authorDid, state);
|
|
1277
|
+
const has3pBlock = !state.ctx?.include3pBlocks && state.postBlocks?.get(uri)?.parent;
|
|
1278
|
+
if (has1pBlock || has3pBlock) {
|
|
1279
|
+
return null;
|
|
1280
|
+
}
|
|
1281
|
+
if (!this.viewerSeesNeedsReview({ uri, did: authorDid }, state)) {
|
|
1282
|
+
return null;
|
|
1283
|
+
}
|
|
1284
|
+
// No unauthenticated.
|
|
1285
|
+
if (this.noUnauthenticatedPost(state, postView)) {
|
|
1286
|
+
return null;
|
|
1287
|
+
}
|
|
1288
|
+
return { authorDid, postView };
|
|
1289
|
+
}
|
|
1290
|
+
isHiddenThreadPost({ rootUri, uri, }, state) {
|
|
1291
|
+
const authorDid = (0, uris_1.uriToDid)(uri);
|
|
1292
|
+
const hiddenByThreadgate = state.ctx?.viewer !== authorDid &&
|
|
1293
|
+
this.replyIsHiddenByThreadgate(uri, rootUri, state);
|
|
1294
|
+
const mutedByViewer = this.viewerMuteExists(authorDid, state);
|
|
1295
|
+
return {
|
|
1296
|
+
hiddenByThreadgate,
|
|
1297
|
+
mutedByViewer,
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
groupThreadChildrenByParent(anchorUri, uris, state) {
|
|
1301
|
+
// Groups children of each parent.
|
|
1302
|
+
const includedPosts = new Set([anchorUri]);
|
|
1303
|
+
const childrenByParentUri = {};
|
|
1304
|
+
uris.forEach((uri) => {
|
|
1305
|
+
const post = state.posts?.get(uri);
|
|
1306
|
+
const parentUri = post?.record.reply?.parent.uri;
|
|
1307
|
+
if (!parentUri)
|
|
1308
|
+
return;
|
|
1309
|
+
if (includedPosts.has(uri))
|
|
1310
|
+
return;
|
|
1311
|
+
includedPosts.add(uri);
|
|
1312
|
+
childrenByParentUri[parentUri] ?? (childrenByParentUri[parentUri] = []);
|
|
1313
|
+
childrenByParentUri[parentUri].push(uri);
|
|
1314
|
+
});
|
|
1315
|
+
return childrenByParentUri;
|
|
1316
|
+
}
|
|
851
1317
|
// Embeds
|
|
852
1318
|
// ------------
|
|
853
1319
|
embed(postUri, embed, state, depth) {
|