Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cope-settings-sync 156 lines 4.3 kB view raw
1import {type $Typed, ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api' 2 3import {EMOJI_REACTION_LIMIT} from '#/lib/constants' 4import {logger} from '#/logger' 5import * as bsky from '#/types/bsky' 6 7export function canBeMessaged(profile: bsky.profile.AnyProfileView) { 8 switch (profile.associated?.chat?.allowIncoming) { 9 case 'none': 10 return false 11 case 'all': 12 return true 13 // if unset, treat as following 14 case 'following': 15 case undefined: 16 return Boolean(profile.viewer?.followedBy) 17 // any other values are invalid according to the lexicon, so 18 // let's treat as false to be safe 19 default: 20 return false 21 } 22} 23 24export function localDateString(date: Date) { 25 // can't use toISOString because it should be in local time 26 const mm = date.getMonth() 27 const dd = date.getDate() 28 const yyyy = date.getFullYear() 29 // not padding with 0s because it's not necessary, it's just used for comparison 30 return `${yyyy}-${mm}-${dd}` 31} 32 33export function hasAlreadyReacted( 34 message: ChatBskyConvoDefs.MessageView, 35 myDid: string | undefined, 36 emoji: string, 37): boolean { 38 if (!message.reactions) { 39 return false 40 } 41 return !!message.reactions.find( 42 reaction => reaction.value === emoji && reaction.sender.did === myDid, 43 ) 44} 45 46export function hasReachedReactionLimit( 47 message: ChatBskyConvoDefs.MessageView, 48 myDid: string | undefined, 49): boolean { 50 if (!message.reactions) { 51 return false 52 } 53 const myReactions = message.reactions.filter( 54 reaction => reaction.sender.did === myDid, 55 ) 56 return myReactions.length >= EMOJI_REACTION_LIMIT 57} 58 59export type GroupConvoMember = ChatBskyActorDefs.ProfileViewBasic & { 60 // can be missing if account deleted 61 kind?: $Typed<ChatBskyActorDefs.GroupConvoMember> 62} 63 64export type DirectConvoMember = ChatBskyActorDefs.ProfileViewBasic & { 65 kind: $Typed<ChatBskyActorDefs.DirectConvoMember> 66} 67 68export type ConvoWithDetails = {view: ChatBskyConvoDefs.ConvoView} & ( 69 | { 70 kind: 'group' 71 details: ChatBskyConvoDefs.GroupConvo 72 primaryMember: GroupConvoMember // the owner 73 members: Array<GroupConvoMember> 74 } 75 | { 76 kind: 'direct' 77 details: ChatBskyConvoDefs.DirectConvo 78 primaryMember: DirectConvoMember // the other user 79 members: Array<DirectConvoMember> 80 } 81) 82 83/** 84 * Converts a raw convoView into something easier to use (i.e. extracts chat owner) 85 * and enforces the correct type for convo members. 86 */ 87export function parseConvoView( 88 convoView: ChatBskyConvoDefs.ConvoView, 89 ownDid: string | undefined, 90): ConvoWithDetails | null { 91 if ( 92 bsky.dangerousIsType<ChatBskyConvoDefs.GroupConvo>( 93 convoView.kind, 94 ChatBskyConvoDefs.isGroupConvo, 95 ) 96 ) { 97 let owner: GroupConvoMember | undefined = undefined 98 99 for (const member of convoView.members) { 100 if ( 101 bsky.dangerousIsType<ChatBskyActorDefs.GroupConvoMember>( 102 member.kind, 103 ChatBskyActorDefs.isGroupConvoMember, 104 ) 105 ) { 106 if (member.kind.role === 'owner') { 107 // have to do a type assertion here 108 // this works: {...member, kind: member.kind} 109 // however that's creating a new object for no good reason 110 owner = member as GroupConvoMember 111 } 112 } else { 113 logger.warn( 114 'Expected a GroupConvoMember, got an unknown kind of member', 115 ) 116 return null 117 } 118 } 119 120 if (!owner) { 121 logger.warn('No owner found in group convo') 122 return null 123 } 124 125 return { 126 view: convoView, 127 kind: 'group', 128 details: convoView.kind, 129 primaryMember: owner, 130 members: convoView.members as Array<GroupConvoMember>, 131 } 132 } else if ( 133 bsky.dangerousIsType<ChatBskyConvoDefs.DirectConvo>( 134 convoView.kind, 135 ChatBskyConvoDefs.isDirectConvo, 136 ) 137 ) { 138 const otherUser = convoView.members.find(m => m.did !== ownDid) 139 140 if (!otherUser) { 141 logger.warn('No other user found in direct convo') 142 return null 143 } 144 145 return { 146 view: convoView, 147 kind: 'direct', 148 details: convoView.kind, 149 primaryMember: otherUser as DirectConvoMember, 150 members: convoView.members as Array<DirectConvoMember>, 151 } 152 } else { 153 logger.warn('Unknown convo kind: ' + JSON.stringify(convoView.kind)) 154 return null 155 } 156}