Implement like and repost
This commit is contained in:
parent
cc8a170204
commit
0ec0ba996f
12 changed files with 307 additions and 40 deletions
|
@ -1,6 +1,17 @@
|
|||
import {makeAutoObservable} from 'mobx'
|
||||
import {makeAutoObservable, runInAction} from 'mobx'
|
||||
import {bsky} from '@adxp/mock-api'
|
||||
import _omit from 'lodash.omit'
|
||||
import {RootStoreModel} from './root-store'
|
||||
import * as apilib from '../lib/api'
|
||||
|
||||
export class FeedViewItemMyStateModel {
|
||||
hasLiked: boolean = false
|
||||
hasReposted: boolean = false
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
}
|
||||
|
||||
export class FeedViewItemModel implements bsky.FeedView.FeedItem {
|
||||
// ui state
|
||||
|
@ -19,11 +30,51 @@ export class FeedViewItemModel implements bsky.FeedView.FeedItem {
|
|||
repostCount: number = 0
|
||||
likeCount: number = 0
|
||||
indexedAt: string = ''
|
||||
myState = new FeedViewItemMyStateModel()
|
||||
|
||||
constructor(reactKey: string, v: bsky.FeedView.FeedItem) {
|
||||
makeAutoObservable(this)
|
||||
constructor(
|
||||
public rootStore: RootStoreModel,
|
||||
reactKey: string,
|
||||
v: bsky.FeedView.FeedItem,
|
||||
) {
|
||||
makeAutoObservable(this, {rootStore: false})
|
||||
this._reactKey = reactKey
|
||||
Object.assign(this, v)
|
||||
Object.assign(this, _omit(v, 'myState'))
|
||||
if (v.myState) {
|
||||
Object.assign(this.myState, v.myState)
|
||||
}
|
||||
}
|
||||
|
||||
async toggleLike() {
|
||||
if (this.myState.hasLiked) {
|
||||
await apilib.unlike(this.rootStore.api, 'alice.com', this.uri)
|
||||
runInAction(() => {
|
||||
this.likeCount--
|
||||
this.myState.hasLiked = false
|
||||
})
|
||||
} else {
|
||||
await apilib.like(this.rootStore.api, 'alice.com', this.uri)
|
||||
runInAction(() => {
|
||||
this.likeCount++
|
||||
this.myState.hasLiked = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async toggleRepost() {
|
||||
if (this.myState.hasReposted) {
|
||||
await apilib.unrepost(this.rootStore.api, 'alice.com', this.uri)
|
||||
runInAction(() => {
|
||||
this.repostCount--
|
||||
this.myState.hasReposted = false
|
||||
})
|
||||
} else {
|
||||
await apilib.repost(this.rootStore.api, 'alice.com', this.uri)
|
||||
runInAction(() => {
|
||||
this.repostCount++
|
||||
this.myState.hasReposted = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,6 +228,6 @@ export class FeedViewModel implements bsky.FeedView.Response {
|
|||
|
||||
private _append(keyId: number, item: bsky.FeedView.FeedItem) {
|
||||
// TODO: validate .record
|
||||
this.feed.push(new FeedViewItemModel(`item-${keyId}`, item))
|
||||
this.feed.push(new FeedViewItemModel(this.rootStore, `item-${keyId}`, item))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import {makeAutoObservable, runInAction} from 'mobx'
|
|||
import {bsky, AdxUri} from '@adxp/mock-api'
|
||||
import _omit from 'lodash.omit'
|
||||
import {RootStoreModel} from './root-store'
|
||||
import * as apilib from '../lib/api'
|
||||
|
||||
function* reactKeyGenerator(): Generator<string> {
|
||||
let counter = 0
|
||||
|
@ -10,6 +11,15 @@ function* reactKeyGenerator(): Generator<string> {
|
|||
}
|
||||
}
|
||||
|
||||
export class PostThreadViewPostMyStateModel {
|
||||
hasLiked: boolean = false
|
||||
hasReposted: boolean = false
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
}
|
||||
|
||||
export class PostThreadViewPostModel implements bsky.PostThreadView.Post {
|
||||
// ui state
|
||||
_reactKey: string = ''
|
||||
|
@ -30,12 +40,20 @@ export class PostThreadViewPostModel implements bsky.PostThreadView.Post {
|
|||
repostCount: number = 0
|
||||
likeCount: number = 0
|
||||
indexedAt: string = ''
|
||||
myState = new PostThreadViewPostMyStateModel()
|
||||
|
||||
constructor(reactKey: string, v?: bsky.PostThreadView.Post) {
|
||||
makeAutoObservable(this)
|
||||
constructor(
|
||||
public rootStore: RootStoreModel,
|
||||
reactKey: string,
|
||||
v?: bsky.PostThreadView.Post,
|
||||
) {
|
||||
makeAutoObservable(this, {rootStore: false})
|
||||
this._reactKey = reactKey
|
||||
if (v) {
|
||||
Object.assign(this, _omit(v, 'parent', 'replies')) // copy everything but the replies and the parent
|
||||
Object.assign(this, _omit(v, 'parent', 'replies', 'myState')) // replies and parent are handled via assignTreeModels
|
||||
if (v.myState) {
|
||||
Object.assign(this.myState, v.myState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +62,7 @@ export class PostThreadViewPostModel implements bsky.PostThreadView.Post {
|
|||
if (v.parent) {
|
||||
// TODO: validate .record
|
||||
const parentModel = new PostThreadViewPostModel(
|
||||
this.rootStore,
|
||||
keyGen.next().value,
|
||||
v.parent,
|
||||
)
|
||||
|
@ -58,7 +77,11 @@ export class PostThreadViewPostModel implements bsky.PostThreadView.Post {
|
|||
const replies = []
|
||||
for (const item of v.replies) {
|
||||
// TODO: validate .record
|
||||
const itemModel = new PostThreadViewPostModel(keyGen.next().value, item)
|
||||
const itemModel = new PostThreadViewPostModel(
|
||||
this.rootStore,
|
||||
keyGen.next().value,
|
||||
item,
|
||||
)
|
||||
itemModel._depth = this._depth + 1
|
||||
if (item.replies) {
|
||||
itemModel.assignTreeModels(keyGen, item)
|
||||
|
@ -68,10 +91,41 @@ export class PostThreadViewPostModel implements bsky.PostThreadView.Post {
|
|||
this.replies = replies
|
||||
}
|
||||
}
|
||||
}
|
||||
const UNLOADED_THREAD = new PostThreadViewPostModel('')
|
||||
|
||||
export class PostThreadViewModel implements bsky.PostThreadView.Response {
|
||||
async toggleLike() {
|
||||
if (this.myState.hasLiked) {
|
||||
await apilib.unlike(this.rootStore.api, 'alice.com', this.uri)
|
||||
runInAction(() => {
|
||||
this.likeCount--
|
||||
this.myState.hasLiked = false
|
||||
})
|
||||
} else {
|
||||
await apilib.like(this.rootStore.api, 'alice.com', this.uri)
|
||||
runInAction(() => {
|
||||
this.likeCount++
|
||||
this.myState.hasLiked = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async toggleRepost() {
|
||||
if (this.myState.hasReposted) {
|
||||
await apilib.unrepost(this.rootStore.api, 'alice.com', this.uri)
|
||||
runInAction(() => {
|
||||
this.repostCount--
|
||||
this.myState.hasReposted = false
|
||||
})
|
||||
} else {
|
||||
await apilib.repost(this.rootStore.api, 'alice.com', this.uri)
|
||||
runInAction(() => {
|
||||
this.repostCount++
|
||||
this.myState.hasReposted = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PostThreadViewModel {
|
||||
// state
|
||||
isLoading = false
|
||||
isRefreshing = false
|
||||
|
@ -81,7 +135,7 @@ export class PostThreadViewModel implements bsky.PostThreadView.Response {
|
|||
params: bsky.PostThreadView.Params
|
||||
|
||||
// data
|
||||
thread: PostThreadViewPostModel = UNLOADED_THREAD
|
||||
thread?: PostThreadViewPostModel
|
||||
|
||||
constructor(
|
||||
public rootStore: RootStoreModel,
|
||||
|
@ -99,7 +153,7 @@ export class PostThreadViewModel implements bsky.PostThreadView.Response {
|
|||
}
|
||||
|
||||
get hasContent() {
|
||||
return this.thread !== UNLOADED_THREAD
|
||||
return typeof this.thread !== 'undefined'
|
||||
}
|
||||
|
||||
get hasError() {
|
||||
|
@ -177,7 +231,11 @@ export class PostThreadViewModel implements bsky.PostThreadView.Response {
|
|||
private _replaceAll(res: bsky.PostThreadView.Response) {
|
||||
// TODO: validate .record
|
||||
const keyGen = reactKeyGenerator()
|
||||
const thread = new PostThreadViewPostModel(keyGen.next().value, res.thread)
|
||||
const thread = new PostThreadViewPostModel(
|
||||
this.rootStore,
|
||||
keyGen.next().value,
|
||||
res.thread,
|
||||
)
|
||||
thread._isHighlightedPost = true
|
||||
thread.assignTreeModels(keyGen, res.thread)
|
||||
this.thread = thread
|
||||
|
|
|
@ -2,6 +2,14 @@ import {makeAutoObservable} from 'mobx'
|
|||
import {bsky} from '@adxp/mock-api'
|
||||
import {RootStoreModel} from './root-store'
|
||||
|
||||
export class ProfileViewMyStateModel {
|
||||
hasFollowed: boolean = false
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
}
|
||||
|
||||
export class ProfileViewModel implements bsky.ProfileView.Response {
|
||||
// state
|
||||
isLoading = false
|
||||
|
@ -19,6 +27,7 @@ export class ProfileViewModel implements bsky.ProfileView.Response {
|
|||
followsCount: number = 0
|
||||
postsCount: number = 0
|
||||
badges: bsky.ProfileView.Badge[] = []
|
||||
myState = new ProfileViewMyStateModel()
|
||||
|
||||
constructor(
|
||||
public rootStore: RootStoreModel,
|
||||
|
@ -101,5 +110,8 @@ export class ProfileViewModel implements bsky.ProfileView.Response {
|
|||
this.followsCount = res.followsCount
|
||||
this.postsCount = res.postsCount
|
||||
this.badges = res.badges
|
||||
if (res.myState) {
|
||||
Object.assign(this.myState, res.myState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue