feat: post message to existing or draft persona (#229)

This commit is contained in:
Vojtech Simetka 2023-03-05 21:15:39 +01:00 committed by GitHub
parent b32ab38593
commit 8a952af3f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 272 additions and 56 deletions

2
package-lock.json generated
View File

@ -40231,7 +40231,7 @@
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vite": "^4.1.1",
"vitest": "*"
"vitest": "^0.28.5"
},
"dependencies": {
"@fontsource/source-sans-pro": {

View File

@ -0,0 +1,60 @@
<script lang="ts">
import LearnMore from '$lib/components/learn-more.svelte'
export let title: string
export let amount: string
export let tokenName: string
export let explanation: string
export let link = '' // FIXME: make this required when we have FAQ build
export let error = false
</script>
<div class={`box ${error ? 'error' : ''}`}>
<div class="h3">{title}</div>
<div class="token-amt">{amount}</div>
<div class="token">{tokenName}</div>
<p>{explanation}</p>
<LearnMore href={link} />
</div>
<style lang="scss">
.box {
border: 1px solid var(--grey-200);
padding: var(--spacing-24);
margin-top: var(--spacing-48);
flex-basis: 100%;
margin-left: 3px;
margin-right: 3px;
&.first-child {
margin-left: 0px;
}
&.last-child {
margin-right: 0px;
}
.token-amt {
font-size: 40px;
line-height: 1;
font-weight: var(--font-weight-sb);
margin-top: var(--spacing-12);
}
.token {
text-transform: uppercase;
font-weight: var(--font-weight-sb);
margin-bottom: var(--spacing-12);
}
&.error {
border: 1px solid var(--color-red);
background-color: rgba(var(--color-red-rgb), 0.05);
.token-amt,
.token {
color: var(--color-red);
}
}
}
</style>

View File

@ -2,14 +2,15 @@ import { writable, type Writable } from 'svelte/store'
export interface TokenData {
go: number
rep: number
repTotal: number
repStaked: number
loading: boolean
}
export type TokenStore = Writable<TokenData>
function createTokenStore(): TokenStore {
const store = writable<TokenData>({ go: 30, rep: 0, loading: false })
const store = writable<TokenData>({ go: 30, repTotal: 10, repStaked: 5, loading: false })
return store
}

View File

@ -1,7 +1,5 @@
<script lang="ts">
import { profile } from '$lib/stores/profile'
import { goto } from '$app/navigation'
import { ROUTES } from '$lib/routes'
import {
createIdentity,
generateGroupProof,
@ -11,10 +9,28 @@
joinGroupOffChain,
joinGroupOnChain,
} from '$lib/services/index'
import Checkmark from '$lib/components/icons/checkmark.svelte'
import Close from '$lib/components/icons/close.svelte'
import { posts } from '$lib/stores/post'
import { hashPost, createPost } from '$lib/services/posts'
import { getWaku } from '$lib/services/waku'
import PostNew from '$lib/components/post_new.svelte'
import InfoScreen from '$lib/components/info_screen.svelte'
import Info from '$lib/components/icons/information.svelte'
import LearnMore from '$lib/components/learn-more.svelte'
import Button from '$lib/components/button.svelte'
import { tokens } from '$lib/stores/tokens'
import TokenInfo from '$lib/components/token-info.svelte'
import Undo from '$lib/components/icons/undo.svelte'
// FIXME: These should come from some constants
const TOKEN_POST_COST_REP = 5
const TOKEN_POST_COST_GO = 5
// FIXME: This should be stored in persona and loaded
const TOKEN_POST_MIN_REP = 5
async function submit(postText: string) {
try {
@ -32,8 +48,7 @@
if (!group.members.includes(commitment)) {
joinGroupOffChain(group, commitment)
const txres = await joinGroupOnChain(globalAnonymousFeed, commitment)
console.log(txres)
await joinGroupOnChain(globalAnonymousFeed, commitment)
}
const post = { text: postText }
@ -49,11 +64,184 @@
timestamp: Date.now(),
text: postText,
})
goto(ROUTES.HOME)
state = 'post_submitted'
} catch (error) {
console.error(error)
}
}
function onBack() {
history.back()
}
let state: 'price_varning' | 'edit' | 'post_submitted' = 'price_varning'
</script>
<PostNew {submit} />
{#if state === 'price_varning'}
{#if $tokens.repTotal < TOKEN_POST_MIN_REP}
<InfoScreen title="Not enough REP" {onBack}>
<div class="token-info">
<div>
<div class="icon">
<Info size={32} />
</div>
<h2>Sorry, you can't submit a post now</h2>
<p>You need at least {TOKEN_POST_MIN_REP} REP to submit a post through this Persona.</p>
<LearnMore href="/" />
</div>
<TokenInfo
title="Available to stake"
amount={$tokens.repTotal.toFixed()}
tokenName="REP"
explanation={`Including staked`}
error
/>
</div>
<svelte:fragment slot="buttons">
<Button label="Back" icon={Undo} on:click={onBack} />
</svelte:fragment>
</InfoScreen>
{:else if $tokens.go < TOKEN_POST_COST_GO || $tokens.repTotal - $tokens.repStaked < TOKEN_POST_COST_REP}
<InfoScreen title="Not enough token" {onBack}>
<div class="token-info">
<div>
<div class="icon">
<Info size={32} />
</div>
<h2>Sorry, you can't submit a post now</h2>
<p>
You need {TOKEN_POST_COST_REP} REP to stake and {TOKEN_POST_COST_GO} GO to submit a post.
</p>
<LearnMore href="/" />
</div>
<div class="side-by-side">
<TokenInfo
title="Available to stake"
amount={($tokens.repTotal - $tokens.repStaked).toFixed()}
tokenName="REP"
explanation={`${$tokens.repStaked} out of ${$tokens.repTotal} staked`}
error={$tokens.repTotal - $tokens.repStaked < TOKEN_POST_COST_REP}
/>
<TokenInfo
title="Currently available"
amount={$tokens.go.toFixed()}
tokenName="GO"
explanation="Until new cycle begins"
error={$tokens.go < TOKEN_POST_COST_GO}
/>
</div>
</div>
<svelte:fragment slot="buttons">
<Button label="Back" icon={Undo} on:click={onBack} />
</svelte:fragment>
</InfoScreen>
{:else}
<InfoScreen title="Submit Post" {onBack}>
<div class="token-info">
<div>
<div class="icon">
<Info size={32} />
</div>
<h2>This will stake {TOKEN_POST_COST_REP} REP and use {TOKEN_POST_COST_GO} GO</h2>
<p>
Your post will be submitted to a community vote, and will be published if the majority
votes to promote it. If promoted, you will earn {TOKEN_POST_COST_REP} REP. If demoted, you
will lose your staked REP.
</p>
<p><LearnMore href="/" /></p>
</div>
<div class="side-by-side">
<TokenInfo
title="Available to stake"
amount={($tokens.repTotal - $tokens.repStaked).toFixed()}
tokenName="REP"
explanation={`${$tokens.repStaked} out of ${$tokens.repTotal} staked`}
/>
<TokenInfo
title="Currently available"
amount={$tokens.go.toFixed()}
tokenName="GO"
explanation="Until new cycle begins"
/>
</div>
</div>
<svelte:fragment slot="buttons">
<Button
label="I agree"
variant="primary"
icon={Checkmark}
on:click={() => (state = 'edit')}
/>
<Button label="Nope" icon={Close} on:click={onBack} />
</svelte:fragment>
</InfoScreen>
{/if}
{:else if state === 'edit'}
<PostNew {submit} {onBack} />
{:else}
<InfoScreen title="Post submitted">
<div class="token-info">
<div class="icon-success">
<Checkmark />
</div>
<h2>Your post is now pending review</h2>
<p>
Your post has been added to "Persona name's" pending list for community review. If it gets
promoted it will be automatically published to "Persona name's" page when the new epoch
begins.
</p>
<LearnMore href="/" />
</div>
<svelte:fragment slot="buttons">
<Button icon={Checkmark} variant="primary" label="Done" on:click={() => history.back()} />
</svelte:fragment>
</InfoScreen>
{/if}
<style lang="scss">
.side-by-side {
display: flex;
flex-direction: row;
}
.token-info {
text-align: center;
.icon {
margin-bottom: var(--spacing-12);
}
p,
h2 {
margin-bottom: var(--spacing-6);
}
}
.icon-success {
position: relative;
display: inline-block;
margin-bottom: var(--spacing-12);
:global(svg) {
fill: var(--color-body-bg);
}
:global(polygon) {
stroke: #fff;
stroke-width: 1px;
}
&::before {
position: absolute;
content: '';
inset: -4px auto auto -6px;
background-color: var(--color-success);
border-radius: 50%;
width: 28px;
height: 28px;
transform: translateX(2px);
z-index: -1;
}
}
</style>

View File

@ -23,6 +23,7 @@
import { personas } from '$lib/stores/persona'
import { tokens } from '$lib/stores/tokens'
import { page } from '$app/stores'
import TokenInfo from '$lib/components/token-info.svelte'
const PERSONA_LIMIT = 5
const TOKEN_POST_COST = 10
@ -155,15 +156,12 @@
<p>This Persona will be live, and everyone will be able to post with it.</p>
<p><LearnMore href="/" /></p>
</div>
<div class="box">
<div class="h3">Currently available</div>
<div class="go-amt">
{$tokens.go}
</div>
<div class="go">GO</div>
<p>Until new cycle begins</p>
<LearnMore href="/" />
</div>
<TokenInfo
title="Currently available"
amount={$tokens.go.toFixed()}
tokenName="GO"
explanation="Until new cycle begins"
/>
</div>
{:else}
<div class="token-info">
@ -175,15 +173,13 @@
<p>You need {TOKEN_POST_COST} GO to publish a Persona.</p>
<LearnMore href="/" />
</div>
<div class="box error">
<div class="h3">Currently available</div>
<div class="go-amt">
{$tokens.go}
</div>
<div class="go">GO</div>
<p>Until new cycle begins</p>
<LearnMore href="/" />
</div>
<TokenInfo
title="Currently available"
amount={$tokens.go.toFixed()}
tokenName="GO"
explanation="Until new cycle begins"
error
/>
</div>
{/if}
@ -242,35 +238,6 @@
}
}
.box {
border: 1px solid var(--grey-200);
padding: var(--spacing-24);
margin-top: var(--spacing-48);
.go-amt {
font-size: 40px;
line-height: 1;
font-weight: var(--font-weight-sb);
margin-top: var(--spacing-12);
}
.go {
text-transform: uppercase;
font-weight: var(--font-weight-sb);
margin-bottom: var(--spacing-12);
}
&.error {
border: 1px solid var(--color-red);
background-color: rgba(var(--color-red-rgb), 0.05);
.go-amt,
.go {
color: var(--color-red);
}
}
}
.icon-success {
position: relative;
display: inline-block;