mirror of
https://github.com/acid-info/Kurate.git
synced 2025-02-11 23:36:25 +00:00
refactor: strenghten firebase data types (#390)
This commit is contained in:
parent
4acdff71fe
commit
005e3d918c
99
packages/ui/src/lib/adapters/firebase/db-adapter.ts
Normal file
99
packages/ui/src/lib/adapters/firebase/db-adapter.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import type { Chat, DraftChat } from '$lib/stores/chat'
|
||||
import type { DraftPersona, Persona } from '$lib/stores/persona'
|
||||
import type { DraftPost, Post, PostPending } from '$lib/stores/post'
|
||||
|
||||
export function personaFromDB(persona: DBPersona, personaId: string): Persona {
|
||||
return {
|
||||
participantsCount: persona.participants.length,
|
||||
picture: persona.picture,
|
||||
minReputation: persona.minReputation,
|
||||
cover: persona.cover,
|
||||
name: persona.name,
|
||||
pitch: persona.pitch,
|
||||
description: persona.description,
|
||||
postsCount: persona.postsCount,
|
||||
timestamp: persona.timestamp,
|
||||
personaId,
|
||||
}
|
||||
}
|
||||
|
||||
export function personaToDB(persona: DraftPersona, participants: string[]): DBPersona {
|
||||
return {
|
||||
cover: persona.cover,
|
||||
description: persona.description,
|
||||
minReputation: persona.minReputation,
|
||||
name: persona.name,
|
||||
postsCount: persona.posts.length,
|
||||
picture: persona.picture,
|
||||
pitch: persona.pitch,
|
||||
timestamp: persona.timestamp,
|
||||
participants,
|
||||
}
|
||||
}
|
||||
|
||||
export function postDraftToDB(post: DraftPost, address: string): DBPostPending {
|
||||
return {
|
||||
...post,
|
||||
address,
|
||||
demote: [],
|
||||
promote: [],
|
||||
}
|
||||
}
|
||||
|
||||
export function postPendingFromDB(
|
||||
post: DBPostPending,
|
||||
postId: string,
|
||||
address?: string,
|
||||
): PostPending {
|
||||
let yourVote: '+' | '-' | undefined = undefined
|
||||
if (address && post.promote.includes(address)) yourVote = '+'
|
||||
if (address && post.demote.includes(address)) yourVote = '-'
|
||||
return {
|
||||
postId,
|
||||
yourVote,
|
||||
text: post.text,
|
||||
images: post.images,
|
||||
timestamp: post.timestamp,
|
||||
myPost: address === post.address,
|
||||
}
|
||||
}
|
||||
|
||||
export function postFromDB(post: DBPost, postId: string, address?: string): Post {
|
||||
return {
|
||||
postId,
|
||||
text: post.text,
|
||||
timestamp: post.timestamp,
|
||||
images: post.images,
|
||||
myPost: address === post.address,
|
||||
}
|
||||
}
|
||||
|
||||
export function chatToDB(chat: DraftChat, address: string, postAddress: string): DBChat {
|
||||
return {
|
||||
users: [address, postAddress],
|
||||
personaId: chat.persona.personaId,
|
||||
post: {
|
||||
postId: chat.post.postId,
|
||||
address: postAddress,
|
||||
images: chat.post.images,
|
||||
text: chat.post.text,
|
||||
timestamp: chat.post.timestamp,
|
||||
},
|
||||
messages: [],
|
||||
}
|
||||
}
|
||||
|
||||
export function chatFromDB(chat: DBChat, persona: Persona, chatId: string): Chat {
|
||||
return {
|
||||
chatId,
|
||||
users: chat.users,
|
||||
persona: persona,
|
||||
post: {
|
||||
postId: chat.post.postId,
|
||||
images: chat.post.images,
|
||||
text: chat.post.text,
|
||||
timestamp: chat.post.timestamp,
|
||||
},
|
||||
messages: chat.messages,
|
||||
}
|
||||
}
|
@ -12,17 +12,9 @@ import {
|
||||
VOTE_GO_PRICE,
|
||||
} from '$lib/constants'
|
||||
import { tokens } from '$lib/stores/tokens'
|
||||
import { posts, type Post } from '$lib/stores/post'
|
||||
import { posts, type Post, type PostPending } from '$lib/stores/post'
|
||||
import { transaction, type TransactionRecord } from '$lib/stores/transaction'
|
||||
|
||||
import type { Adapter } from '..'
|
||||
|
||||
// FIXME: no idea where whe should put these so that they don't leak. I can limit to some specific origin I guess
|
||||
const IPFS_AUTH =
|
||||
'Basic Mk5Nbk1vZUNSTWMyOTlCQjYzWm9QZzlQYTU3OjAwZTk2MmJjZTBkZmQxZWQxNGNhNmY1M2JiYjYxMTli'
|
||||
const IPFS_GATEWAY = 'https://kurate.infura-ipfs.io/ipfs'
|
||||
|
||||
// Import the functions you need from the SDKs you need
|
||||
import { initializeApp } from 'firebase/app'
|
||||
import {
|
||||
getFirestore,
|
||||
@ -38,8 +30,19 @@ import {
|
||||
} from 'firebase/firestore'
|
||||
import { get } from 'svelte/store'
|
||||
import { subscribeAccountChanged, subscribeChainChanged } from '../utils'
|
||||
// TODO: Add SDKs for Firebase products that you want to use
|
||||
// https://firebase.google.com/docs/web/setup#available-libraries
|
||||
import {
|
||||
chatFromDB,
|
||||
chatToDB,
|
||||
personaFromDB,
|
||||
personaToDB,
|
||||
postFromDB,
|
||||
postPendingFromDB,
|
||||
} from './db-adapter'
|
||||
|
||||
// FIXME: no idea where whe should put these so that they don't leak. I can limit to some specific origin I guess
|
||||
const IPFS_AUTH =
|
||||
'Basic Mk5Nbk1vZUNSTWMyOTlCQjYzWm9QZzlQYTU3OjAwZTk2MmJjZTBkZmQxZWQxNGNhNmY1M2JiYjYxMTli'
|
||||
const IPFS_GATEWAY = 'https://kurate.infura-ipfs.io/ipfs'
|
||||
|
||||
// Your web app's Firebase configuration
|
||||
const firebaseConfig = {
|
||||
@ -82,6 +85,8 @@ export class Firebase implements Adapter {
|
||||
private subscriptions: Array<() => unknown> = []
|
||||
private userSubscriptions: Array<() => unknown> = []
|
||||
private votes = new Map<string, { promote: string[]; demote: string[] }>()
|
||||
private participants = new Map<string, string[]>()
|
||||
private postIdParticipant = new Map<string, string>()
|
||||
|
||||
async start() {
|
||||
const personasQuery = query(collection(db, 'personas'))
|
||||
@ -89,10 +94,10 @@ export class Firebase implements Adapter {
|
||||
personas.update((state) => {
|
||||
const all = new Map<string, Persona>()
|
||||
data.docs.forEach((e) => {
|
||||
const persona = e.data()
|
||||
persona.participantsCount = persona.participants?.length
|
||||
persona.personaId = e.id
|
||||
all.set(e.id, persona as Persona)
|
||||
const dbPersona = e.data() as DBPersona
|
||||
const persona = personaFromDB(dbPersona, e.id)
|
||||
this.participants.set(e.id, dbPersona.participants)
|
||||
all.set(e.id, persona)
|
||||
})
|
||||
|
||||
return { ...state, all, loading: false }
|
||||
@ -118,7 +123,8 @@ export class Firebase implements Adapter {
|
||||
const subscribeTransactions = onSnapshot(transactionSnapshot, (res) => {
|
||||
const trns: TransactionRecord[] = []
|
||||
res.docs.forEach((d) => {
|
||||
trns.push(d.data() as TransactionRecord)
|
||||
const transactionsDb = d.data() as DBTransaction
|
||||
trns.push(transactionsDb)
|
||||
})
|
||||
transaction.set({ transactions: trns })
|
||||
})
|
||||
@ -132,16 +138,10 @@ export class Firebase implements Adapter {
|
||||
const newChats = new Map<string, Chat>()
|
||||
const personasTemp = get(personas)
|
||||
res.docs.forEach((d) => {
|
||||
const data = d.data()
|
||||
const data = d.data() as DBChat
|
||||
const persona = personasTemp.all.get(data.personaId)
|
||||
if (!persona) return
|
||||
const chat: Chat = {
|
||||
persona,
|
||||
post: data.post,
|
||||
users: data.users,
|
||||
chatId: d.id,
|
||||
messages: data.messages,
|
||||
}
|
||||
const chat = chatFromDB(data, persona, d.id)
|
||||
newChats.set(d.id, chat)
|
||||
})
|
||||
chats.update((state) => ({ ...state, chats: newChats, loading: false }))
|
||||
@ -233,29 +233,27 @@ export class Firebase implements Adapter {
|
||||
await signer.signMessage('This "transaction" publishes persona')
|
||||
const address = await signer.getAddress()
|
||||
const personasCollection = collection(db, 'personas')
|
||||
const { posts, ...persona } = draftPersona
|
||||
const personaDoc = await addDoc(personasCollection, {
|
||||
...persona,
|
||||
participants: [address],
|
||||
postsCount: 5,
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
const { posts } = draftPersona
|
||||
const personaDoc = await addDoc(personasCollection, personaToDB(draftPersona, [address]))
|
||||
|
||||
const postCollection = collection(db, `personas/${personaDoc.id}/posts`)
|
||||
posts.forEach((p) =>
|
||||
addDoc(postCollection, {
|
||||
posts.forEach((p) => {
|
||||
const dbPost: DBPost = {
|
||||
...p,
|
||||
address,
|
||||
}),
|
||||
)
|
||||
}
|
||||
addDoc(postCollection, dbPost)
|
||||
})
|
||||
|
||||
const profileCollection = collection(db, `users/${address}/transactions`)
|
||||
await addDoc(profileCollection, {
|
||||
const transaction: DBTransaction = {
|
||||
timestamp: Date.now(),
|
||||
goChange: -CREATE_PERSONA_GO_PRICE,
|
||||
repChange: 0,
|
||||
personaId: personaDoc.id,
|
||||
type: 'publish persona',
|
||||
})
|
||||
}
|
||||
await addDoc(profileCollection, transaction)
|
||||
|
||||
const { go, repTotal, repStaked } = get(tokens)
|
||||
const user = doc(db, `users/${address}`)
|
||||
@ -306,9 +304,9 @@ export class Firebase implements Adapter {
|
||||
signer: Signer,
|
||||
): Promise<string> {
|
||||
const address = await signer.getAddress()
|
||||
const isMemberOfGroup = get(personas).all.get(groupId)?.participants?.includes(address)
|
||||
const isMemberOfGroup = this.participants.get(groupId)?.includes(address)
|
||||
|
||||
const post = {
|
||||
const post: DBPostPending = {
|
||||
timestamp: Date.now(),
|
||||
text,
|
||||
images,
|
||||
@ -330,13 +328,15 @@ export class Firebase implements Adapter {
|
||||
const postDoc = await addDoc(pendingPosts, post)
|
||||
|
||||
const profileCollection = collection(db, `users/${address}/transactions`)
|
||||
await addDoc(profileCollection, {
|
||||
|
||||
const transaction: DBTransaction = {
|
||||
timestamp: Date.now(),
|
||||
goChange: -NEW_POST_GO_PRICE,
|
||||
repChange: -NEW_POST_REP_PRICE,
|
||||
personaId: groupId,
|
||||
type: 'publish post',
|
||||
})
|
||||
}
|
||||
await addDoc(profileCollection, transaction)
|
||||
|
||||
const { go, repTotal, repStaked } = get(tokens)
|
||||
const user = doc(db, `users/${address}`)
|
||||
@ -354,32 +354,14 @@ export class Firebase implements Adapter {
|
||||
const postsCollection = collection(db, `personas/${groupId}/posts`)
|
||||
|
||||
const subscribePending = onSnapshot(pendingCollection, (res) => {
|
||||
const newPending: Post[] = []
|
||||
const newPending: PostPending[] = []
|
||||
|
||||
res.docs.forEach((d) => {
|
||||
interface PendingPost {
|
||||
demote: string[]
|
||||
images: string[]
|
||||
promote: string[]
|
||||
text: string
|
||||
timestamp: number
|
||||
address: string
|
||||
}
|
||||
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({
|
||||
text,
|
||||
images,
|
||||
timestamp,
|
||||
yourVote,
|
||||
postId: d.id,
|
||||
address,
|
||||
myPost: loggedUser.address === address,
|
||||
})
|
||||
const postDb = d.data() as DBPostPending
|
||||
const { address } = get(profile)
|
||||
this.votes.set(d.id, { promote: postDb.promote, demote: postDb.demote })
|
||||
this.postIdParticipant.set(d.id, postDb.address)
|
||||
newPending.push(postPendingFromDB(postDb, d.id, address))
|
||||
})
|
||||
|
||||
posts.update(({ data }) => {
|
||||
@ -407,7 +389,9 @@ export class Firebase implements Adapter {
|
||||
if (vt.promote.includes(address)) yourVote = '+'
|
||||
if (vt.demote.includes(address)) yourVote = '-'
|
||||
|
||||
return { ...p, myPost: p.address === address, yourVote }
|
||||
const postSender = this.postIdParticipant.get(p.postId)
|
||||
|
||||
return { ...p, myPost: postSender === address, yourVote }
|
||||
})
|
||||
data.set(groupId, { ...personaPostData, pending })
|
||||
|
||||
@ -420,22 +404,10 @@ export class Firebase implements Adapter {
|
||||
const newPostst: Post[] = []
|
||||
|
||||
res.docs.forEach((d) => {
|
||||
interface DbPost {
|
||||
images: string[]
|
||||
text: string
|
||||
timestamp: number
|
||||
address: string
|
||||
}
|
||||
const { text, images, timestamp, address } = d.data() as DbPost
|
||||
const loggedUser = get(profile)
|
||||
newPostst.push({
|
||||
text,
|
||||
images,
|
||||
timestamp,
|
||||
postId: d.id,
|
||||
address,
|
||||
myPost: address === loggedUser.address,
|
||||
})
|
||||
const postDb = d.data() as DBPost
|
||||
const { address } = get(profile)
|
||||
this.postIdParticipant.set(d.id, postDb.address)
|
||||
newPostst.push(postFromDB(postDb, d.id, address))
|
||||
})
|
||||
|
||||
posts.update(({ data }) => {
|
||||
@ -477,34 +449,29 @@ export class Firebase implements Adapter {
|
||||
setDoc(user, { address, go: go - VOTE_GO_PRICE }, { merge: true })
|
||||
|
||||
const profileCollection = collection(db, `users/${address}/transactions`)
|
||||
await addDoc(profileCollection, {
|
||||
|
||||
const transaction: DBTransaction = {
|
||||
timestamp: Date.now(),
|
||||
goChange: -VOTE_GO_PRICE,
|
||||
repChange: 0,
|
||||
type: promoteDemote,
|
||||
personaId: groupId,
|
||||
})
|
||||
}
|
||||
|
||||
await addDoc(profileCollection, transaction)
|
||||
}
|
||||
|
||||
async startChat(chat: DraftChat): Promise<string> {
|
||||
const address = get(profile).address
|
||||
const { address } = get(profile)
|
||||
const postSender = this.postIdParticipant.get(chat.post.postId)
|
||||
|
||||
if (!address) throw new Error('You need to be logged in to start a chat')
|
||||
if (!chat.post.address) throw new Error('Info about original poster is missing')
|
||||
if (!postSender) throw new Error('Info about original poster is missing')
|
||||
if (!chat.post.postId) throw new Error('PostId is missing')
|
||||
if (!chat.persona.personaId) throw new Error('PersonaId is missing')
|
||||
|
||||
const dbChat = {
|
||||
users: [address, chat.post.address],
|
||||
post: {
|
||||
postId: chat.post.postId,
|
||||
address: chat.post.address,
|
||||
images: chat.post.images,
|
||||
timestamp: chat.post.timestamp,
|
||||
text: chat.post.text,
|
||||
} as Post,
|
||||
personaId: chat.persona.personaId,
|
||||
}
|
||||
const dbChat = chatToDB(chat, address, postSender)
|
||||
|
||||
const chatCollection = collection(db, `/chats`)
|
||||
const chatDoc = await addDoc(chatCollection, dbChat)
|
||||
|
||||
|
39
packages/ui/src/lib/adapters/firebase/types.d.ts
vendored
Normal file
39
packages/ui/src/lib/adapters/firebase/types.d.ts
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
interface DBPersona {
|
||||
cover: string
|
||||
description: string
|
||||
minReputation: ReputationOptions
|
||||
name: string
|
||||
participants: string[]
|
||||
postsCount: number
|
||||
picture: string
|
||||
pitch: string
|
||||
postsCount: string
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
interface DBPost {
|
||||
images: string[]
|
||||
text: string
|
||||
timestamp: number
|
||||
address: string
|
||||
}
|
||||
|
||||
interface DBPostPending extends DBPost {
|
||||
demote: string[]
|
||||
promote: string[]
|
||||
}
|
||||
|
||||
interface DBChatMessage {
|
||||
address: string
|
||||
text: string
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
interface DBChat {
|
||||
messages: DBChatMessage[]
|
||||
personaId: string
|
||||
post: DBPost & { postId: string }
|
||||
users: string[]
|
||||
}
|
||||
|
||||
type DBTransaction = TransactionRecord
|
@ -50,7 +50,7 @@
|
||||
|
||||
let avatar = createAvatar(botttsNeutral, {
|
||||
size: 94, // This is 47pt at 2x resolution
|
||||
seed: chat.chatId,
|
||||
seed: (chat as Chat).chatId,
|
||||
}).toDataUriSync()
|
||||
|
||||
$: if (scrollElement) observer.observe(scrollElement)
|
||||
|
@ -14,7 +14,6 @@ export interface DraftChat {
|
||||
post: Post
|
||||
messages: Message[]
|
||||
closed?: boolean
|
||||
chatId?: string
|
||||
}
|
||||
|
||||
export interface Chat extends DraftChat {
|
||||
|
@ -15,7 +15,6 @@ export interface Persona {
|
||||
postsCount: number
|
||||
minReputation: ReputationOptions
|
||||
timestamp: number
|
||||
participants?: string[] // FIXME: this is only needed for firebase, might want to remove
|
||||
}
|
||||
|
||||
export interface DraftPersona
|
||||
|
@ -7,18 +7,20 @@ export interface DraftPost {
|
||||
}
|
||||
|
||||
export interface Post extends DraftPost {
|
||||
yourVote?: '+' | '-'
|
||||
myPost?: boolean
|
||||
postId: string
|
||||
address?: string // FIXME: only needed for firebase, might want to remove
|
||||
myPost?: boolean
|
||||
}
|
||||
|
||||
export interface PostPending extends Post {
|
||||
yourVote?: '+' | '-'
|
||||
}
|
||||
|
||||
interface PostData {
|
||||
data: Map<string, { approved: Post[]; pending: Post[]; loading: boolean }>
|
||||
data: Map<string, { approved: Post[]; pending: PostPending[]; loading: boolean }>
|
||||
}
|
||||
|
||||
export interface PostStore extends Writable<PostData> {
|
||||
addPending: (post: Post, groupId: string) => void
|
||||
addPending: (post: PostPending, groupId: string) => void
|
||||
addApproved: (post: Post, groupId: string) => void
|
||||
}
|
||||
|
||||
@ -27,7 +29,7 @@ function createPostStore(): PostStore {
|
||||
|
||||
return {
|
||||
...store,
|
||||
addPending: (post: Post, groupId: string) => {
|
||||
addPending: (post: PostPending, groupId: string) => {
|
||||
store.update(({ data }) => {
|
||||
const personaPostData = data.get(groupId)
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { DEFAULT_GO_AMOUNT } from '$lib/constants'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
|
||||
export interface TokenData {
|
||||
@ -15,8 +14,8 @@ export type TokenStore = Writable<TokenData>
|
||||
function createTokenStore(): TokenStore {
|
||||
const epochDuration = 8 * 60 * 60 * 1000
|
||||
const store = writable<TokenData>({
|
||||
go: DEFAULT_GO_AMOUNT,
|
||||
repTotal: 55,
|
||||
go: 0,
|
||||
repTotal: 0,
|
||||
repStaked: 0,
|
||||
loading: false,
|
||||
epochDuration,
|
||||
|
@ -1,13 +1,6 @@
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
|
||||
type TransactionType =
|
||||
| 'publish persona'
|
||||
| 'promote'
|
||||
| 'demote'
|
||||
| 'publish post'
|
||||
| 'vote_win'
|
||||
| 'post_included'
|
||||
| 'post_rejected'
|
||||
export type TransactionType = 'publish persona' | 'promote' | 'demote' | 'publish post'
|
||||
|
||||
export interface TransactionRecord {
|
||||
timestamp: number
|
||||
|
@ -206,7 +206,7 @@
|
||||
on:click={adapter.signIn}
|
||||
disabled={!canConnectWallet()}
|
||||
/>
|
||||
{:else if $profile.signer !== undefined && $profile.address !== post.address}
|
||||
{:else if $profile.signer !== undefined && !post.myPost}
|
||||
<Button variant="primary" label="Chat with poster" icon={ChatBot} on:click={startChat} />
|
||||
{/if}
|
||||
</Post>
|
||||
|
@ -85,7 +85,7 @@
|
||||
on:click={adapter.signIn}
|
||||
disabled={!canConnectWallet()}
|
||||
/>
|
||||
{:else if $profile.signer !== undefined && $profile.address !== post.address}
|
||||
{:else if $profile.signer !== undefined && !post.myPost}
|
||||
<Button variant="primary" label="Chat with poster" icon={ChatBot} on:click={startChat} />
|
||||
{/if}
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user