Finish the upvote/downvote implementation
This commit is contained in:
parent
e650d98924
commit
1fbc4cf1f2
32 changed files with 1207 additions and 587 deletions
|
@ -50,21 +50,45 @@ export async function post(
|
|||
)
|
||||
}
|
||||
|
||||
export async function like(store: RootStoreModel, uri: string, cid: string) {
|
||||
return await store.api.app.bsky.feed.like.create(
|
||||
export async function upvote(store: RootStoreModel, uri: string, cid: string) {
|
||||
return await store.api.app.bsky.feed.vote.create(
|
||||
{did: store.me.did || ''},
|
||||
{
|
||||
subject: {uri, cid},
|
||||
direction: 'up',
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export async function unlike(store: RootStoreModel, likeUri: string) {
|
||||
const likeUrip = new AtUri(likeUri)
|
||||
return await store.api.app.bsky.feed.like.delete({
|
||||
did: likeUrip.hostname,
|
||||
rkey: likeUrip.rkey,
|
||||
export async function unupvote(store: RootStoreModel, upvoteUri: string) {
|
||||
const urip = new AtUri(upvoteUri)
|
||||
return await store.api.app.bsky.feed.vote.delete({
|
||||
did: urip.hostname,
|
||||
rkey: urip.rkey,
|
||||
})
|
||||
}
|
||||
|
||||
export async function downvote(
|
||||
store: RootStoreModel,
|
||||
uri: string,
|
||||
cid: string,
|
||||
) {
|
||||
return await store.api.app.bsky.feed.vote.create(
|
||||
{did: store.me.did || ''},
|
||||
{
|
||||
subject: {uri, cid},
|
||||
direction: 'down',
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export async function undownvote(store: RootStoreModel, downvoteUri: string) {
|
||||
const urip = new AtUri(downvoteUri)
|
||||
return await store.api.app.bsky.feed.vote.delete({
|
||||
did: urip.hostname,
|
||||
rkey: urip.rkey,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ import * as apilib from '../lib/api'
|
|||
|
||||
export class FeedItemMyStateModel {
|
||||
repost?: string
|
||||
like?: string
|
||||
upvote?: string
|
||||
downvote?: string
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this)
|
||||
|
@ -29,7 +30,8 @@ export class FeedItemModel implements GetTimeline.FeedItem {
|
|||
| GetTimeline.UnknownEmbed
|
||||
replyCount: number = 0
|
||||
repostCount: number = 0
|
||||
likeCount: number = 0
|
||||
upvoteCount: number = 0
|
||||
downvoteCount: number = 0
|
||||
indexedAt: string = ''
|
||||
myState = new FeedItemMyStateModel()
|
||||
|
||||
|
@ -52,26 +54,53 @@ export class FeedItemModel implements GetTimeline.FeedItem {
|
|||
this.embed = v.embed
|
||||
this.replyCount = v.replyCount
|
||||
this.repostCount = v.repostCount
|
||||
this.likeCount = v.likeCount
|
||||
this.upvoteCount = v.upvoteCount
|
||||
this.downvoteCount = v.downvoteCount
|
||||
this.indexedAt = v.indexedAt
|
||||
if (v.myState) {
|
||||
this.myState.like = v.myState.like
|
||||
this.myState.upvote = v.myState.upvote
|
||||
this.myState.downvote = v.myState.downvote
|
||||
this.myState.repost = v.myState.repost
|
||||
}
|
||||
}
|
||||
|
||||
async toggleLike() {
|
||||
if (this.myState.like) {
|
||||
await apilib.unlike(this.rootStore, this.myState.like)
|
||||
async _clearVotes() {
|
||||
if (this.myState.upvote) {
|
||||
await apilib.unupvote(this.rootStore, this.myState.upvote)
|
||||
runInAction(() => {
|
||||
this.likeCount--
|
||||
this.myState.like = undefined
|
||||
this.upvoteCount--
|
||||
this.myState.upvote = undefined
|
||||
})
|
||||
} else {
|
||||
const res = await apilib.like(this.rootStore, this.uri, this.cid)
|
||||
}
|
||||
if (this.myState.downvote) {
|
||||
await apilib.undownvote(this.rootStore, this.myState.downvote)
|
||||
runInAction(() => {
|
||||
this.likeCount++
|
||||
this.myState.like = res.uri
|
||||
this.downvoteCount--
|
||||
this.myState.downvote = undefined
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async toggleUpvote() {
|
||||
const wasntUpvoted = !this.myState.upvote
|
||||
await this._clearVotes()
|
||||
if (wasntUpvoted) {
|
||||
const res = await apilib.upvote(this.rootStore, this.uri, this.cid)
|
||||
runInAction(() => {
|
||||
this.upvoteCount++
|
||||
this.myState.upvote = res.uri
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async toggleDownvote() {
|
||||
const wasntDownvoted = !this.myState.downvote
|
||||
await this._clearVotes()
|
||||
if (wasntDownvoted) {
|
||||
const res = await apilib.downvote(this.rootStore, this.uri, this.cid)
|
||||
runInAction(() => {
|
||||
this.downvoteCount++
|
||||
this.myState.downvote = res.uri
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ export class MeModel {
|
|||
this.did = sess.data.did || ''
|
||||
this.handle = sess.data.handle
|
||||
const profile = await this.rootStore.api.app.bsky.actor.getProfile({
|
||||
user: this.did,
|
||||
actor: this.did,
|
||||
})
|
||||
runInAction(() => {
|
||||
if (profile?.data) {
|
||||
|
|
|
@ -57,8 +57,8 @@ export class NotificationsViewItemModel implements GroupedNotification {
|
|||
}
|
||||
}
|
||||
|
||||
get isLike() {
|
||||
return this.reason === 'like'
|
||||
get isUpvote() {
|
||||
return this.reason === 'vote'
|
||||
}
|
||||
|
||||
get isRepost() {
|
||||
|
|
|
@ -13,8 +13,9 @@ function* reactKeyGenerator(): Generator<string> {
|
|||
}
|
||||
|
||||
export class PostThreadViewPostMyStateModel {
|
||||
like?: string
|
||||
repost?: string
|
||||
upvote?: string
|
||||
downvote?: string
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this)
|
||||
|
@ -40,7 +41,8 @@ export class PostThreadViewPostModel implements GetPostThread.Post {
|
|||
replyCount: number = 0
|
||||
replies?: PostThreadViewPostModel[]
|
||||
repostCount: number = 0
|
||||
likeCount: number = 0
|
||||
upvoteCount: number = 0
|
||||
downvoteCount: number = 0
|
||||
indexedAt: string = ''
|
||||
myState = new PostThreadViewPostMyStateModel()
|
||||
|
||||
|
@ -105,18 +107,43 @@ export class PostThreadViewPostModel implements GetPostThread.Post {
|
|||
}
|
||||
}
|
||||
|
||||
async toggleLike() {
|
||||
if (this.myState.like) {
|
||||
await apilib.unlike(this.rootStore, this.myState.like)
|
||||
async _clearVotes() {
|
||||
if (this.myState.upvote) {
|
||||
await apilib.unupvote(this.rootStore, this.myState.upvote)
|
||||
runInAction(() => {
|
||||
this.likeCount--
|
||||
this.myState.like = undefined
|
||||
this.upvoteCount--
|
||||
this.myState.upvote = undefined
|
||||
})
|
||||
} else {
|
||||
const res = await apilib.like(this.rootStore, this.uri, this.cid)
|
||||
}
|
||||
if (this.myState.downvote) {
|
||||
await apilib.undownvote(this.rootStore, this.myState.downvote)
|
||||
runInAction(() => {
|
||||
this.likeCount++
|
||||
this.myState.like = res.uri
|
||||
this.downvoteCount--
|
||||
this.myState.downvote = undefined
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async toggleUpvote() {
|
||||
const wasntUpvoted = !this.myState.upvote
|
||||
await this._clearVotes()
|
||||
if (wasntUpvoted) {
|
||||
const res = await apilib.upvote(this.rootStore, this.uri, this.cid)
|
||||
runInAction(() => {
|
||||
this.upvoteCount++
|
||||
this.myState.upvote = res.uri
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async toggleDownvote() {
|
||||
const wasntDownvoted = !this.myState.downvote
|
||||
await this._clearVotes()
|
||||
if (wasntDownvoted) {
|
||||
const res = await apilib.downvote(this.rootStore, this.uri, this.cid)
|
||||
runInAction(() => {
|
||||
this.downvoteCount++
|
||||
this.myState.downvote = res.uri
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ export class ProfileUiModel {
|
|||
},
|
||||
{autoBind: true},
|
||||
)
|
||||
this.profile = new ProfileViewModel(rootStore, {user: params.user})
|
||||
this.profile = new ProfileViewModel(rootStore, {actor: params.user})
|
||||
this.feed = new FeedModel(rootStore, 'author', {
|
||||
author: params.user,
|
||||
limit: 10,
|
||||
|
|
|
@ -1,45 +1,41 @@
|
|||
import {makeAutoObservable, runInAction} from 'mobx'
|
||||
import {AtUri} from '../../third-party/uri'
|
||||
import * as GetLikedBy from '../../third-party/api/src/client/types/app/bsky/feed/getLikedBy'
|
||||
import * as GetVotes from '../../third-party/api/src/client/types/app/bsky/feed/getVotes'
|
||||
import {RootStoreModel} from './root-store'
|
||||
|
||||
type LikedByItem = GetLikedBy.OutputSchema['likedBy'][number]
|
||||
type VoteItem = GetVotes.OutputSchema['votes'][number]
|
||||
|
||||
export class LikedByViewItemModel implements LikedByItem {
|
||||
export class VotesViewItemModel implements VoteItem {
|
||||
// ui state
|
||||
_reactKey: string = ''
|
||||
|
||||
// data
|
||||
did: string = ''
|
||||
handle: string = ''
|
||||
displayName: string = ''
|
||||
createdAt?: string
|
||||
direction: 'up' | 'down' = 'up'
|
||||
indexedAt: string = ''
|
||||
createdAt: string = ''
|
||||
actor: GetVotes.Actor = {did: '', handle: ''}
|
||||
|
||||
constructor(reactKey: string, v: LikedByItem) {
|
||||
constructor(reactKey: string, v: VoteItem) {
|
||||
makeAutoObservable(this)
|
||||
this._reactKey = reactKey
|
||||
Object.assign(this, v)
|
||||
}
|
||||
}
|
||||
|
||||
export class LikedByViewModel {
|
||||
export class VotesViewModel {
|
||||
// state
|
||||
isLoading = false
|
||||
isRefreshing = false
|
||||
hasLoaded = false
|
||||
error = ''
|
||||
resolvedUri = ''
|
||||
params: GetLikedBy.QueryParams
|
||||
params: GetVotes.QueryParams
|
||||
|
||||
// data
|
||||
uri: string = ''
|
||||
likedBy: LikedByViewItemModel[] = []
|
||||
votes: VotesViewItemModel[] = []
|
||||
|
||||
constructor(
|
||||
public rootStore: RootStoreModel,
|
||||
params: GetLikedBy.QueryParams,
|
||||
) {
|
||||
constructor(public rootStore: RootStoreModel, params: GetVotes.QueryParams) {
|
||||
makeAutoObservable(
|
||||
this,
|
||||
{
|
||||
|
@ -113,7 +109,7 @@ export class LikedByViewModel {
|
|||
private async _fetch(isRefreshing = false) {
|
||||
this._xLoading(isRefreshing)
|
||||
try {
|
||||
const res = await this.rootStore.api.app.bsky.feed.getLikedBy(
|
||||
const res = await this.rootStore.api.app.bsky.feed.getVotes(
|
||||
Object.assign({}, this.params, {uri: this.resolvedUri}),
|
||||
)
|
||||
this._replaceAll(res)
|
||||
|
@ -123,15 +119,15 @@ export class LikedByViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
private _replaceAll(res: GetLikedBy.Response) {
|
||||
this.likedBy.length = 0
|
||||
private _replaceAll(res: GetVotes.Response) {
|
||||
this.votes.length = 0
|
||||
let counter = 0
|
||||
for (const item of res.data.likedBy) {
|
||||
for (const item of res.data.votes) {
|
||||
this._append(counter++, item)
|
||||
}
|
||||
}
|
||||
|
||||
private _append(keyId: number, item: LikedByItem) {
|
||||
this.likedBy.push(new LikedByViewItemModel(`item-${keyId}`, item))
|
||||
private _append(keyId: number, item: VoteItem) {
|
||||
this.votes.push(new VotesViewItemModel(`item-${keyId}`, item))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue