another huge commit but we have notifs now yay
This commit is contained in:
parent
015a3e65e1
commit
998e8f2517
17 changed files with 442 additions and 52 deletions
|
@ -1,6 +1,8 @@
|
|||
<script>
|
||||
import { play_sound } from '../sound.js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { afterUpdate } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let active = false;
|
||||
|
@ -12,10 +14,6 @@
|
|||
export let href = false;
|
||||
|
||||
let classes = [];
|
||||
if (active) classes = ["active"];
|
||||
if (filled) classes = ["filled"];
|
||||
if (disabled) classes = ["disabled"];
|
||||
if (centered) classes.push("centered");
|
||||
|
||||
function click() {
|
||||
if (disabled) return;
|
||||
|
@ -26,6 +24,14 @@
|
|||
play_sound(sound);
|
||||
dispatch('click');
|
||||
}
|
||||
|
||||
afterUpdate(() => {
|
||||
classes = [];
|
||||
if (active) classes = ["active"];
|
||||
if (filled) classes = ["filled"];
|
||||
if (disabled) classes = ["disabled"];
|
||||
if (centered) classes.push("centered");
|
||||
});
|
||||
</script>
|
||||
|
||||
<button
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
<script>
|
||||
import Button from './Button.svelte';
|
||||
import Post from './post/Post.svelte';
|
||||
import { posts, getTimeline } from '$lib/timeline.js';
|
||||
import { getTimeline } from '$lib/timeline.js';
|
||||
|
||||
getTimeline();
|
||||
document.addEventListener("scroll", event => {
|
||||
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 2048) {
|
||||
getTimeline();
|
||||
}
|
||||
});
|
||||
export let posts = [];
|
||||
</script>
|
||||
|
||||
<header>
|
||||
|
@ -26,7 +21,7 @@
|
|||
<span>getting the feed...</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#each $posts as post}
|
||||
{#each posts as post}
|
||||
<Post post_data={post} />
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -34,6 +29,7 @@
|
|||
<style>
|
||||
header {
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
margin: 16px 0 8px 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
|
@ -5,9 +5,12 @@
|
|||
import { client } from '$lib/client/client.js';
|
||||
import { play_sound } from '$lib/sound.js';
|
||||
import { getTimeline } from '$lib/timeline.js';
|
||||
import { getNotifications } from '$lib/notifications.js';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { get } from 'svelte/store';
|
||||
import { onMount } from 'svelte';
|
||||
import { logged_in } from '$lib/stores/user.js';
|
||||
import { unread_notif_count, last_read_notif_id } from '$lib/notifications.js';
|
||||
|
||||
import TimelineIcon from '../../img/icons/timeline.svg';
|
||||
import NotificationsIcon from '../../img/icons/notifications.svg';
|
||||
|
@ -21,27 +24,26 @@
|
|||
import SettingsIcon from '../../img/icons/settings.svg';
|
||||
import LogoutIcon from '../../img/icons/logout.svg';
|
||||
|
||||
export let path;
|
||||
|
||||
const VERSION = APP_VERSION;
|
||||
|
||||
let notification_count = 0;
|
||||
if (notification_count > 99) notification_count = "99+";
|
||||
|
||||
function handle_btn(name) {
|
||||
if (!get(logged_in)) return;
|
||||
let route;
|
||||
switch (name) {
|
||||
case "timeline":
|
||||
if (!get(client).user) break;
|
||||
route = "/";
|
||||
getTimeline(true);
|
||||
break;
|
||||
case "notifcations":
|
||||
case "notifications":
|
||||
route = "/notifications";
|
||||
getNotifications();
|
||||
break;
|
||||
case "explore":
|
||||
case "lists":
|
||||
case "favourites":
|
||||
case "bookmarks":
|
||||
case "hashtags":
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (!route) return;
|
||||
|
@ -66,11 +68,11 @@
|
|||
</div>
|
||||
</header>
|
||||
|
||||
{#if $logged_in}
|
||||
<div id="nav-items">
|
||||
<Button label="Timeline"
|
||||
on:click={() => handle_btn("timeline")}
|
||||
active={path == "/" && $client.user}
|
||||
disabled={!$client.user}>
|
||||
active={$page.url.pathname === "/"}>
|
||||
<svelte:fragment slot="icon">
|
||||
<TimelineIcon/>
|
||||
</svelte:fragment>
|
||||
|
@ -78,14 +80,15 @@
|
|||
</Button>
|
||||
<Button label="Notifications"
|
||||
on:click={() => handle_btn("notifications")}
|
||||
active={path == "/notifications"}
|
||||
disabled>
|
||||
active={$page.url.pathname === "/notifications"}>
|
||||
<svelte:fragment slot="icon">
|
||||
<NotificationsIcon/>
|
||||
</svelte:fragment>
|
||||
Notifications
|
||||
{#if notification_count}
|
||||
<span class="notification-count">{notification_count}</span>
|
||||
{#if $unread_notif_count}
|
||||
<span class="notification-count">
|
||||
{$unread_notif_count <= 99 ? $unread_notif_count : "99+"}
|
||||
</span>
|
||||
{/if}
|
||||
</Button>
|
||||
<Button label="Explore" disabled>
|
||||
|
@ -127,7 +130,6 @@
|
|||
</Button>
|
||||
</div>
|
||||
|
||||
{#if $client.user}
|
||||
<div id="account-items">
|
||||
<div class="flex-row">
|
||||
<Button centered label="Profile information" disabled>
|
||||
|
@ -222,6 +224,7 @@
|
|||
transform: translate(22px, -16px);
|
||||
min-width: 12px;
|
||||
height: 28px;
|
||||
margin-left: auto;
|
||||
padding: 0 8px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
227
src/lib/ui/Notification.svelte
Normal file
227
src/lib/ui/Notification.svelte
Normal file
|
@ -0,0 +1,227 @@
|
|||
<script>
|
||||
import * as api from '$lib/client/api.js';
|
||||
|
||||
import ReplyIcon from '$lib/../img/icons/reply.svg';
|
||||
import RepostIcon from '$lib/../img/icons/repost.svg';
|
||||
import FavouriteIcon from '$lib/../img/icons/like.svg';
|
||||
import ReactIcon from '$lib/../img/icons/react.svg';
|
||||
import QuoteIcon from '$lib/../img/icons/quote.svg';
|
||||
import ReactionBar from '$lib/ui/post/ReactionBar.svelte';
|
||||
import ActionBar from '$lib/ui/post/ActionBar.svelte';
|
||||
|
||||
let mention = (accounts) => {
|
||||
let res = `<a href=${account.url}>${account.rich_name}</a>`;
|
||||
if (accounts.length > 1) res += ` and <strong>${accounts.length - 1}</strong> others`;
|
||||
return res;
|
||||
};
|
||||
|
||||
export let data;
|
||||
let activity_text = function (type) {
|
||||
switch (type) {
|
||||
case "mention":
|
||||
return `%1 mentioned you.`;
|
||||
case "reblog":
|
||||
return `%1 boosted your post.`;
|
||||
case "follow":
|
||||
return `%1 followed you.`;
|
||||
case "follow_request":
|
||||
return `%1 requested to follow you.`;
|
||||
case "favourite":
|
||||
return `%1 favourited your post.`;
|
||||
case "poll":
|
||||
return `%1's poll as ended.`;
|
||||
case "update":
|
||||
return `%1 updated their post.`;
|
||||
default:
|
||||
return `%1 poked you!`;
|
||||
}
|
||||
}(data.type);
|
||||
|
||||
let account = data.accounts[0];
|
||||
$: accounts_short = data.accounts.slice(0, 3).reverse();
|
||||
|
||||
let aria_label = function () {
|
||||
if (accounts.length == 1)
|
||||
return activity_text.replace("%1", account.username) + ' ' + new Date(data.created_at);
|
||||
else
|
||||
return activity_text.replace("%1", `${account.username} and ${accounts.length - 1} others`) + ' ' + new Date(data.created_at);
|
||||
}
|
||||
</script>
|
||||
|
||||
<a class="notification" href={`/post/${data.status.id}`} aria-label={aria_label}>
|
||||
<header aria-hidden>
|
||||
<span class="notif-icon">
|
||||
{#if data.type === "favourite"}
|
||||
<FavouriteIcon />
|
||||
{:else if data.type === "reblog"}
|
||||
<RepostIcon />
|
||||
{:else if data.type === "react"}
|
||||
<ReactIcon />
|
||||
{:else if data.type === "mention"}
|
||||
<ReplyIcon />
|
||||
{:else}
|
||||
<ReactIcon />
|
||||
{/if}
|
||||
</span>
|
||||
<span class="notif-avatars">
|
||||
{#if data.accounts.length == 1}
|
||||
<a href={data.accounts[0].url} class="notif-avatar">
|
||||
<img src={data.accounts[0].avatar_url} alt="" width="28" height="28" />
|
||||
</a>
|
||||
{:else}
|
||||
{#each accounts_short as account}
|
||||
<img src={account.avatar_url} alt="" width="28" height="28" />
|
||||
{/each}
|
||||
{/if}
|
||||
</span>
|
||||
<span class="notif-activity">{@html activity_text.replace("%1", mention(data.accounts))}</span>
|
||||
</header>
|
||||
{#if data.status}
|
||||
<div class="notif-content">
|
||||
{@html data.status.html}
|
||||
</div>
|
||||
{#if data.type === "mention"}
|
||||
{#if data.status.reactions}
|
||||
<ReactionBar post={data.status} />
|
||||
{/if}
|
||||
<ActionBar post={data.status} />
|
||||
{/if}
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.notification {
|
||||
display: block;
|
||||
margin: 8px 0;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
background: var(--bg-800);
|
||||
text-decoration: inherit;
|
||||
color: inherit;
|
||||
transition: background-color .1s;
|
||||
}
|
||||
|
||||
.notification:hover {
|
||||
background-color: color-mix(in srgb, var(--bg-800), black 5%);
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
header .notif-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
header .notif-avatars {
|
||||
display: inline-flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
header .notif-avatar {
|
||||
line-height: 0;
|
||||
}
|
||||
header .notif-avatars img {
|
||||
border-radius: 4px;
|
||||
}
|
||||
header .notif-avatars img:not(:first-child) {
|
||||
box-shadow: 4px 0 8px -2px rgba(0,0,0,.33);
|
||||
}
|
||||
header .notif-avatars img:not(:last-child) {
|
||||
margin-left: -8px;
|
||||
}
|
||||
|
||||
header .notif-activity {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
header :global(a) {
|
||||
font-weight: bold;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
header :global(.emoji) {
|
||||
margin: -.2em 0;
|
||||
}
|
||||
|
||||
.notif-content {
|
||||
margin: 16px 0 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.45em;
|
||||
}
|
||||
|
||||
.notif-content :global(p) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.notif-content :global(.emoji) {
|
||||
position: relative;
|
||||
top: 6px;
|
||||
margin-top: -10px;
|
||||
height: 24px!important;
|
||||
}
|
||||
|
||||
.notif-content :global(blockquote) {
|
||||
margin: .4em 0;
|
||||
padding: .1em 0 .1em 1em;
|
||||
border-left: 4px solid #8888;
|
||||
}
|
||||
|
||||
.notif-content :global(blockquote span) {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.notif-content :global(code) {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.notif-content :global(pre:has(code)) {
|
||||
margin: 8px 0;
|
||||
padding: 8px;
|
||||
display: block;
|
||||
overflow-x: scroll;
|
||||
border-radius: 8px;
|
||||
background-color: #080808;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.notif-content :global(pre code) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.notif-content :global(a) {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.notif-content :global(a.mention) {
|
||||
color: inherit;
|
||||
font-weight: 600;
|
||||
padding: 3px 6px;
|
||||
background: var(--bg-700);
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.notif-content :global(a.mention:hover) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notif-content :global(a.hashtag) {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.notif-content :global(.mention-avatar) {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
height: 20px;
|
||||
margin-right: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
|
@ -84,7 +84,8 @@
|
|||
}
|
||||
|
||||
.post-text {
|
||||
line-height: 1.2em;
|
||||
font-size: .9em;
|
||||
line-height: 1.45em;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,10 +34,11 @@
|
|||
<style>
|
||||
.post-reactions {
|
||||
width: fit-content;
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue