diff options
Diffstat (limited to 'src/state/models')
-rw-r--r-- | src/state/models/_common.ts | 4 | ||||
-rw-r--r-- | src/state/models/feed-view.ts | 9 | ||||
-rw-r--r-- | src/state/models/memberships-view.ts | 4 | ||||
-rw-r--r-- | src/state/models/notifications-view.ts | 43 | ||||
-rw-r--r-- | src/state/models/post-thread-view.ts | 7 | ||||
-rw-r--r-- | src/state/models/profile-view.ts | 13 | ||||
-rw-r--r-- | src/state/models/reposted-by-view.ts | 2 | ||||
-rw-r--r-- | src/state/models/scene-invite-suggestions.ts | 126 | ||||
-rw-r--r-- | src/state/models/shell-ui.ts | 22 | ||||
-rw-r--r-- | src/state/models/suggested-actors-view.ts | 3 | ||||
-rw-r--r-- | src/state/models/user-followers-view.ts | 7 | ||||
-rw-r--r-- | src/state/models/user-follows-view.ts | 7 |
12 files changed, 226 insertions, 21 deletions
diff --git a/src/state/models/_common.ts b/src/state/models/_common.ts new file mode 100644 index 000000000..459dd7602 --- /dev/null +++ b/src/state/models/_common.ts @@ -0,0 +1,4 @@ +export interface Declaration { + cid: string + actorType: string +} diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts index 392f9d215..27b4b04e2 100644 --- a/src/state/models/feed-view.ts +++ b/src/state/models/feed-view.ts @@ -21,8 +21,13 @@ export class FeedItemModel implements GetTimeline.FeedItem { // data uri: string = '' cid: string = '' - author: GetTimeline.User = {did: '', handle: '', displayName: ''} - repostedBy?: GetTimeline.User + author: GetTimeline.Actor = { + did: '', + handle: '', + displayName: '', + declaration: {cid: '', actorType: ''}, + } + repostedBy?: GetTimeline.Actor record: Record<string, unknown> = {} embed?: | GetTimeline.RecordEmbed diff --git a/src/state/models/memberships-view.ts b/src/state/models/memberships-view.ts index 504369c4f..4dfe2ab13 100644 --- a/src/state/models/memberships-view.ts +++ b/src/state/models/memberships-view.ts @@ -47,6 +47,10 @@ export class MembershipsViewModel { return this.hasLoaded && !this.hasContent } + isMemberOf(did: string) { + return !!this.memberships.find(m => m.did === did) + } + // public api // = diff --git a/src/state/models/notifications-view.ts b/src/state/models/notifications-view.ts index 7b1508a19..43bc0cb4d 100644 --- a/src/state/models/notifications-view.ts +++ b/src/state/models/notifications-view.ts @@ -1,7 +1,11 @@ import {makeAutoObservable} from 'mobx' import * as ListNotifications from '../../third-party/api/src/client/types/app/bsky/notification/list' import {RootStoreModel} from './root-store' +import {Declaration} from './_common' import {hasProp} from '../lib/type-guards' +import {APP_BSKY_GRAPH} from '../../third-party/api' + +const UNGROUPABLE_REASONS = ['trend', 'assertion'] export interface GroupedNotification extends ListNotifications.Notification { additional?: ListNotifications.Notification[] @@ -18,7 +22,8 @@ export class NotificationsViewItemModel implements GroupedNotification { did: string handle: string displayName?: string - } = {did: '', handle: ''} + declaration: Declaration + } = {did: '', handle: '', declaration: {cid: '', actorType: ''}} reason: string = '' reasonSubject?: string record: any = {} @@ -65,6 +70,10 @@ export class NotificationsViewItemModel implements GroupedNotification { return this.reason === 'repost' } + get isTrend() { + return this.reason === 'trend' + } + get isReply() { return this.reason === 'reply' } @@ -73,6 +82,16 @@ export class NotificationsViewItemModel implements GroupedNotification { return this.reason === 'follow' } + get isAssertion() { + return this.reason === 'assertion' + } + + get isInvite() { + return ( + this.isAssertion && this.record.assertion === APP_BSKY_GRAPH.AssertMember + ) + } + get subjectUri() { if (this.reasonSubject) { return this.reasonSubject @@ -316,16 +335,18 @@ function groupNotifications( const items2: GroupedNotification[] = [] for (const item of items) { let grouped = false - for (const item2 of items2) { - if ( - item.reason === item2.reason && - item.reasonSubject === item2.reasonSubject && - item.author.did !== item2.author.did - ) { - item2.additional = item2.additional || [] - item2.additional.push(item) - grouped = true - break + if (!UNGROUPABLE_REASONS.includes(item.reason)) { + for (const item2 of items2) { + if ( + item.reason === item2.reason && + item.reasonSubject === item2.reasonSubject && + item.author.did !== item2.author.did + ) { + item2.additional = item2.additional || [] + item2.additional.push(item) + grouped = true + break + } } } if (!grouped) { diff --git a/src/state/models/post-thread-view.ts b/src/state/models/post-thread-view.ts index 7a735de03..385aa2e8e 100644 --- a/src/state/models/post-thread-view.ts +++ b/src/state/models/post-thread-view.ts @@ -31,7 +31,12 @@ export class PostThreadViewPostModel implements GetPostThread.Post { // data uri: string = '' cid: string = '' - author: GetPostThread.User = {did: '', handle: '', displayName: ''} + author: GetPostThread.User = { + did: '', + handle: '', + displayName: '', + declaration: {cid: '', actorType: ''}, + } record: Record<string, unknown> = {} embed?: | GetPostThread.RecordEmbed diff --git a/src/state/models/profile-view.ts b/src/state/models/profile-view.ts index 62a17a6fd..4df338fc0 100644 --- a/src/state/models/profile-view.ts +++ b/src/state/models/profile-view.ts @@ -1,6 +1,7 @@ import {makeAutoObservable, runInAction} from 'mobx' import * as GetProfile from '../../third-party/api/src/client/types/app/bsky/actor/getProfile' import * as Profile from '../../third-party/api/src/client/types/app/bsky/actor/profile' +import {Declaration} from './_common' import {RootStoreModel} from './root-store' import * as apilib from '../lib/api' @@ -9,6 +10,7 @@ export const ACTOR_TYPE_SCENE = 'app.bsky.system.actorScene' export class ProfileViewMyStateModel { follow?: string + member?: string constructor() { makeAutoObservable(this) @@ -26,7 +28,10 @@ export class ProfileViewModel { // data did: string = '' handle: string = '' - actorType = ACTOR_TYPE_USER + declaration: Declaration = { + cid: '', + actorType: '', + } creator: string = '' displayName?: string description?: string @@ -64,11 +69,11 @@ export class ProfileViewModel { } get isUser() { - return this.actorType === ACTOR_TYPE_USER + return this.declaration.actorType === ACTOR_TYPE_USER } get isScene() { - return this.actorType === ACTOR_TYPE_SCENE + return this.declaration.actorType === ACTOR_TYPE_SCENE } // public api @@ -145,7 +150,7 @@ export class ProfileViewModel { console.log(res.data) this.did = res.data.did this.handle = res.data.handle - this.actorType = res.data.actorType + Object.assign(this.declaration, res.data.declaration) this.creator = res.data.creator this.displayName = res.data.displayName this.description = res.data.description diff --git a/src/state/models/reposted-by-view.ts b/src/state/models/reposted-by-view.ts index 2911ae7ef..211a755dc 100644 --- a/src/state/models/reposted-by-view.ts +++ b/src/state/models/reposted-by-view.ts @@ -2,6 +2,7 @@ import {makeAutoObservable, runInAction} from 'mobx' import {AtUri} from '../../third-party/uri' import * as GetRepostedBy from '../../third-party/api/src/client/types/app/bsky/feed/getRepostedBy' import {RootStoreModel} from './root-store' +import {Declaration} from './_common' type RepostedByItem = GetRepostedBy.OutputSchema['repostedBy'][number] @@ -13,6 +14,7 @@ export class RepostedByViewItemModel implements RepostedByItem { did: string = '' handle: string = '' displayName: string = '' + declaration: Declaration = {cid: '', actorType: ''} createdAt?: string indexedAt: string = '' diff --git a/src/state/models/scene-invite-suggestions.ts b/src/state/models/scene-invite-suggestions.ts new file mode 100644 index 000000000..34bb5c28d --- /dev/null +++ b/src/state/models/scene-invite-suggestions.ts @@ -0,0 +1,126 @@ +import {makeAutoObservable, runInAction} from 'mobx' +import {RootStoreModel} from './root-store' +import {MembersViewModel} from './members-view' +import {UserFollowsViewModel, FollowItem} from './user-follows-view' + +export interface SceneInviteSuggestionsParams { + sceneDid: string +} + +export class SceneInviteSuggestions { + // state + isLoading = false + isRefreshing = false + hasLoaded = false + error = '' + params: SceneInviteSuggestionsParams + sceneMembersView: MembersViewModel + myFollowsView: UserFollowsViewModel + + // data + suggestions: FollowItem[] = [] + + constructor( + public rootStore: RootStoreModel, + params: SceneInviteSuggestionsParams, + ) { + makeAutoObservable( + this, + { + rootStore: false, + params: false, + }, + {autoBind: true}, + ) + this.params = params + this.sceneMembersView = new MembersViewModel(rootStore, { + actor: params.sceneDid, + }) + this.myFollowsView = new UserFollowsViewModel(rootStore, { + user: rootStore.me.did || '', + }) + } + + get hasContent() { + return this.suggestions.length > 0 + } + + get hasError() { + return this.error !== '' + } + + get isEmpty() { + return this.hasLoaded && !this.hasContent + } + + // public api + // = + + async setup() { + await this._fetch(false) + } + + async refresh() { + await this._fetch(true) + } + + async loadMore() { + // TODO + } + + // state transitions + // = + + private _xLoading(isRefreshing = false) { + this.isLoading = true + this.isRefreshing = isRefreshing + this.error = '' + } + + private _xIdle(err: string = '') { + this.isLoading = false + this.isRefreshing = false + this.hasLoaded = true + this.error = err + } + + // loader functions + // = + + private async _fetch(isRefreshing = false) { + this._xLoading(isRefreshing) + try { + await this.sceneMembersView.setup() + } catch (e) { + console.error(e) + this._xIdle( + 'Failed to fetch the current scene members. Check your internet connection and try again.', + ) + return + } + try { + await this.myFollowsView.setup() + } catch (e) { + console.error(e) + this._xIdle( + 'Failed to fetch the your current followers. Check your internet connection and try again.', + ) + return + } + + // collect all followed users that arent already in the scene + const newSuggestions: FollowItem[] = [] + for (const follow of this.myFollowsView.follows) { + // TODO: filter out scenes + if ( + !this.sceneMembersView.members.find(member => member.did === follow.did) + ) { + newSuggestions.push(follow) + } + } + runInAction(() => { + this.suggestions = newSuggestions + }) + this._xIdle() + } +} diff --git a/src/state/models/shell-ui.ts b/src/state/models/shell-ui.ts index 345a6b4a9..8eefc711c 100644 --- a/src/state/models/shell-ui.ts +++ b/src/state/models/shell-ui.ts @@ -19,6 +19,18 @@ export class LinkActionsModel { } } +export class ConfirmModel { + name = 'confirm' + + constructor( + public title: string, + public message: string | (() => JSX.Element), + public onPressConfirm: () => void | Promise<void>, + ) { + makeAutoObservable(this) + } +} + export class SharePostModel { name = 'share-post' @@ -43,6 +55,14 @@ export class CreateSceneModel { } } +export class InviteToSceneModel { + name = 'invite-to-scene' + + constructor(public profileView: ProfileViewModel) { + makeAutoObservable(this) + } +} + export interface ComposerOpts { replyTo?: Post.PostRef onPost?: () => void @@ -52,6 +72,7 @@ export class ShellUiModel { isModalActive = false activeModal: | LinkActionsModel + | ConfirmModel | SharePostModel | EditProfileModel | CreateSceneModel @@ -66,6 +87,7 @@ export class ShellUiModel { openModal( modal: | LinkActionsModel + | ConfirmModel | SharePostModel | EditProfileModel | CreateSceneModel, diff --git a/src/state/models/suggested-actors-view.ts b/src/state/models/suggested-actors-view.ts index 245dbf020..a22532378 100644 --- a/src/state/models/suggested-actors-view.ts +++ b/src/state/models/suggested-actors-view.ts @@ -1,5 +1,6 @@ import {makeAutoObservable} from 'mobx' import {RootStoreModel} from './root-store' +import {Declaration} from './_common' interface Response { data: { @@ -9,6 +10,7 @@ interface Response { export type ResponseSuggestedActor = { did: string handle: string + declaration: Declaration displayName?: string description?: string createdAt?: string @@ -109,7 +111,6 @@ export class SuggestedActorsViewModel { for (const item of res.data.suggestions) { this._append({ _reactKey: `item-${counter++}`, - description: 'Just another cool person using Bluesky', ...item, }) } diff --git a/src/state/models/user-followers-view.ts b/src/state/models/user-followers-view.ts index 1edd95232..b27201997 100644 --- a/src/state/models/user-followers-view.ts +++ b/src/state/models/user-followers-view.ts @@ -16,7 +16,12 @@ export class UserFollowersViewModel { params: GetFollowers.QueryParams // data - subject: Subject = {did: '', handle: '', displayName: ''} + subject: Subject = { + did: '', + handle: '', + displayName: '', + declaration: {cid: '', actorType: ''}, + } followers: FollowerItem[] = [] constructor( diff --git a/src/state/models/user-follows-view.ts b/src/state/models/user-follows-view.ts index b7875c22c..13742c327 100644 --- a/src/state/models/user-follows-view.ts +++ b/src/state/models/user-follows-view.ts @@ -16,7 +16,12 @@ export class UserFollowsViewModel { params: GetFollows.QueryParams // data - subject: Subject = {did: '', handle: '', displayName: ''} + subject: Subject = { + did: '', + handle: '', + displayName: '', + declaration: {cid: '', actorType: ''}, + } follows: FollowItem[] = [] constructor( |