add: support for new notifs

This commit is contained in:
Andrei O 2023-04-13 03:32:37 +03:00
parent bb8e894f8e
commit 93879ecdaa
No known key found for this signature in database
GPG Key ID: B961E5B68389457E
7 changed files with 235 additions and 120 deletions

View File

@ -1,5 +1,11 @@
# Change Log # Change Log
## [Version 1.0.6]
- refactored notifications
- added support for aggregated notifications
- added support for follow notification
## [Version 1.0.5] ## [Version 1.0.5]
- added: `setting` to enable right click page context like at user suggestion - added: `setting` to enable right click page context like at user suggestion

View File

@ -1,7 +1,7 @@
{ {
"name": "yup live", "name": "yup live",
"description": "Light alternative extension for yup protocol", "description": "Light alternative extension for yup protocol",
"version": "1.0.5", "version": "1.0.6",
"manifest_version": 3, "manifest_version": 3,
"icons": { "icons": {
"16": "src/assets/icons/yup_ext_16.png", "16": "src/assets/icons/yup_ext_16.png",

View File

@ -1,93 +1,202 @@
<script lang="ts"> <script lang="ts">
import { timeSince } from '@/utils/time'; import { timeSince } from "@/utils/time";
import ImgLoader from './ImgLoader.svelte'; import ImgLoader from "./ImgLoader.svelte";
import type { Notification } from '@/utils/types'; import type { Notification } from "@/utils/types";
import { mainStore } from '@/utils/store'; import { mainStore } from "@/utils/store";
import { chromeUrl } from '@/utils/chrome-misc'; import { chromeUrl } from "@/utils/chrome-misc";
import { extrenalNavigate } from "@/utils/chrome-misc"; import { extrenalNavigate } from "@/utils/chrome-misc";
let loader let loader;
export let notif: Notification export let notif: Notification;
</script> </script>
{#if notif.action === 'vote'} {#if notif.action === "vote"}
{@const url = notif.post.url } {@const url = notif.post.url}
{@const length = url.length} {@const length = url.length}
{@const shortUrl = url.slice(0, 10) + '...' + url.slice(length - 10, length) } {@const shortUrl = url.slice(0, 10) + "..." + url.slice(length - 10, length)}
{@const finalUrl = length > 24 ? shortUrl : url } {@const finalUrl = length > 24 ? shortUrl : url}
<div class="flex flex-row notifBody"> <div class="flex flex-row notifBody">
<ImgLoader bind:this={loader} source="{notif.image} "> <ImgLoader bind:this={loader} source="{notif.image} ">
<img class="notificationImage" on:load="{() => loader.onLoad()}" on:error={() => loader.onError()} style="{ $mainStore.settings.theme === 'light'? 'filter: invert(0.9);' : '' }" slot="img" src="{notif.image}" alt="preview"> <img
<svg class="notificationImage" style="{ $mainStore.settings.theme === 'light'? 'filter: invert(0.9);' : '' }" slot="error" viewBox="0 0 512 512"><g><polygon points="40,38.999 40,468.998 215,293.998 270,348.998 360,258.998 470,358.998 470,38.999 " style="fill:#EFF3F6;"/> class="notificationImage"
<g><circle cx="150" cy="158.999" r="40" style="fill:#FCD884;"/></g><g><polygon points="470,358.998 470,468.998 385,468.998 385,463.998 270,348.998 360,258.998 " style="fill:#70993F;"/></g><g><polygon points="385,463.998 385,468.998 40,468.998 215,293.998 270,348.998" style="fill:#80AF52;"/></g></g><g/></svg> on:load={() => loader.onLoad()}
</ImgLoader> on:error={() => loader.onError()}
<div class="ml-4 text-left" style="width: 97%"> style={$mainStore.settings.theme === "light" ? "filter: invert(0.9);" : ""}
<p class="text-xs text-gray-200 my-0 mt-1"> slot="img"
{#if notif.like} src={notif.image}
<svg class="w-4 like-dsilike" viewBox="0 0 24 24"> alt="preview"
<path fill="#fff" d="M12,3.172L5.586,9.586c-0.781,0.781-0.781,2.047,0,2.828s2.047,0.781,2.828,0L10,10.828v7.242c0,1.104,0.895,2,2,2 c1.104,0,2-0.896,2-2v-7.242l1.586,1.586C15.977,12.805,16.488,13,17,13s1.023-0.195,1.414-0.586c0.781-0.781,0.781-2.047,0-2.828 L12,3.172z"/> />
</svg> <svg
{:else} class="notificationImage"
<svg class="w-4 down like-dsilike" viewBox="0 0 24 24"> style={$mainStore.settings.theme === "light" ? "filter: invert(0.9);" : ""}
<path fill="#fff" d="M12,3.172L5.586,9.586c-0.781,0.781-0.781,2.047,0,2.828s2.047,0.781,2.828,0L10,10.828v7.242c0,1.104,0.895,2,2,2 c1.104,0,2-0.896,2-2v-7.242l1.586,1.586C15.977,12.805,16.488,13,17,13s1.023-0.195,1.414-0.586c0.781-0.781,0.781-2.047,0-2.828 L12,3.172z"/> slot="error"
</svg> viewBox="0 0 512 512"
{/if} ><g
by {notif.voter.length > 12 ? notif.voter.slice(0, 12) + '...' : notif.voter} ><polygon
</p> points="40,38.999 40,468.998 215,293.998 270,348.998 360,258.998 470,358.998 470,38.999 "
<p class="text-xs text-gray-200 my-0 mt-1"> style="fill:#EFF3F6;"
<span on:click={() => extrenalNavigate(`https://yup-live.pages.dev/post/${notif.postid}`)} />
aria-hidden <g><circle cx="150" cy="158.999" r="40" style="fill:#FCD884;" /></g><g
class="text-blue-200 interactive-svg">{finalUrl}</span> ><polygon
</p> points="470,358.998 470,468.998 385,468.998 385,463.998 270,348.998 360,258.998 "
<p class="text-xs text-gray-200 my-0 my-1 text-right mr-2"> style="fill:#70993F;"
<svg class="w-3 inline" viewBox="0 0 20 20" ><path fill="#fff" d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"/></svg> /></g
{timeSince(new Date(notif.createdAt))}</p> ><g><polygon points="385,463.998 385,468.998 40,468.998 215,293.998 270,348.998" style="fill:#80AF52;" /></g
</div> ></g
</div> ><g /></svg
{:else if notif.action === 'reward'} >
<div class="flex flex-row notifBody"> </ImgLoader>
<img class="notificationImage" src="{chromeUrl('src/assets/res/reward_optimized.png')}" alt="reward"> <div class="ml-4 text-left" style="width: 97%">
<div class="ml-4 text-left" style="width: 97%"> <p class="text-xs text-gray-200 my-0 mt-1">
<p class="text-xs text-gray-200 my-0 mt-1">You were alocated a future reward of {notif?.quantity ?? 'unknown'} amount of YUP. {#if notif.like}
</p> <svg class="w-4 like-dislike" viewBox="0 0 24 24">
<p class="text-xs text-gray-500 my-0 my-1 text-right mr-2"> <path
<svg class="w-3 inline" viewBox="0 0 20 20" ><path fill="#fff" d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"/></svg> fill="#fff"
{timeSince(new Date(notif.createdAt))}</p> d="M12,3.172L5.586,9.586c-0.781,0.781-0.781,2.047,0,2.828s2.047,0.781,2.828,0L10,10.828v7.242c0,1.104,0.895,2,2,2 c1.104,0,2-0.896,2-2v-7.242l1.586,1.586C15.977,12.805,16.488,13,17,13s1.023-0.195,1.414-0.586c0.781-0.781,0.781-2.047,0-2.828 L12,3.172z"
</div> />
</div> </svg>
{:else } {:else}
<div class="flex flex-row items-center notifBody"> <svg class="w-4 down like-dislike" viewBox="0 0 24 24">
<div class="ml-4 text-left" style="width: 97%"> <path
<p class="text-xs text-gray-200 my-0 mt-1">{notif?.message ?? 'unknown notification type'}</p> fill="#fff"
<p class="text-xs text-gray-500 my-0 my-1 text-right mr-2"> d="M12,3.172L5.586,9.586c-0.781,0.781-0.781,2.047,0,2.828s2.047,0.781,2.828,0L10,10.828v7.242c0,1.104,0.895,2,2,2 c1.104,0,2-0.896,2-2v-7.242l1.586,1.586C15.977,12.805,16.488,13,17,13s1.023-0.195,1.414-0.586c0.781-0.781,0.781-2.047,0-2.828 L12,3.172z"
<svg viewBox="0 0 20 20" ><path fill="#fff" d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"/></svg> />
{timeSince(new Date(notif.createdAt))}</p> </svg>
</div>
</div>
{/if} {/if}
by
{#if notif?.notifications?.length > 1}
{notif?.notifications[0].VoterHandle}
{#if notif.notifications.length - 1 > 0}
<span class="opacity-60"> and {notif.notifications.length - 1} more</span>
{/if}
{:else}
{notif.voter.length > 12 ? notif.voter.slice(0, 12) + "..." : notif.voter}
{/if}
</p>
<p class="text-xs text-gray-200 my-0 mt-1">
<span
on:click={() => extrenalNavigate(`https://yup-live.pages.dev/post/${notif.postid}`)}
aria-hidden
class="text-blue-200 interactive-svg">{finalUrl}</span
>
</p>
<p class="text-xs text-gray-200 my-0 my-1 text-right mr-2">
<svg class="w-3 inline" viewBox="0 0 20 20"
><path
fill="#fff"
d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"
/></svg
>
{timeSince(new Date(notif.createdAt))}
</p>
</div>
</div>
{:else if notif.action === "reward"}
<div class="flex flex-row notifBody">
<img class="notificationImage" src={chromeUrl("src/assets/res/reward_optimized.png")} alt="reward" />
<div class="ml-4 text-left" style="width: 97%">
<p class="text-xs text-gray-200 my-0 mt-1">
You were alocated a future reward of {notif?.quantity ?? "unknown"} amount of YUP.
</p>
<p class="text-xs text-gray-500 my-0 my-1 text-right mr-2">
<svg class="w-3 inline" viewBox="0 0 20 20"
><path
fill="#fff"
d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"
/></svg
>
{timeSince(new Date(notif.createdAt))}
</p>
</div>
</div>
{:else if ["follow", "unfollow"].includes(notif.action)}
<div class="flex flex-row notifBody">
<ImgLoader bind:this={loader} source="{notif?.EVMRecipient?.avatar} ">
<img
class="notificationImage"
on:load={() => loader.onLoad()}
on:error={() => loader.onError()}
style={$mainStore.settings.theme === "light" ? "filter: invert(0.9);" : ""}
slot="img"
src={notif.image}
alt="preview"
/>
<svg
class="notificationImage"
style={$mainStore.settings.theme === "light" ? "filter: invert(0.9);" : ""}
slot="error"
viewBox="0 0 512 512"
><g
><polygon
points="40,38.999 40,468.998 215,293.998 270,348.998 360,258.998 470,358.998 470,38.999 "
style="fill:#EFF3F6;"
/>
<g><circle cx="150" cy="158.999" r="40" style="fill:#FCD884;" /></g><g
><polygon
points="470,358.998 470,468.998 385,468.998 385,463.998 270,348.998 360,258.998 "
style="fill:#70993F;"
/></g
><g><polygon points="385,463.998 385,468.998 40,468.998 215,293.998 270,348.998" style="fill:#80AF52;" /></g
></g
><g /></svg
>
</ImgLoader>
<div class="ml-4 text-left" style="width: 97%">
<p
aria-hidden
class="text-xs text-gray-200 my-0 mt-1"
on:click={() => extrenalNavigate(`https://yup-live.pages.dev/web3-profile/${notif.EVMRecipient?.address}`)}
>
<b>{notif?.EVMRecipient?.handle || `${notif.EVMRecipient?.address?.slice(0, 6)}...`}</b>
{notif.action === "follow" ? "followed" : "unfollowed"} you.
</p>
<p class="text-xs text-gray-200 my-0 my-1 text-right mr-2">
<svg class="w-3 inline" viewBox="0 0 20 20"
><path
fill="#fff"
d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"
/></svg
>
{timeSince(new Date(notif.createdAt))}
</p>
</div>
</div>
{:else}
<div class="flex flex-row items-center notifBody">
<div class="ml-4 text-left" style="width: 97%">
<p class="text-xs text-gray-200 my-0 mt-1">{notif?.message ?? "unknown notification type"}</p>
<p class="text-xs text-gray-500 my-0 my-1 text-right mr-2">
<svg class="w-3 inline" viewBox="0 0 20 20"
><path
fill="#fff"
d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"
/></svg
>
{timeSince(new Date(notif.createdAt))}
</p>
</div>
</div>
{/if}
<style class="scss"> <style class="scss">
.notificationImage { .notificationImage {
width: 50px; width: 50px;
height: 50px; height: 50px;
border-radius: 50%; border-radius: 50%;
object-fit: cover; object-fit: cover;
border-radius: 6px; border-radius: 6px;
margin-left: 0.5rem; margin-left: 0.5rem;
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.notifBody { .notifBody {
background: #00000061; background: #00000061;
border: 1px solid rgba(0, 0, 0, 0.938); border: 1px solid rgba(0, 0, 0, 0.938);
} }
.like-dsilike { .like-dislike {
position: relative; position: relative;
top: 0.2rem; top: 0.2rem;
} }
</style>
</style>

View File

@ -12,7 +12,7 @@ let loading = true;
let noNotifications = false; let noNotifications = false;
let notifs = []; let notifs = [];
let pastNotifsPromise let pastNotifsPromise
let type = 'all' let type = null
onMount(async () => { onMount(async () => {
notifs = await getNotifications({ notifs = await getNotifications({
@ -35,13 +35,9 @@ onMount(async () => {
}); });
const changeNotifsType = async (t :string) => { const changeNotifsType = async (t : string[] | null) => {
if(type === t) return; if(String(type) === String(t)) return;
if(!['all', 'rewards'].includes(t)){ type = t
console.error('Invalid type');
return;
}
type = t;
loading = true; loading = true;
notifs = await getNotifications({ notifs = await getNotifications({
userId: $mainStore.user.auth.userId, userId: $mainStore.user.auth.userId,
@ -71,8 +67,8 @@ const changeNotifsType = async (t :string) => {
{/await} {/await}
{:else} {:else}
<div class="text-[0.75rem] py-1"> <div class="text-[0.75rem] py-1">
<span on:click={() => changeNotifsType('all')} aria-hidden class="inline-block mr-2 interactive-svg text-blue-200 interactive-svg" >All</span> <span on:click={() => changeNotifsType(null)} aria-hidden class="inline-block mr-2 interactive-svg text-blue-200 interactive-svg" >All</span>
<span on:click={() => changeNotifsType('rewards')} aria-hidden class="text-blue-200 interactive-svg interactive-svg text-blue-200 interactive-svg">Rewards</span> <span on:click={() => changeNotifsType(['reward'])} aria-hidden class="text-blue-200 interactive-svg interactive-svg text-blue-200 interactive-svg">Rewards</span>
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
{#each notifs.reverse() as notif} {#each notifs.reverse() as notif}

View File

@ -3,33 +3,30 @@ import type { StorageType } from '@/utils/storage'
import { fetchWAuth } from '@/utils/auth' import { fetchWAuth } from '@/utils/auth'
import { wait } from '@/utils/time' import { wait } from '@/utils/time'
export const notificationTypes = ['vote', 'reward', 'all-followers', 'follow', 'unfollow']
export const getNotifications = async ( export const getNotifications = async (
{ type, limit, skip, userId } = { type: 'all', limit: '10', skip: '0' } as { userId: string, type: string; limit?: string; skip?: string } { type, limit, skip, userId } = { type: null, limit: '10', skip: '0' } as { userId: string, type: null | string[]; limit?: string; skip?: string }
) => { ) => {
let req let req
let queryType = ''
if (type === 'all') { if (type) {
req = await fetch(`${API_BASE}/notifications/${userId}?skip=${skip}&limit=${limit}`, { queryType = `&inType=${type.join(',')}`
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
} else if (type === 'vote') {
req = await fetch(`${API_BASE}/notifications/${userId}?skip=${skip}&limit=${limit}&type=vote`, {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
} else {
req = await fetch(`${API_BASE}/notifications/${userId}?skip=${skip}&limit=${limit}&type=reward`, {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
} }
if (!limit) {
limit = '10'
}
if (!skip) {
skip = '0'
}
req = await fetch(`${API_BASE}/notifications/${userId}?skip=${skip}&limit=${limit}${queryType}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
})
if (!req.ok) { if (!req.ok) {
return false return false

View File

@ -27,6 +27,12 @@ export interface Vote {
title: string title: string
tag: string tag: string
} }
EVMRecipient: {
handle: string
address: string
avatar: string
}
notifications: Notification[]
seen: boolean seen: boolean
postid: string postid: string
rating: number rating: number
@ -35,4 +41,5 @@ export interface Vote {
createdAt: string createdAt: string
quantity?: string quantity?: string
message?: string message?: string
VoterHandle: string
} }

View File

@ -67,7 +67,7 @@ export const executeVote = async ({
body: JSON.stringify(body) body: JSON.stringify(body)
}) })
if (req.ok) { if (req.ok) {
noVoteAlert || $alertStore?.show('Rating submited!') noVoteAlert || $alertStore?.show('Rating submitted!')
return await req.json() return await req.json()
} else { } else {
const err = await req.text() const err = await req.text()
@ -76,9 +76,9 @@ export const executeVote = async ({
} else if(err.includes('requests')) { } else if(err.includes('requests')) {
$alertStore?.show('You have made too many request try again after 24h', 'warning') $alertStore?.show('You have made too many request try again after 24h', 'warning')
} else if(err.toLocaleLowerCase().includes('unauthorized')) { } else if(err.toLocaleLowerCase().includes('unauthorized')) {
$alertStore?.show('Seems your auth token is not valid anymore re-login!!', 'error') $alertStore?.show('Seems your auth token has expired re-login!!', 'error')
} else { } else {
$alertStore?.show('Vote not submited due to error try to re-login!', 'error') $alertStore?.show('Rating not submitted due to error try to re-login!', 'error')
} }
return null return null
} }