mirror of
https://github.com/acid-info/Kurate.git
synced 2025-01-12 08:54:08 +00:00
fix: clicking on a pending posts correctly links to it (#318)
This commit is contained in:
parent
71930ae827
commit
0486feacf8
@ -66,6 +66,7 @@ export class Firebase implements Adapter {
|
||||
})
|
||||
private subscriptions: Array<() => unknown> = []
|
||||
private userSubscriptions: Array<() => unknown> = []
|
||||
private votes = new Map<string, { promote: string[]; demote: string[] }>()
|
||||
|
||||
async start() {
|
||||
const personasQuery = query(collection(db, 'personas'))
|
||||
@ -340,6 +341,7 @@ export class Firebase implements Adapter {
|
||||
const { text, images, timestamp, demote, promote, address } = d.data() as PendingPost
|
||||
const loggedUser = get(profile)
|
||||
let yourVote: '+' | '-' | undefined = undefined
|
||||
this.votes.set(d.id, { promote, demote })
|
||||
if (loggedUser.address && promote.includes(loggedUser.address)) yourVote = '+'
|
||||
if (loggedUser.address && demote.includes(loggedUser.address)) yourVote = '-'
|
||||
newPending.push({
|
||||
@ -363,6 +365,30 @@ export class Firebase implements Adapter {
|
||||
})
|
||||
})
|
||||
|
||||
// Ensures that votuse and whether the post is yours is updated after user logs in
|
||||
const subscribeProfileChangePending = profile.subscribe(({ address }) => {
|
||||
if (address) {
|
||||
posts.update(({ data }) => {
|
||||
const personaPostData = data.get(groupId)
|
||||
if (!personaPostData) return { data }
|
||||
|
||||
const pending = personaPostData.pending.map((p) => {
|
||||
if (p.postId === undefined) return p
|
||||
const vt = this.votes.get(p.postId)
|
||||
if (vt === undefined) return p
|
||||
let yourVote: '+' | '-' | undefined = undefined
|
||||
if (vt.promote.includes(address)) yourVote = '+'
|
||||
if (vt.demote.includes(address)) yourVote = '-'
|
||||
|
||||
return { ...p, myPost: p.address === address, yourVote }
|
||||
})
|
||||
data.set(groupId, { ...personaPostData, pending })
|
||||
|
||||
return { data }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const subscribePosts = onSnapshot(postsCollection, (res) => {
|
||||
const newPostst: Post[] = []
|
||||
|
||||
@ -396,13 +422,17 @@ export class Firebase implements Adapter {
|
||||
})
|
||||
|
||||
return () => {
|
||||
subscribeProfileChangePending()
|
||||
subscribePending()
|
||||
subscribePosts()
|
||||
}
|
||||
}
|
||||
|
||||
async voteOnPost(groupId: string, postId: number, vote: '+' | '-', signer: Signer) {
|
||||
await signer.signMessage(`This "transaction" votes ${vote === '+' ? 'promote' : 'demote'}`)
|
||||
const promoteDemote: 'promote' | 'demote' = vote === '+' ? 'promote' : 'demote'
|
||||
await signer.signMessage(
|
||||
`By confirming this "transaction" you are casting ${promoteDemote} vote on the post`,
|
||||
)
|
||||
const address = await signer.getAddress()
|
||||
|
||||
const postData = get(posts).data.get(groupId)?.pending[postId]
|
||||
@ -410,7 +440,7 @@ export class Firebase implements Adapter {
|
||||
|
||||
const postDoc = doc(db, `personas/${groupId}/pending/${postData.postId}`)
|
||||
updateDoc(postDoc, {
|
||||
[vote === '+' ? 'promote' : 'demote']: arrayUnion(address),
|
||||
[promoteDemote]: arrayUnion(address),
|
||||
})
|
||||
|
||||
const { go } = get(tokens)
|
||||
@ -422,7 +452,7 @@ export class Firebase implements Adapter {
|
||||
timestamp: Date.now(),
|
||||
goChange: -VOTE_GO_PRICE,
|
||||
repChange: 0,
|
||||
type: vote === '+' ? 'promote' : 'demote',
|
||||
type: promoteDemote,
|
||||
personaId: groupId,
|
||||
})
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ export const ROUTES = {
|
||||
PERSONA_PENDING: (slug: string | number) => `/persona/${slug}/pending`,
|
||||
PERSONA_NEW: '/persona/new',
|
||||
PERSONA_POST: (id: string | number, postId: string | number) => `/persona/${id}/post/${postId}`,
|
||||
PERSONA_PENDING_POST: (id: string | number, postId: string | number) =>
|
||||
`/persona/${id}/pending/${postId}`,
|
||||
PERSONA_DRAFT: (id: string | number) => `/persona/draft/${id}`,
|
||||
POST_NEW: (slug: string) => `/persona/${slug}/post/new`,
|
||||
CHATS: '/chat',
|
||||
|
@ -15,7 +15,6 @@ interface PostData {
|
||||
}
|
||||
|
||||
export interface PostStore extends Writable<PostData> {
|
||||
setLoading: (groupId: string, loading: boolean) => void
|
||||
addPending: (post: Post, groupId: string) => void
|
||||
addApproved: (post: Post, groupId: string) => void
|
||||
}
|
||||
@ -25,16 +24,6 @@ function createPostStore(): PostStore {
|
||||
|
||||
return {
|
||||
...store,
|
||||
setLoading: (groupId, loading) => {
|
||||
store.update(({ data }) => {
|
||||
const personaPostData = data.get(groupId)
|
||||
const approved = personaPostData?.approved ?? []
|
||||
const pending = personaPostData?.pending ?? []
|
||||
data.set(groupId, { loading, approved, pending })
|
||||
|
||||
return { data }
|
||||
})
|
||||
},
|
||||
addPending: (post: Post, groupId: string) => {
|
||||
store.update(({ data }) => {
|
||||
const personaPostData = data.get(groupId)
|
||||
|
@ -19,6 +19,13 @@
|
||||
import InfoBox from '$lib/components/info-box.svelte'
|
||||
import Banner from '$lib/components/message-banner.svelte'
|
||||
import SingleColumn from '$lib/components/single-column.svelte'
|
||||
import SectionTitle from '$lib/components/section-title.svelte'
|
||||
import Dropdown from '$lib/components/dropdown.svelte'
|
||||
import DropdownItem from '$lib/components/dropdown-item.svelte'
|
||||
import Search from '$lib/components/search.svelte'
|
||||
import InfoScreen from '$lib/components/info_screen.svelte'
|
||||
import LearnMore from '$lib/components/learn-more.svelte'
|
||||
import BorderBox from '$lib/components/border-box.svelte'
|
||||
|
||||
import { posts } from '$lib/stores/post'
|
||||
import { personas } from '$lib/stores/persona'
|
||||
@ -29,15 +36,8 @@
|
||||
import adapter from '$lib/adapters'
|
||||
import { canConnectWallet } from '$lib/services'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import SectionTitle from '$lib/components/section-title.svelte'
|
||||
import Dropdown from '$lib/components/dropdown.svelte'
|
||||
import DropdownItem from '$lib/components/dropdown-item.svelte'
|
||||
import Search from '$lib/components/search.svelte'
|
||||
import { tokens } from '$lib/stores/tokens'
|
||||
import { VOTE_GO_PRICE } from '$lib/constants'
|
||||
import InfoScreen from '$lib/components/info_screen.svelte'
|
||||
import LearnMore from '$lib/components/learn-more.svelte'
|
||||
import BorderBox from '$lib/components/border-box.svelte'
|
||||
|
||||
const groupId = $page.params.id
|
||||
const persona = $personas.all.get(groupId)
|
||||
@ -240,7 +240,7 @@
|
||||
{:else}
|
||||
<Grid>
|
||||
{#each personaPosts.pending as post, index}
|
||||
<Post {post} on:click={() => goto(ROUTES.PERSONA_POST(groupId, index))}>
|
||||
<Post {post} on:click={() => goto(ROUTES.PERSONA_PENDING_POST(groupId, index))}>
|
||||
{#if post.yourVote === '+' && $profile.signer !== undefined}
|
||||
<Button icon={FavoriteFilled} variant="accent" label="You promoted this" />
|
||||
{:else if post.yourVote === '-' && $profile.signer !== undefined}
|
||||
|
@ -0,0 +1,212 @@
|
||||
<script lang="ts">
|
||||
import Post from '$lib/components/post.svelte'
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import ChatBot from '$lib/components/icons/chat-bot.svelte'
|
||||
import Wallet from '$lib/components/icons/wallet.svelte'
|
||||
import ThumbsDown from '$lib/components/icons/thumbs-down.svelte'
|
||||
import Favorite from '$lib/components/icons/favorite.svelte'
|
||||
import FavoriteFilled from '$lib/components/icons/favorite-filled.svelte'
|
||||
import Info from '$lib/components/icons/information.svelte'
|
||||
import Checkmark from '$lib/components/icons/checkmark.svelte'
|
||||
import Close from '$lib/components/icons/close.svelte'
|
||||
|
||||
import Banner from '$lib/components/message-banner.svelte'
|
||||
import Header from '$lib/components/header.svelte'
|
||||
import Container from '$lib/components/container.svelte'
|
||||
import InfoBox from '$lib/components/info-box.svelte'
|
||||
import InfoScreen from '$lib/components/info_screen.svelte'
|
||||
import SingleColumn from '$lib/components/single-column.svelte'
|
||||
import BorderBox from '$lib/components/border-box.svelte'
|
||||
import LearnMore from '$lib/components/learn-more.svelte'
|
||||
|
||||
import { posts } from '$lib/stores/post'
|
||||
import { profile } from '$lib/stores/profile'
|
||||
import type { DraftChat } from '$lib/stores/chat'
|
||||
import { personas } from '$lib/stores/persona'
|
||||
import { goto } from '$app/navigation'
|
||||
import { page } from '$app/stores'
|
||||
import { ROUTES } from '$lib/routes'
|
||||
import adapter from '$lib/adapters'
|
||||
import { canConnectWallet } from '$lib/services'
|
||||
import ChatScreen from '$lib/components/chat-screen.svelte'
|
||||
import { VOTE_GO_PRICE } from '$lib/constants'
|
||||
import { tokens } from '$lib/stores/tokens'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
|
||||
type Vote = {
|
||||
index: number
|
||||
vote: '+' | '-'
|
||||
}
|
||||
|
||||
let vote: Vote | undefined = undefined
|
||||
let unsubscribe: () => unknown
|
||||
|
||||
onMount(() => {
|
||||
adapter.subscribePersonaPosts(groupId).then((unsub) => (unsubscribe = unsub))
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscribe) unsubscribe()
|
||||
})
|
||||
|
||||
const postId = $page.params.postId as unknown as number
|
||||
const groupId = $page.params.id
|
||||
let post = $posts.data.get(groupId)?.pending[postId]
|
||||
$: post = $posts.data.get(groupId)?.pending[postId]
|
||||
const persona = $personas.all.get(groupId)
|
||||
let draftChat: DraftChat | undefined = undefined
|
||||
|
||||
const startChat = async () => {
|
||||
if (!persona || !post) return
|
||||
|
||||
draftChat = {
|
||||
persona,
|
||||
post,
|
||||
messages: [],
|
||||
}
|
||||
}
|
||||
|
||||
async function sendMessage(text: string) {
|
||||
if (!draftChat) return
|
||||
const chat = {
|
||||
...draftChat,
|
||||
messages: [{ timestamp: Date.now(), text, address: $profile.address }],
|
||||
}
|
||||
const chatId = await adapter.startChat(chat)
|
||||
goto(ROUTES.CHAT(chatId))
|
||||
}
|
||||
|
||||
let y: number
|
||||
|
||||
export let onBack: () => unknown = () => history.back()
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY={y} />
|
||||
|
||||
{#if $tokens.go > 0}
|
||||
<Banner icon={Info}>{$tokens.go} GO left in this cycle</Banner>
|
||||
{:else}
|
||||
<Banner icon={Info} variant="danger">No GO left in this cycle</Banner>
|
||||
{/if}
|
||||
|
||||
{#if post === undefined}
|
||||
<Container>
|
||||
<InfoBox>
|
||||
<div>There is no post with post ID {$page.params.postId}</div>
|
||||
</InfoBox>
|
||||
</Container>
|
||||
{:else if persona === undefined}
|
||||
<Container>
|
||||
<InfoBox>
|
||||
<div>There is no persona with ID {$page.params.id}</div>
|
||||
</InfoBox>
|
||||
</Container>
|
||||
{:else if draftChat !== undefined}
|
||||
<ChatScreen chat={draftChat} {sendMessage} title="New chat" onBack={() => history.back()} />
|
||||
{:else if vote !== undefined && $tokens.go >= VOTE_GO_PRICE}
|
||||
<InfoScreen title={vote.vote === '+' ? 'Promote' : 'Demote'} onBack={() => (vote = undefined)}>
|
||||
<SingleColumn>
|
||||
<InfoBox>
|
||||
<div class="icon">
|
||||
<Info size={32} />
|
||||
</div>
|
||||
<h2>This will use {VOTE_GO_PRICE} GO.</h2>
|
||||
<p>
|
||||
You will earn REP if the majority of the community also votes to {vote.vote === '+'
|
||||
? 'promote'
|
||||
: 'demote'} this content.
|
||||
</p>
|
||||
<p><LearnMore href="/" /></p>
|
||||
</InfoBox>
|
||||
<BorderBox
|
||||
title="Currently available"
|
||||
amount={$tokens.go.toFixed()}
|
||||
tokenName="GO"
|
||||
explanation="Until new cycle begins"
|
||||
/>
|
||||
</SingleColumn>
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button
|
||||
label="I agree"
|
||||
variant="primary"
|
||||
icon={Checkmark}
|
||||
on:click={async () => {
|
||||
if (!vote || !$profile.signer) return
|
||||
await adapter.voteOnPost(groupId, vote.index, vote.vote, $profile.signer)
|
||||
vote = undefined
|
||||
}}
|
||||
/>
|
||||
<Button label="Nope" icon={Close} on:click={() => (vote = undefined)} />
|
||||
</svelte:fragment>
|
||||
</InfoScreen>
|
||||
{:else if vote !== undefined}
|
||||
<InfoScreen title="Not enough token" onBack={() => (vote = undefined)}>
|
||||
<SingleColumn>
|
||||
<InfoBox>
|
||||
<div class="icon">
|
||||
<Info size={32} />
|
||||
</div>
|
||||
<h2>Sorry, you can't vote now.</h2>
|
||||
<p>
|
||||
You need {VOTE_GO_PRICE} GO to promote or demote content.
|
||||
</p>
|
||||
<p><LearnMore href="/" /></p>
|
||||
</InfoBox>
|
||||
<BorderBox
|
||||
title="Currently available"
|
||||
amount={$tokens.go.toFixed()}
|
||||
tokenName="GO"
|
||||
explanation="Until new cycle begins"
|
||||
error
|
||||
/>
|
||||
</SingleColumn>
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button
|
||||
label="I agree"
|
||||
variant="primary"
|
||||
icon={Checkmark}
|
||||
on:click={() => {
|
||||
if (!vote || !$profile.signer) return
|
||||
adapter.voteOnPost(groupId, vote.index, vote.vote, $profile.signer)
|
||||
vote = undefined
|
||||
}}
|
||||
/>
|
||||
<Button label="Nope" icon={Close} on:click={() => (vote = undefined)} />
|
||||
</svelte:fragment>
|
||||
</InfoScreen>
|
||||
{:else}
|
||||
<Header title="Post" {onBack} />
|
||||
<!-- TODO: This is the post page so I'm thinking there shouldn't be an action on the post -->
|
||||
<Post {post} on:click noHover>
|
||||
{#if post.yourVote === '+' && $profile.signer !== undefined}
|
||||
<Button icon={FavoriteFilled} variant="accent" label="You promoted this" />
|
||||
{:else if post.yourVote === '-' && $profile.signer !== undefined}
|
||||
<Button icon={ThumbsDown} variant="accent" label="You demoted this" />
|
||||
{:else}
|
||||
<Button
|
||||
variant="secondary"
|
||||
label="Promote"
|
||||
icon={Favorite}
|
||||
disabled={$profile.signer === undefined}
|
||||
on:click={() => (vote = { index: postId, vote: '+' })}
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
label="Demote"
|
||||
icon={ThumbsDown}
|
||||
disabled={$profile.signer === undefined}
|
||||
on:click={() => (vote = { index: postId, vote: '-' })}
|
||||
/>
|
||||
{/if}
|
||||
{#if $profile.signer === undefined}
|
||||
<Button
|
||||
variant="primary"
|
||||
icon={Wallet}
|
||||
on:click={adapter.signIn}
|
||||
disabled={!canConnectWallet()}
|
||||
/>
|
||||
{:else if $profile.signer !== undefined && $profile.address !== post.address}
|
||||
<Button variant="primary" label="Chat with poster" icon={ChatBot} on:click={startChat} />
|
||||
{/if}
|
||||
</Post>
|
||||
{/if}
|
@ -17,8 +17,12 @@
|
||||
import adapter from '$lib/adapters'
|
||||
import { canConnectWallet } from '$lib/services'
|
||||
import ChatScreen from '$lib/components/chat-screen.svelte'
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
|
||||
const post = $posts.data.get($page.params.id)?.approved[$page.params.postId as unknown as number]
|
||||
const postId = $page.params.postId as unknown as number
|
||||
const groupId = $page.params.id
|
||||
let post = $posts.data.get(groupId)?.approved[postId]
|
||||
$: post = $posts.data.get(groupId)?.approved[postId]
|
||||
const persona = $personas.all.get($page.params.id)
|
||||
let draftChat: DraftChat | undefined = undefined
|
||||
|
||||
@ -41,6 +45,15 @@
|
||||
const chatId = await adapter.startChat(chat)
|
||||
goto(ROUTES.CHAT(chatId))
|
||||
}
|
||||
let unsubscribe: () => unknown
|
||||
|
||||
onMount(() => {
|
||||
adapter.subscribePersonaPosts(groupId).then((unsub) => (unsubscribe = unsub))
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscribe) unsubscribe()
|
||||
})
|
||||
|
||||
let y: number
|
||||
|
||||
@ -68,15 +81,15 @@
|
||||
<!-- TODO: This is the post page so I'm thinking there shouldn't be an action on the post -->
|
||||
<Post {post} on:click noHover />
|
||||
<div class="center">
|
||||
{#if $profile.signer !== undefined}
|
||||
<Button variant="primary" label="Chat with poster" icon={ChatBot} on:click={startChat} />
|
||||
{:else}
|
||||
{#if $profile.signer === undefined}
|
||||
<Button
|
||||
variant="primary"
|
||||
icon={Wallet}
|
||||
on:click={adapter.signIn}
|
||||
disabled={!canConnectWallet()}
|
||||
/>
|
||||
{:else if $profile.signer !== undefined && $profile.address !== post.address}
|
||||
<Button variant="primary" label="Chat with poster" icon={ChatBot} on:click={startChat} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
Loading…
x
Reference in New Issue
Block a user