move LeftMenu to its own component, remove the notifications thing from before as it wasn't good enough, pre-step before saving events to ensure we don't save old metadata from people.

This commit is contained in:
fiatjaf 2021-12-20 09:13:01 -03:00
parent 02e2fb8217
commit c79859437e
10 changed files with 165 additions and 134 deletions

View File

@ -1,9 +1,11 @@
import LeftMenu from '../components/LeftMenu.vue'
import Publish from '../components/Publish.vue' import Publish from '../components/Publish.vue'
import Balloon from '../components/Balloon.vue' import Balloon from '../components/Balloon.vue'
import Reply from '../components/Reply.vue' import Reply from '../components/Reply.vue'
import Post from '../components/Post.vue' import Post from '../components/Post.vue'
export default ({app}) => { export default ({app}) => {
app.component('LeftMenu', LeftMenu)
app.component('Publish', Publish) app.component('Publish', Publish)
app.component('Balloon', Balloon) app.component('Balloon', Balloon)
app.component('Reply', Reply) app.component('Reply', Reply)

118
src/components/LeftMenu.vue Normal file
View File

@ -0,0 +1,118 @@
<template>
<q-dialog v-model="dialogPublish">
<Publish />
</q-dialog>
<div class="hidden sm:flex w-1/4 justify-center px-8">
<q-card flat no-box-shadow class="text-xl">
<q-card-section class="flex justify-center">
<q-img src="/bird.png" fit="scale-down" width="80px" />
</q-card-section>
<q-list class="text-slate-700">
<q-item v-ripple clickable :to="'/'" active-class="">
<q-item-section avatar>
<q-icon name="home" color="secondary" />
</q-item-section>
<q-item-section
:class="{
'text-primary': $route.name === 'home'
}"
>
Home
</q-item-section>
</q-item>
<q-item v-ripple clickable :to="'/notifications'" active-class="">
<q-item-section avatar>
<q-icon name="notifications" color="secondary" />
</q-item-section>
<q-item-section
:class="{'text-primary': $route.name === 'notifications'}"
>
Notifications
<q-badge
v-if="notifications.length"
color="primary"
floating
transparent
>
{{ notifications.length }}
</q-badge>
</q-item-section>
</q-item>
<q-item v-ripple clickable :to="'/messages'" active-class="">
<q-item-section avatar>
<q-icon name="email" color="secondary" />
</q-item-section>
<q-item-section :class="{'text-primary': $route.name === 'messages'}">
Messages
</q-item-section>
</q-item>
<q-item
v-ripple
clickable
:to="'/' + $store.state.keys.pub"
active-class=""
>
<q-item-section avatar>
<q-icon name="person" color="secondary" />
</q-item-section>
<q-item-section
:class="{
'text-primary':
$route.name === 'profile' &&
$route.params.pubkey === $store.state.keys.pub
}"
>
Profile
</q-item-section>
</q-item>
<q-item v-ripple clickable :to="'/settings'" active-class="">
<q-item-section avatar>
<q-icon name="settings" color="secondary" />
</q-item-section>
<q-item-section :class="{'text-primary': $route.name === 'settings'}">
Settings
</q-item-section>
</q-item>
</q-list>
<div class="flex justify-center">
<q-btn
rounded
unelevated
color="primary"
size="md"
label="Publish"
class="mx-2 my-4 py-2 px-4 w-full"
@click="dialogPublish = true"
></q-btn>
</div>
</q-card>
</div>
</template>
<script>
import helpersMixin from '../utils/mixin'
export default {
name: 'LeftMenu',
mixins: [helpersMixin],
data() {
return {
notifications: [],
dialogPublish: false
}
}
}
</script>

View File

@ -35,7 +35,10 @@
</span> </span>
</div> </div>
</div> </div>
<div class="text-slate-500 cursor-pointer hover:underline text-xs"> <div
class="text-slate-500 cursor-pointer hover:underline text-xs"
@click="toEvent(event.id)"
>
{{ niceDate(event.created_at) }} {{ niceDate(event.created_at) }}
</div> </div>
</q-item-label> </q-item-label>

View File

@ -5,7 +5,7 @@
<q-input v-model="text" dense label="Say something" maxlength="280"> <q-input v-model="text" dense label="Say something" maxlength="280">
<template #before> <template #before>
<q-btn round class="mr-3" @click="toProfile($store.state.keys.pub)"> <q-btn round class="mr-3" @click="toProfile($store.state.keys.pub)">
<q-avatar rounded size="42px"> <q-avatar round size="42px">
<img :src="$store.getters.avatar($store.state.keys.pub)" /> <img :src="$store.getters.avatar($store.state.keys.pub)" />
</q-avatar> </q-avatar>
</q-btn> </q-btn>

View File

@ -68,6 +68,39 @@ db.upsert('_design/main', current => {
db.viewCleanup().then(r => console.log('view cleanup done', r)) db.viewCleanup().then(r => console.log('view cleanup done', r))
}) })
// general function for saving an event, with granular logic for each kind
//
export async function dbSave(event) {
switch (event.kind) {
case 0: {
// first check if we don't have a newer metadata for this user
let current = await dbGetProfile(event.pubkey)
if (current.created_at >= event.created_at) {
return
}
break
}
case 1:
break
case 2:
break
case 3:
break
case 4:
break
}
event._id = event.id
try {
await db.put(event)
} catch (err) {
if (err.name !== 'conflict') {
console.error('unexpected error saving event', event, err)
}
}
}
// db queries // db queries
// ~ // ~
export async function dbGetHomeFeedNotes( export async function dbGetHomeFeedNotes(
@ -202,12 +235,7 @@ export async function dbGetProfile(pubkey) {
sorted sorted
.slice(1) .slice(1)
.filter(row => row.doc) .filter(row => row.doc)
.forEach(row => .forEach(row => db.remove(row.doc))
db
.remove(row.doc)
.then(x => console.log('deleted', x))
.catch(e => console.log('failed to delete', e))
)
return sorted[0].doc return sorted[0].doc
} }
} }

