Implement scene invitation and membership controls
parent
ecf56729b0
commit
d3707f30e3
|
@ -8,6 +8,7 @@ import {sessionClient as AtpApi} from '../../third-party/api'
|
||||||
import * as Profile from '../../third-party/api/src/client/types/app/bsky/actor/profile'
|
import * as Profile from '../../third-party/api/src/client/types/app/bsky/actor/profile'
|
||||||
import * as Post from '../../third-party/api/src/client/types/app/bsky/feed/post'
|
import * as Post from '../../third-party/api/src/client/types/app/bsky/feed/post'
|
||||||
import {AtUri} from '../../third-party/uri'
|
import {AtUri} from '../../third-party/uri'
|
||||||
|
import {APP_BSKY_GRAPH} from '../../third-party/api'
|
||||||
import {RootStoreModel} from '../models/root-store'
|
import {RootStoreModel} from '../models/root-store'
|
||||||
import {extractEntities} from '../../view/lib/strings'
|
import {extractEntities} from '../../view/lib/strings'
|
||||||
|
|
||||||
|
@ -156,6 +157,54 @@ export async function updateProfile(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function inviteToScene(
|
||||||
|
store: RootStoreModel,
|
||||||
|
sceneDid: string,
|
||||||
|
subjectDid: string,
|
||||||
|
subjectDeclarationCid: string,
|
||||||
|
): Promise<string> {
|
||||||
|
const res = await store.api.app.bsky.graph.assertion.create(
|
||||||
|
{
|
||||||
|
did: sceneDid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
subject: {
|
||||||
|
did: subjectDid,
|
||||||
|
declarationCid: subjectDeclarationCid,
|
||||||
|
},
|
||||||
|
assertion: APP_BSKY_GRAPH.AssertMember,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return res.uri
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Confirmation {
|
||||||
|
originator: {
|
||||||
|
did: string
|
||||||
|
declarationCid: string
|
||||||
|
}
|
||||||
|
assertion: {
|
||||||
|
uri: string
|
||||||
|
cid: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function acceptSceneInvite(
|
||||||
|
store: RootStoreModel,
|
||||||
|
details: Confirmation,
|
||||||
|
): Promise<string> {
|
||||||
|
const res = await store.api.app.bsky.graph.confirmation.create(
|
||||||
|
{
|
||||||
|
did: store.me.did || '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...details,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return res.uri
|
||||||
|
}
|
||||||
|
|
||||||
interface FetchHandlerResponse {
|
interface FetchHandlerResponse {
|
||||||
status: number
|
status: number
|
||||||
headers: Record<string, string>
|
headers: Record<string, string>
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string
|
||||||
|
actorType: string
|
||||||
|
}
|
|
@ -21,8 +21,13 @@ export class FeedItemModel implements GetTimeline.FeedItem {
|
||||||
// data
|
// data
|
||||||
uri: string = ''
|
uri: string = ''
|
||||||
cid: string = ''
|
cid: string = ''
|
||||||
author: GetTimeline.User = {did: '', handle: '', displayName: ''}
|
author: GetTimeline.Actor = {
|
||||||
repostedBy?: GetTimeline.User
|
did: '',
|
||||||
|
handle: '',
|
||||||
|
displayName: '',
|
||||||
|
declaration: {cid: '', actorType: ''},
|
||||||
|
}
|
||||||
|
repostedBy?: GetTimeline.Actor
|
||||||
record: Record<string, unknown> = {}
|
record: Record<string, unknown> = {}
|
||||||
embed?:
|
embed?:
|
||||||
| GetTimeline.RecordEmbed
|
| GetTimeline.RecordEmbed
|
||||||
|
|
|
@ -47,6 +47,10 @@ export class MembershipsViewModel {
|
||||||
return this.hasLoaded && !this.hasContent
|
return this.hasLoaded && !this.hasContent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isMemberOf(did: string) {
|
||||||
|
return !!this.memberships.find(m => m.did === did)
|
||||||
|
}
|
||||||
|
|
||||||
// public api
|
// public api
|
||||||
// =
|
// =
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import {makeAutoObservable} from 'mobx'
|
import {makeAutoObservable} from 'mobx'
|
||||||
import * as ListNotifications from '../../third-party/api/src/client/types/app/bsky/notification/list'
|
import * as ListNotifications from '../../third-party/api/src/client/types/app/bsky/notification/list'
|
||||||
import {RootStoreModel} from './root-store'
|
import {RootStoreModel} from './root-store'
|
||||||
|
import {Declaration} from './_common'
|
||||||
import {hasProp} from '../lib/type-guards'
|
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 {
|
export interface GroupedNotification extends ListNotifications.Notification {
|
||||||
additional?: ListNotifications.Notification[]
|
additional?: ListNotifications.Notification[]
|
||||||
|
@ -18,7 +22,8 @@ export class NotificationsViewItemModel implements GroupedNotification {
|
||||||
did: string
|
did: string
|
||||||
handle: string
|
handle: string
|
||||||
displayName?: string
|
displayName?: string
|
||||||
} = {did: '', handle: ''}
|
declaration: Declaration
|
||||||
|
} = {did: '', handle: '', declaration: {cid: '', actorType: ''}}
|
||||||
reason: string = ''
|
reason: string = ''
|
||||||
reasonSubject?: string
|
reasonSubject?: string
|
||||||
record: any = {}
|
record: any = {}
|
||||||
|
@ -65,6 +70,10 @@ export class NotificationsViewItemModel implements GroupedNotification {
|
||||||
return this.reason === 'repost'
|
return this.reason === 'repost'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isTrend() {
|
||||||
|
return this.reason === 'trend'
|
||||||
|
}
|
||||||
|
|
||||||
get isReply() {
|
get isReply() {
|
||||||
return this.reason === 'reply'
|
return this.reason === 'reply'
|
||||||
}
|
}
|
||||||
|
@ -73,6 +82,16 @@ export class NotificationsViewItemModel implements GroupedNotification {
|
||||||
return this.reason === 'follow'
|
return this.reason === 'follow'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isAssertion() {
|
||||||
|
return this.reason === 'assertion'
|
||||||
|
}
|
||||||
|
|
||||||
|
get isInvite() {
|
||||||
|
return (
|
||||||
|
this.isAssertion && this.record.assertion === APP_BSKY_GRAPH.AssertMember
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
get subjectUri() {
|
get subjectUri() {
|
||||||
if (this.reasonSubject) {
|
if (this.reasonSubject) {
|
||||||
return this.reasonSubject
|
return this.reasonSubject
|
||||||
|
@ -316,16 +335,18 @@ function groupNotifications(
|
||||||
const items2: GroupedNotification[] = []
|
const items2: GroupedNotification[] = []
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
let grouped = false
|
let grouped = false
|
||||||
for (const item2 of items2) {
|
if (!UNGROUPABLE_REASONS.includes(item.reason)) {
|
||||||
if (
|
for (const item2 of items2) {
|
||||||
item.reason === item2.reason &&
|
if (
|
||||||
item.reasonSubject === item2.reasonSubject &&
|
item.reason === item2.reason &&
|
||||||
item.author.did !== item2.author.did
|
item.reasonSubject === item2.reasonSubject &&
|
||||||
) {
|
item.author.did !== item2.author.did
|
||||||
item2.additional = item2.additional || []
|
) {
|
||||||
item2.additional.push(item)
|
item2.additional = item2.additional || []
|
||||||
grouped = true
|
item2.additional.push(item)
|
||||||
break
|
grouped = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!grouped) {
|
if (!grouped) {
|
||||||
|
|
|
@ -31,7 +31,12 @@ export class PostThreadViewPostModel implements GetPostThread.Post {
|
||||||
// data
|
// data
|
||||||
uri: string = ''
|
uri: string = ''
|
||||||
cid: string = ''
|
cid: string = ''
|
||||||
author: GetPostThread.User = {did: '', handle: '', displayName: ''}
|
author: GetPostThread.User = {
|
||||||
|
did: '',
|
||||||
|
handle: '',
|
||||||
|
displayName: '',
|
||||||
|
declaration: {cid: '', actorType: ''},
|
||||||
|
}
|
||||||
record: Record<string, unknown> = {}
|
record: Record<string, unknown> = {}
|
||||||
embed?:
|
embed?:
|
||||||
| GetPostThread.RecordEmbed
|
| GetPostThread.RecordEmbed
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {makeAutoObservable, runInAction} from 'mobx'
|
import {makeAutoObservable, runInAction} from 'mobx'
|
||||||
import * as GetProfile from '../../third-party/api/src/client/types/app/bsky/actor/getProfile'
|
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 * as Profile from '../../third-party/api/src/client/types/app/bsky/actor/profile'
|
||||||
|
import {Declaration} from './_common'
|
||||||
import {RootStoreModel} from './root-store'
|
import {RootStoreModel} from './root-store'
|
||||||
import * as apilib from '../lib/api'
|
import * as apilib from '../lib/api'
|
||||||
|
|
||||||
|
@ -9,6 +10,7 @@ export const ACTOR_TYPE_SCENE = 'app.bsky.system.actorScene'
|
||||||
|
|
||||||
export class ProfileViewMyStateModel {
|
export class ProfileViewMyStateModel {
|
||||||
follow?: string
|
follow?: string
|
||||||
|
member?: string
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeAutoObservable(this)
|
makeAutoObservable(this)
|
||||||
|
@ -26,7 +28,10 @@ export class ProfileViewModel {
|
||||||
// data
|
// data
|
||||||
did: string = ''
|
did: string = ''
|
||||||
handle: string = ''
|
handle: string = ''
|
||||||
actorType = ACTOR_TYPE_USER
|
declaration: Declaration = {
|
||||||
|
cid: '',
|
||||||
|
actorType: '',
|
||||||
|
}
|
||||||
creator: string = ''
|
creator: string = ''
|
||||||
displayName?: string
|
displayName?: string
|
||||||
description?: string
|
description?: string
|
||||||
|
@ -64,11 +69,11 @@ export class ProfileViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
get isUser() {
|
get isUser() {
|
||||||
return this.actorType === ACTOR_TYPE_USER
|
return this.declaration.actorType === ACTOR_TYPE_USER
|
||||||
}
|
}
|
||||||
|
|
||||||
get isScene() {
|
get isScene() {
|
||||||
return this.actorType === ACTOR_TYPE_SCENE
|
return this.declaration.actorType === ACTOR_TYPE_SCENE
|
||||||
}
|
}
|
||||||
|
|
||||||
// public api
|
// public api
|
||||||
|
@ -145,7 +150,7 @@ export class ProfileViewModel {
|
||||||
console.log(res.data)
|
console.log(res.data)
|
||||||
this.did = res.data.did
|
this.did = res.data.did
|
||||||
this.handle = res.data.handle
|
this.handle = res.data.handle
|
||||||
this.actorType = res.data.actorType
|
Object.assign(this.declaration, res.data.declaration)
|
||||||
this.creator = res.data.creator
|
this.creator = res.data.creator
|
||||||
this.displayName = res.data.displayName
|
this.displayName = res.data.displayName
|
||||||
this.description = res.data.description
|
this.description = res.data.description
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {makeAutoObservable, runInAction} from 'mobx'
|
||||||
import {AtUri} from '../../third-party/uri'
|
import {AtUri} from '../../third-party/uri'
|
||||||
import * as GetRepostedBy from '../../third-party/api/src/client/types/app/bsky/feed/getRepostedBy'
|
import * as GetRepostedBy from '../../third-party/api/src/client/types/app/bsky/feed/getRepostedBy'
|
||||||
import {RootStoreModel} from './root-store'
|
import {RootStoreModel} from './root-store'
|
||||||
|
import {Declaration} from './_common'
|
||||||
|
|
||||||
type RepostedByItem = GetRepostedBy.OutputSchema['repostedBy'][number]
|
type RepostedByItem = GetRepostedBy.OutputSchema['repostedBy'][number]
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ export class RepostedByViewItemModel implements RepostedByItem {
|
||||||
did: string = ''
|
did: string = ''
|
||||||
handle: string = ''
|
handle: string = ''
|
||||||
displayName: string = ''
|
displayName: string = ''
|
||||||
|
declaration: Declaration = {cid: '', actorType: ''}
|
||||||
createdAt?: string
|
createdAt?: string
|
||||||
indexedAt: string = ''
|
indexedAt: string = ''
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
export class SharePostModel {
|
||||||
name = 'share-post'
|
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 {
|
export interface ComposerOpts {
|
||||||
replyTo?: Post.PostRef
|
replyTo?: Post.PostRef
|
||||||
onPost?: () => void
|
onPost?: () => void
|
||||||
|
@ -52,6 +72,7 @@ export class ShellUiModel {
|
||||||
isModalActive = false
|
isModalActive = false
|
||||||
activeModal:
|
activeModal:
|
||||||
| LinkActionsModel
|
| LinkActionsModel
|
||||||
|
| ConfirmModel
|
||||||
| SharePostModel
|
| SharePostModel
|
||||||
| EditProfileModel
|
| EditProfileModel
|
||||||
| CreateSceneModel
|
| CreateSceneModel
|
||||||
|
@ -66,6 +87,7 @@ export class ShellUiModel {
|
||||||
openModal(
|
openModal(
|
||||||
modal:
|
modal:
|
||||||
| LinkActionsModel
|
| LinkActionsModel
|
||||||
|
| ConfirmModel
|
||||||
| SharePostModel
|
| SharePostModel
|
||||||
| EditProfileModel
|
| EditProfileModel
|
||||||
| CreateSceneModel,
|
| CreateSceneModel,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {makeAutoObservable} from 'mobx'
|
import {makeAutoObservable} from 'mobx'
|
||||||
import {RootStoreModel} from './root-store'
|
import {RootStoreModel} from './root-store'
|
||||||
|
import {Declaration} from './_common'
|
||||||
|
|
||||||
interface Response {
|
interface Response {
|
||||||
data: {
|
data: {
|
||||||
|
@ -9,6 +10,7 @@ interface Response {
|
||||||
export type ResponseSuggestedActor = {
|
export type ResponseSuggestedActor = {
|
||||||
did: string
|
did: string
|
||||||
handle: string
|
handle: string
|
||||||
|
declaration: Declaration
|
||||||
displayName?: string
|
displayName?: string
|
||||||
description?: string
|
description?: string
|
||||||
createdAt?: string
|
createdAt?: string
|
||||||
|
@ -109,7 +111,6 @@ export class SuggestedActorsViewModel {
|
||||||
for (const item of res.data.suggestions) {
|
for (const item of res.data.suggestions) {
|
||||||
this._append({
|
this._append({
|
||||||
_reactKey: `item-${counter++}`,
|
_reactKey: `item-${counter++}`,
|
||||||
description: 'Just another cool person using Bluesky',
|
|
||||||
...item,
|
...item,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,12 @@ export class UserFollowersViewModel {
|
||||||
params: GetFollowers.QueryParams
|
params: GetFollowers.QueryParams
|
||||||
|
|
||||||
// data
|
// data
|
||||||
subject: Subject = {did: '', handle: '', displayName: ''}
|
subject: Subject = {
|
||||||
|
did: '',
|
||||||
|
handle: '',
|
||||||
|
displayName: '',
|
||||||
|
declaration: {cid: '', actorType: ''},
|
||||||
|
}
|
||||||
followers: FollowerItem[] = []
|
followers: FollowerItem[] = []
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -16,7 +16,12 @@ export class UserFollowsViewModel {
|
||||||
params: GetFollows.QueryParams
|
params: GetFollows.QueryParams
|
||||||
|
|
||||||
// data
|
// data
|
||||||
subject: Subject = {did: '', handle: '', displayName: ''}
|
subject: Subject = {
|
||||||
|
did: '',
|
||||||
|
handle: '',
|
||||||
|
displayName: '',
|
||||||
|
declaration: {cid: '', actorType: ''},
|
||||||
|
}
|
||||||
follows: FollowItem[] = []
|
follows: FollowItem[] = []
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -36,6 +36,8 @@ import * as AppBskyFeedGetVotes from './types/app/bsky/feed/getVotes';
|
||||||
import * as AppBskyFeedMediaEmbed from './types/app/bsky/feed/mediaEmbed';
|
import * as AppBskyFeedMediaEmbed from './types/app/bsky/feed/mediaEmbed';
|
||||||
import * as AppBskyFeedPost from './types/app/bsky/feed/post';
|
import * as AppBskyFeedPost from './types/app/bsky/feed/post';
|
||||||
import * as AppBskyFeedRepost from './types/app/bsky/feed/repost';
|
import * as AppBskyFeedRepost from './types/app/bsky/feed/repost';
|
||||||
|
import * as AppBskyFeedSetVote from './types/app/bsky/feed/setVote';
|
||||||
|
import * as AppBskyFeedTrend from './types/app/bsky/feed/trend';
|
||||||
import * as AppBskyFeedVote from './types/app/bsky/feed/vote';
|
import * as AppBskyFeedVote from './types/app/bsky/feed/vote';
|
||||||
import * as AppBskyGraphAssertion from './types/app/bsky/graph/assertion';
|
import * as AppBskyGraphAssertion from './types/app/bsky/graph/assertion';
|
||||||
import * as AppBskyGraphConfirmation from './types/app/bsky/graph/confirmation';
|
import * as AppBskyGraphConfirmation from './types/app/bsky/graph/confirmation';
|
||||||
|
@ -85,6 +87,8 @@ export * as AppBskyFeedGetVotes from './types/app/bsky/feed/getVotes';
|
||||||
export * as AppBskyFeedMediaEmbed from './types/app/bsky/feed/mediaEmbed';
|
export * as AppBskyFeedMediaEmbed from './types/app/bsky/feed/mediaEmbed';
|
||||||
export * as AppBskyFeedPost from './types/app/bsky/feed/post';
|
export * as AppBskyFeedPost from './types/app/bsky/feed/post';
|
||||||
export * as AppBskyFeedRepost from './types/app/bsky/feed/repost';
|
export * as AppBskyFeedRepost from './types/app/bsky/feed/repost';
|
||||||
|
export * as AppBskyFeedSetVote from './types/app/bsky/feed/setVote';
|
||||||
|
export * as AppBskyFeedTrend from './types/app/bsky/feed/trend';
|
||||||
export * as AppBskyFeedVote from './types/app/bsky/feed/vote';
|
export * as AppBskyFeedVote from './types/app/bsky/feed/vote';
|
||||||
export * as AppBskyGraphAssertion from './types/app/bsky/graph/assertion';
|
export * as AppBskyGraphAssertion from './types/app/bsky/graph/assertion';
|
||||||
export * as AppBskyGraphConfirmation from './types/app/bsky/graph/confirmation';
|
export * as AppBskyGraphConfirmation from './types/app/bsky/graph/confirmation';
|
||||||
|
@ -225,13 +229,14 @@ export declare class ProfileRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
export declare class FeedNS {
|
export declare class FeedNS {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
mediaEmbed: MediaEmbedRecord;
|
mediaEmbed: MediaEmbedRecord;
|
||||||
post: PostRecord;
|
post: PostRecord;
|
||||||
repost: RepostRecord;
|
repost: RepostRecord;
|
||||||
|
trend: TrendRecord;
|
||||||
vote: VoteRecord;
|
vote: VoteRecord;
|
||||||
constructor(service: ServiceClient);
|
constructor(service: ServiceClient);
|
||||||
getAuthorFeed(params?: AppBskyFeedGetAuthorFeed.QueryParams, opts?: AppBskyFeedGetAuthorFeed.CallOptions): Promise<AppBskyFeedGetAuthorFeed.Response>;
|
getAuthorFeed(params?: AppBskyFeedGetAuthorFeed.QueryParams, opts?: AppBskyFeedGetAuthorFeed.CallOptions): Promise<AppBskyFeedGetAuthorFeed.Response>;
|
||||||
|
@ -239,6 +244,7 @@ export declare class FeedNS {
|
||||||
getRepostedBy(params?: AppBskyFeedGetRepostedBy.QueryParams, opts?: AppBskyFeedGetRepostedBy.CallOptions): Promise<AppBskyFeedGetRepostedBy.Response>;
|
getRepostedBy(params?: AppBskyFeedGetRepostedBy.QueryParams, opts?: AppBskyFeedGetRepostedBy.CallOptions): Promise<AppBskyFeedGetRepostedBy.Response>;
|
||||||
getTimeline(params?: AppBskyFeedGetTimeline.QueryParams, opts?: AppBskyFeedGetTimeline.CallOptions): Promise<AppBskyFeedGetTimeline.Response>;
|
getTimeline(params?: AppBskyFeedGetTimeline.QueryParams, opts?: AppBskyFeedGetTimeline.CallOptions): Promise<AppBskyFeedGetTimeline.Response>;
|
||||||
getVotes(params?: AppBskyFeedGetVotes.QueryParams, opts?: AppBskyFeedGetVotes.CallOptions): Promise<AppBskyFeedGetVotes.Response>;
|
getVotes(params?: AppBskyFeedGetVotes.QueryParams, opts?: AppBskyFeedGetVotes.CallOptions): Promise<AppBskyFeedGetVotes.Response>;
|
||||||
|
setVote(data?: AppBskyFeedSetVote.InputSchema, opts?: AppBskyFeedSetVote.CallOptions): Promise<AppBskyFeedSetVote.Response>;
|
||||||
}
|
}
|
||||||
export declare class MediaEmbedRecord {
|
export declare class MediaEmbedRecord {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
|
@ -259,7 +265,7 @@ export declare class MediaEmbedRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
export declare class PostRecord {
|
export declare class PostRecord {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
|
@ -280,7 +286,7 @@ export declare class PostRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
export declare class RepostRecord {
|
export declare class RepostRecord {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
|
@ -301,7 +307,28 @@ export declare class RepostRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
|
}
|
||||||
|
export declare class TrendRecord {
|
||||||
|
_service: ServiceClient;
|
||||||
|
constructor(service: ServiceClient);
|
||||||
|
list(params: Omit<ComAtprotoRepoListRecords.QueryParams, 'collection'>): Promise<{
|
||||||
|
cursor?: string;
|
||||||
|
records: {
|
||||||
|
uri: string;
|
||||||
|
value: AppBskyFeedTrend.Record;
|
||||||
|
}[];
|
||||||
|
}>;
|
||||||
|
get(params: Omit<ComAtprotoRepoGetRecord.QueryParams, 'collection'>): Promise<{
|
||||||
|
uri: string;
|
||||||
|
cid: string;
|
||||||
|
value: AppBskyFeedTrend.Record;
|
||||||
|
}>;
|
||||||
|
create(params: Omit<ComAtprotoRepoCreateRecord.InputSchema, 'collection' | 'record'>, record: AppBskyFeedTrend.Record, headers?: Record<string, string>): Promise<{
|
||||||
|
uri: string;
|
||||||
|
cid: string;
|
||||||
|
}>;
|
||||||
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
export declare class VoteRecord {
|
export declare class VoteRecord {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
|
@ -322,7 +349,7 @@ export declare class VoteRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
export declare class GraphNS {
|
export declare class GraphNS {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
|
@ -354,7 +381,7 @@ export declare class AssertionRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
export declare class ConfirmationRecord {
|
export declare class ConfirmationRecord {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
|
@ -375,7 +402,7 @@ export declare class ConfirmationRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
export declare class FollowRecord {
|
export declare class FollowRecord {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
|
@ -396,7 +423,7 @@ export declare class FollowRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
export declare class NotificationNS {
|
export declare class NotificationNS {
|
||||||
_service: ServiceClient;
|
_service: ServiceClient;
|
||||||
|
@ -429,5 +456,5 @@ export declare class DeclarationRecord {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
}>;
|
}>;
|
||||||
delete(params: Omit<ComAtprotoRepoDeleteRecord.QueryParams, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
delete(params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, headers?: Record<string, string>): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ export declare const ids: {
|
||||||
AppBskyFeedMediaEmbed: string;
|
AppBskyFeedMediaEmbed: string;
|
||||||
AppBskyFeedPost: string;
|
AppBskyFeedPost: string;
|
||||||
AppBskyFeedRepost: string;
|
AppBskyFeedRepost: string;
|
||||||
|
AppBskyFeedTrend: string;
|
||||||
AppBskyFeedVote: string;
|
AppBskyFeedVote: string;
|
||||||
AppBskyGraphAssertion: string;
|
AppBskyGraphAssertion: string;
|
||||||
AppBskyGraphConfirmation: string;
|
AppBskyGraphConfirmation: string;
|
||||||
|
|
|
@ -10,10 +10,16 @@ export interface InputSchema {
|
||||||
handle: string;
|
handle: string;
|
||||||
recoveryKey?: string;
|
recoveryKey?: string;
|
||||||
}
|
}
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
handle: string;
|
handle: string;
|
||||||
did: string;
|
did: string;
|
||||||
declarationCid: string;
|
declaration: Declaration;
|
||||||
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
}
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|
|
@ -10,8 +10,8 @@ export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.
|
||||||
export declare type ActorUnknown = string;
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
actorType: ActorKnown | ActorUnknown;
|
|
||||||
creator: string;
|
creator: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
@ -21,8 +21,13 @@ export interface OutputSchema {
|
||||||
postsCount: number;
|
postsCount: number;
|
||||||
myState?: {
|
myState?: {
|
||||||
follow?: string;
|
follow?: string;
|
||||||
|
member?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -7,12 +7,14 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
actors: {
|
actors: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
actorType: string;
|
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
indexedAt?: string;
|
indexedAt?: string;
|
||||||
|
@ -21,6 +23,10 @@ export interface OutputSchema {
|
||||||
};
|
};
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -8,16 +8,23 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
users: {
|
users: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
indexedAt?: string;
|
indexedAt?: string;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -7,13 +7,20 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
users: {
|
users: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -8,6 +8,8 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
feed: FeedItem[];
|
feed: FeedItem[];
|
||||||
|
@ -15,8 +17,9 @@ export interface OutputSchema {
|
||||||
export interface FeedItem {
|
export interface FeedItem {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
author: User;
|
author: Actor;
|
||||||
repostedBy?: User;
|
trendedBy?: Actor;
|
||||||
|
repostedBy?: Actor;
|
||||||
record: {};
|
record: {};
|
||||||
embed?: RecordEmbed | ExternalEmbed | UnknownEmbed;
|
embed?: RecordEmbed | ExternalEmbed | UnknownEmbed;
|
||||||
replyCount: number;
|
replyCount: number;
|
||||||
|
@ -30,14 +33,19 @@ export interface FeedItem {
|
||||||
downvote?: string;
|
downvote?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface User {
|
export interface Actor {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface RecordEmbed {
|
export interface RecordEmbed {
|
||||||
type: 'record';
|
type: 'record';
|
||||||
author: User;
|
author: Actor;
|
||||||
record: {};
|
record: {};
|
||||||
}
|
}
|
||||||
export interface ExternalEmbed {
|
export interface ExternalEmbed {
|
||||||
|
|
|
@ -7,6 +7,8 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
thread: Post;
|
thread: Post;
|
||||||
}
|
}
|
||||||
|
@ -31,9 +33,14 @@ export interface Post {
|
||||||
}
|
}
|
||||||
export interface User {
|
export interface User {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface RecordEmbed {
|
export interface RecordEmbed {
|
||||||
type: 'record';
|
type: 'record';
|
||||||
author: User;
|
author: User;
|
||||||
|
|
|
@ -9,18 +9,25 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid?: string;
|
cid?: string;
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
repostedBy: {
|
repostedBy: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
indexedAt: string;
|
indexedAt: string;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -8,6 +8,8 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
feed: FeedItem[];
|
feed: FeedItem[];
|
||||||
|
@ -15,8 +17,9 @@ export interface OutputSchema {
|
||||||
export interface FeedItem {
|
export interface FeedItem {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid: string;
|
cid: string;
|
||||||
author: User;
|
author: Actor;
|
||||||
repostedBy?: User;
|
trendedBy?: Actor;
|
||||||
|
repostedBy?: Actor;
|
||||||
record: {};
|
record: {};
|
||||||
embed?: RecordEmbed | ExternalEmbed | UnknownEmbed;
|
embed?: RecordEmbed | ExternalEmbed | UnknownEmbed;
|
||||||
replyCount: number;
|
replyCount: number;
|
||||||
|
@ -30,14 +33,20 @@ export interface FeedItem {
|
||||||
downvote?: string;
|
downvote?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface User {
|
export interface Actor {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
|
actorType?: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface RecordEmbed {
|
export interface RecordEmbed {
|
||||||
type: 'record';
|
type: 'record';
|
||||||
author: User;
|
author: Actor;
|
||||||
record: {};
|
record: {};
|
||||||
}
|
}
|
||||||
export interface ExternalEmbed {
|
export interface ExternalEmbed {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Headers } from '@atproto/xrpc';
|
||||||
export interface QueryParams {
|
export interface QueryParams {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid?: string;
|
cid?: string;
|
||||||
direction?: string;
|
direction?: 'up' | 'down';
|
||||||
limit?: number;
|
limit?: number;
|
||||||
before?: string;
|
before?: string;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
uri: string;
|
uri: string;
|
||||||
cid?: string;
|
cid?: string;
|
||||||
|
@ -23,9 +25,14 @@ export interface OutputSchema {
|
||||||
}
|
}
|
||||||
export interface Actor {
|
export interface Actor {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Headers } from '@atproto/xrpc';
|
||||||
|
export interface QueryParams {
|
||||||
|
}
|
||||||
|
export interface CallOptions {
|
||||||
|
headers?: Headers;
|
||||||
|
qp?: QueryParams;
|
||||||
|
encoding: 'application/json';
|
||||||
|
}
|
||||||
|
export interface InputSchema {
|
||||||
|
subject: Subject;
|
||||||
|
direction: 'up' | 'down' | 'none';
|
||||||
|
}
|
||||||
|
export interface Subject {
|
||||||
|
uri: string;
|
||||||
|
cid: string;
|
||||||
|
}
|
||||||
|
export interface OutputSchema {
|
||||||
|
upvote?: string;
|
||||||
|
downvote?: string;
|
||||||
|
}
|
||||||
|
export interface Response {
|
||||||
|
success: boolean;
|
||||||
|
headers: Headers;
|
||||||
|
data: OutputSchema;
|
||||||
|
}
|
||||||
|
export declare function toKnownErr(e: any): any;
|
|
@ -0,0 +1,10 @@
|
||||||
|
export interface Record {
|
||||||
|
subject: Subject;
|
||||||
|
createdAt: string;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}
|
||||||
|
export interface Subject {
|
||||||
|
uri: string;
|
||||||
|
cid: string;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}
|
|
@ -8,21 +8,29 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
subject: {
|
subject: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
};
|
};
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
followers: {
|
followers: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
indexedAt: string;
|
indexedAt: string;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -8,21 +8,29 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
subject: {
|
subject: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
};
|
};
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
follows: {
|
follows: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
indexedAt: string;
|
indexedAt: string;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -8,25 +8,29 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
subject: {
|
subject: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
};
|
};
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
members: {
|
members: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
declaration: {
|
|
||||||
cid: string;
|
|
||||||
actorType: string;
|
|
||||||
};
|
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
indexedAt: string;
|
indexedAt: string;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -8,25 +8,29 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
subject: {
|
subject: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
};
|
};
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
memberships: {
|
memberships: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
declaration: {
|
|
||||||
cid: string;
|
|
||||||
actorType: string;
|
|
||||||
};
|
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
indexedAt: string;
|
indexedAt: string;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -7,6 +7,8 @@ export interface CallOptions {
|
||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
}
|
}
|
||||||
export declare type InputSchema = undefined;
|
export declare type InputSchema = undefined;
|
||||||
|
export declare type ActorKnown = 'app.bsky.system.actorUser' | 'app.bsky.system.actorScene';
|
||||||
|
export declare type ActorUnknown = string;
|
||||||
export interface OutputSchema {
|
export interface OutputSchema {
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
notifications: Notification[];
|
notifications: Notification[];
|
||||||
|
@ -16,6 +18,7 @@ export interface Notification {
|
||||||
cid: string;
|
cid: string;
|
||||||
author: {
|
author: {
|
||||||
did: string;
|
did: string;
|
||||||
|
declaration: Declaration;
|
||||||
handle: string;
|
handle: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
};
|
};
|
||||||
|
@ -25,6 +28,10 @@ export interface Notification {
|
||||||
isRead: boolean;
|
isRead: boolean;
|
||||||
indexedAt: string;
|
indexedAt: string;
|
||||||
}
|
}
|
||||||
|
export interface Declaration {
|
||||||
|
cid: string;
|
||||||
|
actorType: ActorKnown | ActorUnknown;
|
||||||
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
headers: Headers;
|
headers: Headers;
|
||||||
|
|
|
@ -18,7 +18,6 @@ export interface OutputSchema {
|
||||||
refreshJwt: string;
|
refreshJwt: string;
|
||||||
handle: string;
|
handle: string;
|
||||||
did: string;
|
did: string;
|
||||||
declarationCid: string;
|
|
||||||
}
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,95 @@
|
||||||
|
import React, {useState} from 'react'
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from 'react-native'
|
||||||
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
|
import {useStores} from '../../../state'
|
||||||
|
import {s, colors, gradients} from '../../lib/styles'
|
||||||
|
import {ErrorMessage} from '../util/ErrorMessage'
|
||||||
|
|
||||||
|
export const snapPoints = ['50%']
|
||||||
|
|
||||||
|
export function Component({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
onPressConfirm,
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
message: string | (() => JSX.Element)
|
||||||
|
onPressConfirm: () => void | Promise<void>
|
||||||
|
}) {
|
||||||
|
const store = useStores()
|
||||||
|
const [isProcessing, setIsProcessing] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<string>('')
|
||||||
|
const onPress = async () => {
|
||||||
|
setError('')
|
||||||
|
setIsProcessing(true)
|
||||||
|
try {
|
||||||
|
await onPressConfirm()
|
||||||
|
store.shell.closeModal()
|
||||||
|
return
|
||||||
|
} catch (e: any) {
|
||||||
|
setError(e.toString())
|
||||||
|
setIsProcessing(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View style={[s.flex1, s.pl10, s.pr10]}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
{typeof message === 'string' ? (
|
||||||
|
<Text style={styles.description}>{message}</Text>
|
||||||
|
) : (
|
||||||
|
message()
|
||||||
|
)}
|
||||||
|
{error ? (
|
||||||
|
<View style={s.mt10}>
|
||||||
|
<ErrorMessage message={error} />
|
||||||
|
</View>
|
||||||
|
) : undefined}
|
||||||
|
{isProcessing ? (
|
||||||
|
<View style={[styles.btn, s.mt10]}>
|
||||||
|
<ActivityIndicator />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<TouchableOpacity style={s.mt10} onPress={onPress}>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[gradients.primary.start, gradients.primary.end]}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 1, y: 1}}
|
||||||
|
style={[styles.btn]}>
|
||||||
|
<Text style={[s.white, s.bold, s.f18]}>Confirm</Text>
|
||||||
|
</LinearGradient>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
title: {
|
||||||
|
textAlign: 'center',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: 24,
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: 17,
|
||||||
|
paddingHorizontal: 22,
|
||||||
|
color: colors.gray5,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
btn: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
|
borderRadius: 32,
|
||||||
|
padding: 14,
|
||||||
|
backgroundColor: colors.gray1,
|
||||||
|
},
|
||||||
|
})
|
|
@ -53,7 +53,7 @@ export function Component({}: {}) {
|
||||||
{
|
{
|
||||||
subject: {
|
subject: {
|
||||||
did: createSceneRes.data.did,
|
did: createSceneRes.data.did,
|
||||||
declarationCid: createSceneRes.data.declarationCid,
|
declarationCid: createSceneRes.data.declaration.cid,
|
||||||
},
|
},
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
import React, {useState, useEffect, useMemo} from 'react'
|
||||||
|
import Toast from '../util/Toast'
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
FlatList,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
useWindowDimensions,
|
||||||
|
View,
|
||||||
|
} from 'react-native'
|
||||||
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import {
|
||||||
|
TabView,
|
||||||
|
SceneMap,
|
||||||
|
Route,
|
||||||
|
TabBar,
|
||||||
|
TabBarProps,
|
||||||
|
} from 'react-native-tab-view'
|
||||||
|
import _omit from 'lodash.omit'
|
||||||
|
import {AtUri} from '../../../third-party/uri'
|
||||||
|
import {ProfileCard} from '../profile/ProfileCard'
|
||||||
|
import {ErrorMessage} from '../util/ErrorMessage'
|
||||||
|
import {useStores} from '../../../state'
|
||||||
|
import * as apilib from '../../../state/lib/api'
|
||||||
|
import {ProfileViewModel} from '../../../state/models/profile-view'
|
||||||
|
import {SceneInviteSuggestions} from '../../../state/models/scene-invite-suggestions'
|
||||||
|
import {FollowItem} from '../../../state/models/user-follows-view'
|
||||||
|
import {s, colors} from '../../lib/styles'
|
||||||
|
|
||||||
|
export const snapPoints = ['70%']
|
||||||
|
|
||||||
|
export function Component({profileView}: {profileView: ProfileViewModel}) {
|
||||||
|
const store = useStores()
|
||||||
|
const layout = useWindowDimensions()
|
||||||
|
const [index, setIndex] = useState(0)
|
||||||
|
const tabRoutes = [
|
||||||
|
{key: 'suggestions', title: 'Suggestions'},
|
||||||
|
{key: 'pending', title: 'Pending Invites'},
|
||||||
|
]
|
||||||
|
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<string>('')
|
||||||
|
const suggestions = useMemo(
|
||||||
|
() => new SceneInviteSuggestions(store, {sceneDid: profileView.did}),
|
||||||
|
[profileView.did],
|
||||||
|
)
|
||||||
|
const [createdInvites, setCreatedInvites] = useState<Record<string, string>>(
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let aborted = false
|
||||||
|
if (hasSetup) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
suggestions.setup().then(() => {
|
||||||
|
if (aborted) return
|
||||||
|
setHasSetup(true)
|
||||||
|
})
|
||||||
|
return () => {
|
||||||
|
aborted = true
|
||||||
|
}
|
||||||
|
}, [profileView.did])
|
||||||
|
|
||||||
|
const onPressInvite = async (follow: FollowItem) => {
|
||||||
|
setError('')
|
||||||
|
try {
|
||||||
|
const assertionUri = await apilib.inviteToScene(
|
||||||
|
store,
|
||||||
|
profileView.did,
|
||||||
|
follow.did,
|
||||||
|
follow.declaration.cid,
|
||||||
|
)
|
||||||
|
setCreatedInvites({[follow.did]: assertionUri, ...createdInvites})
|
||||||
|
Toast.show('Invite sent', {
|
||||||
|
duration: Toast.durations.LONG,
|
||||||
|
position: Toast.positions.TOP,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
setError('There was an issue with the invite. Please try again.')
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onPressUndo = async (subjectDid: string, assertionUri: string) => {
|
||||||
|
setError('')
|
||||||
|
const urip = new AtUri(assertionUri)
|
||||||
|
try {
|
||||||
|
await store.api.app.bsky.graph.assertion.delete({
|
||||||
|
did: profileView.did,
|
||||||
|
rkey: urip.rkey,
|
||||||
|
})
|
||||||
|
setCreatedInvites(_omit(createdInvites, [subjectDid]))
|
||||||
|
} catch (e) {
|
||||||
|
setError('There was an issue with the invite. Please try again.')
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderSuggestionItem = ({item}: {item: FollowItem}) => {
|
||||||
|
const createdInvite = createdInvites[item.did]
|
||||||
|
return (
|
||||||
|
<ProfileCard
|
||||||
|
did={item.did}
|
||||||
|
handle={item.handle}
|
||||||
|
displayName={item.displayName}
|
||||||
|
renderButton={() =>
|
||||||
|
!createdInvite ? (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon="user-plus" style={[s.mr5]} size={14} />
|
||||||
|
<Text style={[s.fw400, s.f14]}>Invite</Text>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon="x" style={[s.mr5]} size={14} />
|
||||||
|
<Text style={[s.fw400, s.f14]}>Undo invite</Text>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onPressButton={() =>
|
||||||
|
!createdInvite
|
||||||
|
? onPressInvite(item)
|
||||||
|
: onPressUndo(item.did, createdInvite)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Suggestions = () => (
|
||||||
|
<View style={s.flex1}>
|
||||||
|
{hasSetup ? (
|
||||||
|
<View style={s.flex1}>
|
||||||
|
<View style={styles.todoContainer}>
|
||||||
|
<Text style={styles.todoLabel}>
|
||||||
|
User search is still being implemented. For now, you can pick from
|
||||||
|
your follows below.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{!suggestions.hasContent ? (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
paddingTop: 10,
|
||||||
|
paddingHorizontal: 40,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: colors.gray5,
|
||||||
|
}}>
|
||||||
|
{suggestions.myFollowsView.follows.length
|
||||||
|
? 'Sorry! You dont follow anybody for us to suggest.'
|
||||||
|
: 'Sorry! All of the users you follow are members already.'}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<FlatList
|
||||||
|
data={suggestions.suggestions}
|
||||||
|
keyExtractor={item => item._reactKey}
|
||||||
|
renderItem={renderSuggestionItem}
|
||||||
|
style={s.flex1}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
) : !error ? (
|
||||||
|
<ActivityIndicator />
|
||||||
|
) : undefined}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
const PendingInvites = () => (
|
||||||
|
<View>
|
||||||
|
<View style={styles.todoContainer}>
|
||||||
|
<Text style={styles.todoLabel}>
|
||||||
|
Pending invites are still being implemented. Check back soon!
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderScene = SceneMap({
|
||||||
|
suggestions: Suggestions,
|
||||||
|
pending: PendingInvites,
|
||||||
|
})
|
||||||
|
|
||||||
|
const renderTabBar = (props: TabBarProps<Route>) => (
|
||||||
|
<TabBar
|
||||||
|
{...props}
|
||||||
|
style={{backgroundColor: 'white'}}
|
||||||
|
activeColor="black"
|
||||||
|
inactiveColor={colors.gray5}
|
||||||
|
labelStyle={{textTransform: 'none'}}
|
||||||
|
indicatorStyle={{backgroundColor: colors.purple3}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={s.flex1}>
|
||||||
|
<Text style={styles.title}>
|
||||||
|
Invite to {profileView.displayName || profileView.handle}
|
||||||
|
</Text>
|
||||||
|
{error !== '' ? (
|
||||||
|
<View style={s.p10}>
|
||||||
|
<ErrorMessage message={error} />
|
||||||
|
</View>
|
||||||
|
) : undefined}
|
||||||
|
<TabView
|
||||||
|
navigationState={{index, routes: tabRoutes}}
|
||||||
|
renderScene={renderScene}
|
||||||
|
renderTabBar={renderTabBar}
|
||||||
|
onIndexChange={setIndex}
|
||||||
|
initialLayout={{width: layout.width}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
title: {
|
||||||
|
textAlign: 'center',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: 18,
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
todoContainer: {
|
||||||
|
backgroundColor: colors.pink1,
|
||||||
|
margin: 10,
|
||||||
|
padding: 10,
|
||||||
|
borderRadius: 6,
|
||||||
|
},
|
||||||
|
todoLabel: {
|
||||||
|
color: colors.pink5,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
|
||||||
|
tabBar: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
tabItem: {
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 16,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
})
|
|
@ -8,9 +8,11 @@ import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
|
||||||
import * as models from '../../../state/models/shell-ui'
|
import * as models from '../../../state/models/shell-ui'
|
||||||
|
|
||||||
import * as LinkActionsModal from './LinkActions'
|
import * as LinkActionsModal from './LinkActions'
|
||||||
|
import * as ConfirmModal from './Confirm'
|
||||||
import * as SharePostModal from './SharePost.native'
|
import * as SharePostModal from './SharePost.native'
|
||||||
import * as EditProfile from './EditProfile'
|
import * as EditProfileModal from './EditProfile'
|
||||||
import * as CreateScene from './CreateScene'
|
import * as CreateSceneModal from './CreateScene'
|
||||||
|
import * as InviteToSceneModal from './InviteToScene'
|
||||||
|
|
||||||
const CLOSED_SNAPPOINTS = ['10%']
|
const CLOSED_SNAPPOINTS = ['10%']
|
||||||
|
|
||||||
|
@ -44,6 +46,13 @@ export const Modal = observer(function Modal() {
|
||||||
{...(store.shell.activeModal as models.LinkActionsModel)}
|
{...(store.shell.activeModal as models.LinkActionsModel)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
} else if (store.shell.activeModal?.name === 'confirm') {
|
||||||
|
snapPoints = ConfirmModal.snapPoints
|
||||||
|
element = (
|
||||||
|
<ConfirmModal.Component
|
||||||
|
{...(store.shell.activeModal as models.ConfirmModel)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
} else if (store.shell.activeModal?.name === 'share-post') {
|
} else if (store.shell.activeModal?.name === 'share-post') {
|
||||||
snapPoints = SharePostModal.snapPoints
|
snapPoints = SharePostModal.snapPoints
|
||||||
element = (
|
element = (
|
||||||
|
@ -52,15 +61,22 @@ export const Modal = observer(function Modal() {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (store.shell.activeModal?.name === 'edit-profile') {
|
} else if (store.shell.activeModal?.name === 'edit-profile') {
|
||||||
snapPoints = EditProfile.snapPoints
|
snapPoints = EditProfileModal.snapPoints
|
||||||
element = (
|
element = (
|
||||||
<EditProfile.Component
|
<EditProfileModal.Component
|
||||||
{...(store.shell.activeModal as models.EditProfileModel)}
|
{...(store.shell.activeModal as models.EditProfileModel)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
} else if (store.shell.activeModal?.name === 'create-scene') {
|
} else if (store.shell.activeModal?.name === 'create-scene') {
|
||||||
snapPoints = CreateScene.snapPoints
|
snapPoints = CreateSceneModal.snapPoints
|
||||||
element = <CreateScene.Component />
|
element = <CreateSceneModal.Component />
|
||||||
|
} else if (store.shell.activeModal?.name === 'invite-to-scene') {
|
||||||
|
snapPoints = InviteToSceneModal.snapPoints
|
||||||
|
element = (
|
||||||
|
<InviteToSceneModal.Component
|
||||||
|
{...(store.shell.activeModal as models.InviteToSceneModel)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
element = <View />
|
element = <View />
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {useMemo} from 'react'
|
import React, {useMemo} from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {Image, StyleSheet, Text, View} from 'react-native'
|
import {StyleSheet, Text, View} from 'react-native'
|
||||||
import {AtUri} from '../../../third-party/uri'
|
import {AtUri} from '../../../third-party/uri'
|
||||||
import {FontAwesomeIcon, Props} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon, Props} from '@fortawesome/react-native-fontawesome'
|
||||||
import {NotificationsViewItemModel} from '../../../state/models/notifications-view'
|
import {NotificationsViewItemModel} from '../../../state/models/notifications-view'
|
||||||
|
@ -11,6 +11,7 @@ import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {PostText} from '../post/PostText'
|
import {PostText} from '../post/PostText'
|
||||||
import {Post} from '../post/Post'
|
import {Post} from '../post/Post'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
|
import {InviteAccepter} from './InviteAccepter'
|
||||||
|
|
||||||
const MAX_AUTHORS = 8
|
const MAX_AUTHORS = 8
|
||||||
|
|
||||||
|
@ -20,10 +21,10 @@ export const FeedItem = observer(function FeedItem({
|
||||||
item: NotificationsViewItemModel
|
item: NotificationsViewItemModel
|
||||||
}) {
|
}) {
|
||||||
const itemHref = useMemo(() => {
|
const itemHref = useMemo(() => {
|
||||||
if (item.isUpvote || item.isRepost) {
|
if (item.isUpvote || item.isRepost || item.isTrend) {
|
||||||
const urip = new AtUri(item.subjectUri)
|
const urip = new AtUri(item.subjectUri)
|
||||||
return `/profile/${urip.host}/post/${urip.rkey}`
|
return `/profile/${urip.host}/post/${urip.rkey}`
|
||||||
} else if (item.isFollow) {
|
} else if (item.isFollow || item.isAssertion) {
|
||||||
return `/profile/${item.author.handle}`
|
return `/profile/${item.author.handle}`
|
||||||
} else if (item.isReply) {
|
} else if (item.isReply) {
|
||||||
const urip = new AtUri(item.uri)
|
const urip = new AtUri(item.uri)
|
||||||
|
@ -34,7 +35,7 @@ export const FeedItem = observer(function FeedItem({
|
||||||
const itemTitle = useMemo(() => {
|
const itemTitle = useMemo(() => {
|
||||||
if (item.isUpvote || item.isRepost) {
|
if (item.isUpvote || item.isRepost) {
|
||||||
return 'Post'
|
return 'Post'
|
||||||
} else if (item.isFollow) {
|
} else if (item.isFollow || item.isAssertion) {
|
||||||
return item.author.handle
|
return item.author.handle
|
||||||
} else if (item.isReply) {
|
} else if (item.isReply) {
|
||||||
return 'Post'
|
return 'Post'
|
||||||
|
@ -66,6 +67,10 @@ export const FeedItem = observer(function FeedItem({
|
||||||
action = 'reposted your post'
|
action = 'reposted your post'
|
||||||
icon = 'retweet'
|
icon = 'retweet'
|
||||||
iconStyle = [s.green3]
|
iconStyle = [s.green3]
|
||||||
|
} else if (item.isTrend) {
|
||||||
|
action = 'Your post is trending with'
|
||||||
|
icon = 'arrow-trend-up'
|
||||||
|
iconStyle = [s.blue3]
|
||||||
} else if (item.isReply) {
|
} else if (item.isReply) {
|
||||||
action = 'replied to your post'
|
action = 'replied to your post'
|
||||||
icon = ['far', 'comment']
|
icon = ['far', 'comment']
|
||||||
|
@ -73,6 +78,10 @@ export const FeedItem = observer(function FeedItem({
|
||||||
action = 'followed you'
|
action = 'followed you'
|
||||||
icon = 'user-plus'
|
icon = 'user-plus'
|
||||||
iconStyle = [s.blue3]
|
iconStyle = [s.blue3]
|
||||||
|
} else if (item.isInvite) {
|
||||||
|
icon = 'users'
|
||||||
|
iconStyle = [s.blue3]
|
||||||
|
action = 'invited you to join their scene'
|
||||||
} else {
|
} else {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
@ -133,6 +142,9 @@ export const FeedItem = observer(function FeedItem({
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.meta}>
|
<View style={styles.meta}>
|
||||||
|
{item.isTrend && (
|
||||||
|
<Text style={[styles.metaItem, s.f15]}>{action}</Text>
|
||||||
|
)}
|
||||||
<Link
|
<Link
|
||||||
key={authors[0].href}
|
key={authors[0].href}
|
||||||
style={styles.metaItem}
|
style={styles.metaItem}
|
||||||
|
@ -150,7 +162,9 @@ export const FeedItem = observer(function FeedItem({
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
<Text style={[styles.metaItem, s.f15]}>{action}</Text>
|
{!item.isTrend && (
|
||||||
|
<Text style={[styles.metaItem, s.f15]}>{action}</Text>
|
||||||
|
)}
|
||||||
<Text style={[styles.metaItem, s.f15, s.gray5]}>
|
<Text style={[styles.metaItem, s.f15, s.gray5]}>
|
||||||
{ago(item.indexedAt)}
|
{ago(item.indexedAt)}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -162,6 +176,11 @@ export const FeedItem = observer(function FeedItem({
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
{item.isInvite && (
|
||||||
|
<View style={styles.addedContainer}>
|
||||||
|
<InviteAccepter item={item} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
{item.isReply ? (
|
{item.isReply ? (
|
||||||
<View style={s.pt5}>
|
<View style={s.pt5}>
|
||||||
<Post uri={item.uri} />
|
<Post uri={item.uri} />
|
||||||
|
@ -216,6 +235,7 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
paddingTop: 6,
|
paddingTop: 6,
|
||||||
paddingBottom: 2,
|
paddingBottom: 2,
|
||||||
},
|
},
|
||||||
|
@ -225,4 +245,9 @@ const styles = StyleSheet.create({
|
||||||
postText: {
|
postText: {
|
||||||
paddingBottom: 5,
|
paddingBottom: 5,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addedContainer: {
|
||||||
|
paddingTop: 4,
|
||||||
|
paddingLeft: 36,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import React, {useState} from 'react'
|
||||||
|
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||||
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import * as apilib from '../../../state/lib/api'
|
||||||
|
import {NotificationsViewItemModel} from '../../../state/models/notifications-view'
|
||||||
|
import {ConfirmModel} from '../../../state/models/shell-ui'
|
||||||
|
import {useStores} from '../../../state'
|
||||||
|
import {ProfileCard} from '../profile/ProfileCard'
|
||||||
|
import Toast from '../util/Toast'
|
||||||
|
import {s, colors, gradients} from '../../lib/styles'
|
||||||
|
|
||||||
|
export function InviteAccepter({item}: {item: NotificationsViewItemModel}) {
|
||||||
|
const store = useStores()
|
||||||
|
const [confirmationUri, setConfirmationUri] = useState<string>('')
|
||||||
|
const isMember =
|
||||||
|
confirmationUri !== '' || store.me.memberships?.isMemberOf(item.author.did)
|
||||||
|
const onPressAccept = async () => {
|
||||||
|
store.shell.openModal(
|
||||||
|
new ConfirmModel(
|
||||||
|
'Join this scene?',
|
||||||
|
() => (
|
||||||
|
<View>
|
||||||
|
<View style={styles.profileCardContainer}>
|
||||||
|
<ProfileCard
|
||||||
|
did={item.author.did}
|
||||||
|
handle={item.author.handle}
|
||||||
|
displayName={item.author.displayName}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
),
|
||||||
|
onPressConfirmAccept,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const onPressConfirmAccept = async () => {
|
||||||
|
const uri = await apilib.acceptSceneInvite(store, {
|
||||||
|
originator: {
|
||||||
|
did: item.author.did,
|
||||||
|
declarationCid: item.author.declaration.cid,
|
||||||
|
},
|
||||||
|
assertion: {
|
||||||
|
uri: item.uri,
|
||||||
|
cid: item.cid,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
store.me.refreshMemberships()
|
||||||
|
Toast.show('Invite accepted', {
|
||||||
|
duration: Toast.durations.LONG,
|
||||||
|
position: Toast.positions.TOP,
|
||||||
|
})
|
||||||
|
setConfirmationUri(uri)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{!isMember ? (
|
||||||
|
<TouchableOpacity onPress={onPressAccept}>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[gradients.primary.start, gradients.primary.end]}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 1, y: 1}}
|
||||||
|
style={[styles.btn]}>
|
||||||
|
<Text style={[s.white, s.bold, s.f16]}>Accept Invite</Text>
|
||||||
|
</LinearGradient>
|
||||||
|
</TouchableOpacity>
|
||||||
|
) : (
|
||||||
|
<View style={styles.inviteAccepted}>
|
||||||
|
<FontAwesomeIcon icon="check" size={14} style={s.mr5} />
|
||||||
|
<Text style={[s.gray5, s.f15]}>Invite accepted</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
btn: {
|
||||||
|
borderRadius: 32,
|
||||||
|
paddingHorizontal: 18,
|
||||||
|
paddingVertical: 8,
|
||||||
|
backgroundColor: colors.gray1,
|
||||||
|
},
|
||||||
|
profileCardContainer: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.gray3,
|
||||||
|
borderRadius: 6,
|
||||||
|
},
|
||||||
|
inviteAccepted: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, Text, View} from 'react-native'
|
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
@ -9,11 +9,15 @@ export function ProfileCard({
|
||||||
handle,
|
handle,
|
||||||
displayName,
|
displayName,
|
||||||
description,
|
description,
|
||||||
|
renderButton,
|
||||||
|
onPressButton,
|
||||||
}: {
|
}: {
|
||||||
did: string
|
did: string
|
||||||
handle: string
|
handle: string
|
||||||
displayName?: string
|
displayName?: string
|
||||||
description?: string
|
description?: string
|
||||||
|
renderButton?: () => JSX.Element
|
||||||
|
onPressButton?: () => void
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Link style={styles.outer} href={`/profile/${handle}`} title={handle}>
|
<Link style={styles.outer} href={`/profile/${handle}`} title={handle}>
|
||||||
|
@ -22,9 +26,20 @@ export function ProfileCard({
|
||||||
<UserAvatar size={40} displayName={displayName} handle={handle} />
|
<UserAvatar size={40} displayName={displayName} handle={handle} />
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.layoutContent}>
|
<View style={styles.layoutContent}>
|
||||||
<Text style={[s.f16, s.bold]}>{displayName || handle}</Text>
|
<Text style={[s.f16, s.bold]} numberOfLines={1}>
|
||||||
<Text style={[s.f15, s.gray5]}>@{handle}</Text>
|
{displayName || handle}
|
||||||
|
</Text>
|
||||||
|
<Text style={[s.f15, s.gray5]} numberOfLines={1}>
|
||||||
|
@{handle}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
{renderButton ? (
|
||||||
|
<View style={styles.layoutButton}>
|
||||||
|
<TouchableOpacity onPress={onPressButton} style={styles.btn}>
|
||||||
|
{renderButton()}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
|
@ -34,9 +49,11 @@ const styles = StyleSheet.create({
|
||||||
outer: {
|
outer: {
|
||||||
marginTop: 1,
|
marginTop: 1,
|
||||||
backgroundColor: colors.white,
|
backgroundColor: colors.white,
|
||||||
|
borderRadius: 6,
|
||||||
},
|
},
|
||||||
layout: {
|
layout: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
layoutAvi: {
|
layoutAvi: {
|
||||||
width: 60,
|
width: 60,
|
||||||
|
@ -56,4 +73,17 @@ const styles = StyleSheet.create({
|
||||||
paddingTop: 12,
|
paddingTop: 12,
|
||||||
paddingBottom: 10,
|
paddingBottom: 10,
|
||||||
},
|
},
|
||||||
|
layoutButton: {
|
||||||
|
paddingRight: 10,
|
||||||
|
},
|
||||||
|
btn: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingVertical: 7,
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
borderRadius: 50,
|
||||||
|
backgroundColor: colors.gray1,
|
||||||
|
marginLeft: 6,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react'
|
import React, {useMemo} from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
|
@ -9,12 +9,18 @@ import {
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
|
import {AtUri} from '../../../third-party/uri'
|
||||||
import {ProfileViewModel} from '../../../state/models/profile-view'
|
import {ProfileViewModel} from '../../../state/models/profile-view'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {EditProfileModel} from '../../../state/models/shell-ui'
|
import {
|
||||||
|
ConfirmModel,
|
||||||
|
EditProfileModel,
|
||||||
|
InviteToSceneModel,
|
||||||
|
} from '../../../state/models/shell-ui'
|
||||||
import {pluralize} from '../../lib/strings'
|
import {pluralize} from '../../lib/strings'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {getGradient} from '../../lib/asset-gen'
|
import {getGradient} from '../../lib/asset-gen'
|
||||||
|
import {DropdownBtn, DropdownItem} from '../util/DropdownBtn'
|
||||||
import Toast from '../util/Toast'
|
import Toast from '../util/Toast'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {UserBanner} from '../util/UserBanner'
|
import {UserBanner} from '../util/UserBanner'
|
||||||
|
@ -22,10 +28,16 @@ import {UserInfoText} from '../util/UserInfoText'
|
||||||
|
|
||||||
export const ProfileHeader = observer(function ProfileHeader({
|
export const ProfileHeader = observer(function ProfileHeader({
|
||||||
view,
|
view,
|
||||||
|
onRefreshAll,
|
||||||
}: {
|
}: {
|
||||||
view: ProfileViewModel
|
view: ProfileViewModel
|
||||||
|
onRefreshAll: () => void
|
||||||
}) {
|
}) {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
const isMember = useMemo(
|
||||||
|
() => view.isScene && view.myState.member,
|
||||||
|
[view.myState.member],
|
||||||
|
)
|
||||||
|
|
||||||
const onPressBack = () => {
|
const onPressBack = () => {
|
||||||
store.nav.tab.goBack()
|
store.nav.tab.goBack()
|
||||||
|
@ -49,9 +61,6 @@ export const ProfileHeader = observer(function ProfileHeader({
|
||||||
const onPressEditProfile = () => {
|
const onPressEditProfile = () => {
|
||||||
store.shell.openModal(new EditProfileModel(view))
|
store.shell.openModal(new EditProfileModel(view))
|
||||||
}
|
}
|
||||||
const onPressMenu = () => {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
const onPressFollowers = () => {
|
const onPressFollowers = () => {
|
||||||
store.nav.navigate(`/profile/${view.handle}/followers`)
|
store.nav.navigate(`/profile/${view.handle}/followers`)
|
||||||
}
|
}
|
||||||
|
@ -61,6 +70,31 @@ export const ProfileHeader = observer(function ProfileHeader({
|
||||||
const onPressMembers = () => {
|
const onPressMembers = () => {
|
||||||
store.nav.navigate(`/profile/${view.handle}/members`)
|
store.nav.navigate(`/profile/${view.handle}/members`)
|
||||||
}
|
}
|
||||||
|
const onPressInviteMembers = () => {
|
||||||
|
store.shell.openModal(new InviteToSceneModel(view))
|
||||||
|
}
|
||||||
|
const onPressLeaveScene = () => {
|
||||||
|
store.shell.openModal(
|
||||||
|
new ConfirmModel(
|
||||||
|
'Leave this scene?',
|
||||||
|
`You'll be able to come back unless your invite is revoked.`,
|
||||||
|
onPressConfirmLeaveScene,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const onPressConfirmLeaveScene = async () => {
|
||||||
|
if (view.myState.member) {
|
||||||
|
await store.api.app.bsky.graph.confirmation.delete({
|
||||||
|
did: store.me.did || '',
|
||||||
|
rkey: new AtUri(view.myState.member).rkey,
|
||||||
|
})
|
||||||
|
Toast.show(`Scene left`, {
|
||||||
|
duration: Toast.durations.LONG,
|
||||||
|
position: Toast.positions.TOP,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onRefreshAll()
|
||||||
|
}
|
||||||
|
|
||||||
// loading
|
// loading
|
||||||
// =
|
// =
|
||||||
|
@ -86,6 +120,23 @@ export const ProfileHeader = observer(function ProfileHeader({
|
||||||
// =
|
// =
|
||||||
const gradient = getGradient(view.handle)
|
const gradient = getGradient(view.handle)
|
||||||
const isMe = store.me.did === view.did
|
const isMe = store.me.did === view.did
|
||||||
|
const isCreator = view.isScene && view.creator === store.me.did
|
||||||
|
let dropdownItems: DropdownItem[] | undefined
|
||||||
|
if (isCreator || isMember) {
|
||||||
|
dropdownItems = []
|
||||||
|
if (isCreator) {
|
||||||
|
dropdownItems.push({
|
||||||
|
label: 'Edit Profile',
|
||||||
|
onPress: () => {}, // TODO
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (isMember) {
|
||||||
|
dropdownItems.push({
|
||||||
|
label: 'Leave Scene...',
|
||||||
|
onPress: onPressLeaveScene,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<View style={styles.outer}>
|
<View style={styles.outer}>
|
||||||
<UserBanner handle={view.handle} />
|
<UserBanner handle={view.handle} />
|
||||||
|
@ -136,11 +187,14 @@ export const ProfileHeader = observer(function ProfileHeader({
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<TouchableOpacity
|
{view.isScene &&
|
||||||
onPress={onPressMenu}
|
(view.myState.member || view.creator === store.me.did) ? (
|
||||||
style={[styles.btn, styles.secondaryBtn]}>
|
<DropdownBtn
|
||||||
<FontAwesomeIcon icon="ellipsis" style={[s.gray5]} />
|
items={dropdownItems}
|
||||||
</TouchableOpacity>
|
style={[styles.btn, styles.secondaryBtn]}>
|
||||||
|
<FontAwesomeIcon icon="ellipsis" style={[s.gray5]} />
|
||||||
|
</DropdownBtn>
|
||||||
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.displayNameLine}>
|
<View style={styles.displayNameLine}>
|
||||||
<Text style={styles.displayName}>
|
<Text style={styles.displayName}>
|
||||||
|
@ -224,6 +278,24 @@ export const ProfileHeader = observer(function ProfileHeader({
|
||||||
</View>
|
</View>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
|
{view.isScene && view.creator === store.me.did ? (
|
||||||
|
<View style={styles.sceneAdminContainer}>
|
||||||
|
<TouchableOpacity onPress={onPressInviteMembers}>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[gradient[1], gradient[0]]}
|
||||||
|
start={{x: 0, y: 0}}
|
||||||
|
end={{x: 1, y: 1}}
|
||||||
|
style={[styles.btn, styles.gradientBtn, styles.sceneAdminBtn]}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="user-plus"
|
||||||
|
style={[s.mr5, s.white]}
|
||||||
|
size={15}
|
||||||
|
/>
|
||||||
|
<Text style={[s.bold, s.f15, s.white]}>Invite Members</Text>
|
||||||
|
</LinearGradient>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -340,4 +412,15 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 5,
|
marginBottom: 5,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sceneAdminContainer: {
|
||||||
|
borderColor: colors.gray1,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
paddingVertical: 12,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
},
|
||||||
|
sceneAdminBtn: {
|
||||||
|
paddingVertical: 8,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,7 +16,10 @@ export const Link = observer(function Link({
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const onPress = () => store.nav.navigate(href)
|
const onPress = () => {
|
||||||
|
store.shell.closeModal() // close any active modals
|
||||||
|
store.nav.navigate(href)
|
||||||
|
}
|
||||||
const onLongPress = () => {
|
const onLongPress = () => {
|
||||||
store.shell.openModal(new LinkActionsModel(href, title || href))
|
store.shell.openModal(new LinkActionsModel(href, title || href))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {faArrowRightFromBracket} from '@fortawesome/free-solid-svg-icons'
|
||||||
import {faArrowUpFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowUpFromBracket'
|
import {faArrowUpFromBracket} from '@fortawesome/free-solid-svg-icons/faArrowUpFromBracket'
|
||||||
import {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare'
|
import {faArrowUpRightFromSquare} from '@fortawesome/free-solid-svg-icons/faArrowUpRightFromSquare'
|
||||||
import {faArrowsRotate} from '@fortawesome/free-solid-svg-icons/faArrowsRotate'
|
import {faArrowsRotate} from '@fortawesome/free-solid-svg-icons/faArrowsRotate'
|
||||||
|
import {faArrowTrendUp} from '@fortawesome/free-solid-svg-icons/faArrowTrendUp'
|
||||||
import {faAt} from '@fortawesome/free-solid-svg-icons/faAt'
|
import {faAt} from '@fortawesome/free-solid-svg-icons/faAt'
|
||||||
import {faBars} from '@fortawesome/free-solid-svg-icons/faBars'
|
import {faBars} from '@fortawesome/free-solid-svg-icons/faBars'
|
||||||
import {faBell} from '@fortawesome/free-solid-svg-icons/faBell'
|
import {faBell} from '@fortawesome/free-solid-svg-icons/faBell'
|
||||||
|
@ -47,6 +48,7 @@ import {faUser} from '@fortawesome/free-regular-svg-icons/faUser'
|
||||||
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'
|
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'
|
||||||
import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck'
|
import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck'
|
||||||
import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus'
|
import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus'
|
||||||
|
import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark'
|
||||||
import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket'
|
import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket'
|
||||||
import {faX} from '@fortawesome/free-solid-svg-icons/faX'
|
import {faX} from '@fortawesome/free-solid-svg-icons/faX'
|
||||||
|
|
||||||
|
@ -61,6 +63,7 @@ export function setup() {
|
||||||
faArrowUpFromBracket,
|
faArrowUpFromBracket,
|
||||||
faArrowUpRightFromSquare,
|
faArrowUpRightFromSquare,
|
||||||
faArrowsRotate,
|
faArrowsRotate,
|
||||||
|
faArrowTrendUp,
|
||||||
faAt,
|
faAt,
|
||||||
faBars,
|
faBars,
|
||||||
faBell,
|
faBell,
|
||||||
|
@ -99,6 +102,7 @@ export function setup() {
|
||||||
faUsers,
|
faUsers,
|
||||||
faUserCheck,
|
faUserCheck,
|
||||||
faUserPlus,
|
faUserPlus,
|
||||||
|
faUserXmark,
|
||||||
faTicket,
|
faTicket,
|
||||||
faX,
|
faX,
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,6 +18,7 @@ export const Notifications = ({visible}: ScreenParams) => {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
store.me.refreshMemberships() // needed for the invite notifications
|
||||||
if (hasSetup) {
|
if (hasSetup) {
|
||||||
console.log('Updating notifications feed')
|
console.log('Updating notifications feed')
|
||||||
notesView?.update()
|
notesView?.update()
|
||||||
|
|
|
@ -73,7 +73,7 @@ export const Profile = observer(({visible, params}: ScreenParams) => {
|
||||||
if (!uiState) {
|
if (!uiState) {
|
||||||
return <View />
|
return <View />
|
||||||
}
|
}
|
||||||
return <ProfileHeader view={uiState.profile} />
|
return <ProfileHeader view={uiState.profile} onRefreshAll={onRefresh} />
|
||||||
}
|
}
|
||||||
let renderItem
|
let renderItem
|
||||||
let items: any[] = []
|
let items: any[] = []
|
||||||
|
|
19
todos.txt
19
todos.txt
|
@ -6,24 +6,22 @@ Paul's todo list
|
||||||
- Cursor behaviors on all views
|
- Cursor behaviors on all views
|
||||||
- Update swipe behaviors: edge always goes back, leftmost always goes back, main connects to selector if present
|
- Update swipe behaviors: edge always goes back, leftmost always goes back, main connects to selector if present
|
||||||
- Onboarding flow
|
- Onboarding flow
|
||||||
- *
|
- Confirm email
|
||||||
- Avatars
|
- Setup rpfoile?
|
||||||
- SVG generate
|
|
||||||
- Create scene
|
- Create scene
|
||||||
- Set profile during creation
|
- Set profile during creation
|
||||||
- Discover scenes view
|
- Discover scenes view
|
||||||
- *
|
- *
|
||||||
|
- Invite to scene
|
||||||
|
- User search
|
||||||
|
- Filter out scenes from suggestions
|
||||||
|
- Filter out unconfirmed invites from suggestions
|
||||||
|
- Use pagination to make sure there are suggestions
|
||||||
|
- Unconfirmed invites
|
||||||
- User profile
|
- User profile
|
||||||
- User
|
|
||||||
- Invite to scene
|
|
||||||
- Remove from scene
|
|
||||||
- Scene
|
- Scene
|
||||||
- Trending
|
- Trending
|
||||||
- Invite to scene
|
|
||||||
- Remove from scene
|
|
||||||
- Edit profile
|
- Edit profile
|
||||||
- Notifications
|
|
||||||
- Scene invite
|
|
||||||
- Reply gating
|
- Reply gating
|
||||||
- Composer
|
- Composer
|
||||||
- View on post
|
- View on post
|
||||||
|
@ -37,6 +35,7 @@ Paul's todo list
|
||||||
- Follows list
|
- Follows list
|
||||||
- Members list
|
- Members list
|
||||||
- Bugs
|
- Bugs
|
||||||
|
- Create account broken
|
||||||
- Follows are broken
|
- Follows are broken
|
||||||
- Auth token refresh seems broken
|
- Auth token refresh seems broken
|
||||||
- Check that sub components arent reloading too much
|
- Check that sub components arent reloading too much
|
||||||
|
|
Loading…
Reference in New Issue