View File

@ -1,110 +1,7 @@
<template> <template>
<q-layout> <q-layout>
<q-dialog v-model="dialogPublish">
<Publish />
</q-dialog>
<div class="flex"> <div class="flex">
<div class="hidden sm:flex w-1/4 justify-center px-8"> <LeftMenu />
<q-card flat no-box-shadow class="text-xl">
<q-card-section class="flex justify-center">
<q-img src="/bird.png" fit="scale-down" width="80px" />
</q-card-section>
<q-list class="text-slate-700">
<q-item v-ripple clickable :to="'/'" active-class="">
<q-item-section avatar>
<q-icon name="home" color="secondary" />
</q-item-section>
<q-item-section
:class="{
'text-primary': $route.name === 'home'
}"
>
Home
</q-item-section>
</q-item>
<q-item v-ripple clickable :to="'/notifications'" active-class="">
<q-item-section avatar>
<q-icon name="notifications" color="secondary" />
</q-item-section>
<q-item-section
:class="{'text-primary': $route.name === 'notifications'}"
>
Notifications
<q-badge
v-if="Object.keys($store.state.notifications).length"
color="primary"
floating
transparent
>
{{ Object.keys($store.state.notifications).length }}
</q-badge>
</q-item-section>
</q-item>
<q-item v-ripple clickable :to="'/messages'" active-class="">
<q-item-section avatar>
<q-icon name="email" color="secondary" />
</q-item-section>
<q-item-section
:class="{'text-primary': $route.name === 'messages'}"
>
Messages
</q-item-section>
</q-item>
<q-item
v-ripple
clickable
:to="'/' + $store.state.keys.pub"
active-class=""
>
<q-item-section avatar>
<q-icon name="person" color="secondary" />
</q-item-section>
<q-item-section
:class="{
'text-primary':
$route.name === 'profile' &&
$route.params.pubkey === $store.state.keys.pub
}"
>
Profile
</q-item-section>
</q-item>
<q-item v-ripple clickable :to="'/settings'" active-class="">
<q-item-section avatar>
<q-icon name="settings" color="secondary" />
</q-item-section>
<q-item-section
:class="{'text-primary': $route.name === 'settings'}"
>
Settings
</q-item-section>
</q-item>
</q-list>
<div class="flex justify-center">
<q-btn
rounded
unelevated
color="primary"
size="md"
label="Publish"
class="mx-2 my-4 py-2 px-4 w-full"
@click="dialogPublish = true"
></q-btn>
</div>
</q-card>
</div>
<div class="w-full sm:w-3/4 lg:w-2/4 pl-4"> <div class="w-full sm:w-3/4 lg:w-2/4 pl-4">
<q-page-container> <q-page-container>
@ -173,8 +70,6 @@ export default {
data() { data() {
return { return {
dialogGenerate: false,
dialogPublish: false,
searchingProfile: '' searchingProfile: ''
} }
}, },

View File

@ -5,7 +5,7 @@
</div> </div>
<div class="flex justify-left items-center mt-4"> <div class="flex justify-left items-center mt-4">
<q-avatar rounded> <q-avatar round>
<img :src="$store.getters.avatar($route.params.pubkey)" /> <img :src="$store.getters.avatar($route.params.pubkey)" />
</q-avatar> </q-avatar>
<div class="ml-4"> <div class="ml-4">

View File

@ -2,7 +2,7 @@ import {encrypt, decrypt} from 'nostr-tools/nip04'
import {Notify} from 'quasar' import {Notify} from 'quasar'
import {pool} from '../pool' import {pool} from '../pool'
import {db, dbGetProfile} from '../db' import {dbSave, dbGetProfile} from '../db'
export function launch(store) { export function launch(store) {
if (!store.state.keys.pub) { if (!store.state.keys.pub) {
@ -177,24 +177,15 @@ export async function sendChatMessage(store, {pubkey, text, replyTo}) {
} }
export async function addEvent(store, event) { export async function addEvent(store, event) {
event._id = event.id await dbSave(event)
db.put(event).catch(err => {
if (err.name === 'conflict') return
console.error(err)
})
// do things after the event is saved
switch (event.kind) { switch (event.kind) {
case 0: case 0:
// this will reset the profile cache for this URL // this will reset the profile cache for this URL
store.dispatch('useProfile', event.pubkey) store.dispatch('useProfile', event.pubkey)
break break
case 1: case 1:
if (
event.tags.find(([t, v]) => t === 'p' && v === store.state.keys.pub)
) {
store.dispatch('addNotification', event)
}
break break
case 2: case 2:
break break

View File

@ -92,7 +92,3 @@ export function addProfileToCache(state, event) {
state.me = JSON.parse(event.content) state.me = JSON.parse(event.content)
} }
} }
export function addNotification(state, event) {
state.notifications[event.id] = true
}

View File

@ -8,8 +8,6 @@ export default function () {
following: LocalStorage.getItem('following') || [], // [ pubkeys... ] following: LocalStorage.getItem('following') || [], // [ pubkeys... ]
profilesCache: {}, // { [pubkey]: {name, about, picture, ...} } profilesCache: {}, // { [pubkey]: {name, about, picture, ...} }
profilesCacheLRU: [], // [ pubkeys... ] profilesCacheLRU: [] // [ pubkeys... ]
notifications: {}
} }
} }