campfire/src/lib/ui/Navigation.svelte

365 lines
10 KiB
Svelte
Raw Normal View History

<script>
import * as api from '$lib/api.js';
import { account } from '$lib/stores/account.js';
import { server } from '$lib/client/server.js';
import { app } from '$lib/client/app.js';
import { playSound } from '$lib/sound.js';
import { getTimeline } from '$lib/timeline.js';
import { getNotifications } from '$lib/notifications.js';
2024-06-29 16:19:34 +01:00
import { goto } from '$app/navigation';
import { page } from '$app/stores';
2024-07-04 16:55:57 +01:00
import { createEventDispatcher } from 'svelte';
import { notifications, unread_notif_count } from '$lib/notifications.js';
2025-07-13 18:49:49 +01:00
import Lang from '$lib/lang';
import Logo from '$lib/../img/campfire-logo.svg';
import Button from './Button.svelte';
2024-06-30 19:57:32 +01:00
import TimelineIcon from '../../img/icons/timeline.svg';
import NotificationsIcon from '../../img/icons/notifications.svg';
import ExploreIcon from '../../img/icons/explore.svg';
import ListIcon from '../../img/icons/lists.svg';
import FavouritesIcon from '../../img/icons/like_fill.svg';
import BookmarkIcon from '../../img/icons/bookmark.svg';
import HashtagIcon from '../../img/icons/hashtag.svg';
import PostIcon from '../../img/icons/post.svg';
import InfoIcon from '../../img/icons/info.svg';
import SettingsIcon from '../../img/icons/settings.svg';
import LogoutIcon from '../../img/icons/logout.svg';
const VERSION = APP_VERSION;
const lang = Lang('en_GB');
2024-07-04 16:55:57 +01:00
const dispatch = createEventDispatcher();
2025-07-14 00:19:42 +01:00
function gotoProfile() {
if (!$account) return;
playSound();
window.scrollTo({
top: 0,
behavior: "smooth"
});
goto(`/${$server.host}/${$account.username}`);
}
async function log_out() {
if (!confirm("This will log you out. Are you sure?")) return;
const res = await api.revokeToken(
$server.host,
$app.id,
$app.secret,
$app.token
);
if (!res.ok)
2025-07-13 20:44:54 +01:00
console.warn(lang.string('logs.token_revoke_failed'));
account.set(false);
app.set(false);
server.set(false);
goto("/");
}
</script>
<div id="navigation">
<header class="server-header">
<div class="app-logo">
<Logo />
</div>
</header>
{#if $account}
<div id="nav-items">
<Button label="Timeline"
href="/")}
active={$page.url.pathname === "/"}>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<TimelineIcon/>
</svelte:fragment>
{lang.string('navigation.timeline')}
2024-06-30 19:57:32 +01:00
</Button>
<Button label="Notifications"
href="notifications"}
active={$page.url.pathname === "/notifications"}>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<NotificationsIcon/>
</svelte:fragment>
{lang.string('navigation.notifications')}
{#if $unread_notif_count}
<span class="notification-count">
{$unread_notif_count <= 99 ? $unread_notif_count : "99+"}
</span>
{/if}
</Button>
2024-06-30 19:57:32 +01:00
<Button label="Explore" disabled>
<svelte:fragment slot="icon">
<ExploreIcon height="auto"/>
</svelte:fragment>
{lang.string('navigation.explore')}
2024-06-30 19:57:32 +01:00
</Button>
<Button label="Lists" disabled>
<svelte:fragment slot="icon">
<ListIcon/>
</svelte:fragment>
{lang.string('navigation.lists')}
2024-06-30 19:57:32 +01:00
</Button>
<div class="flex-row">
<Button centered label="{lang.string('navigation.favourites')}" disabled>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<FavouritesIcon/>
</svelte:fragment>
</Button>
<Button centered label="{lang.string('navigation.bookmarks')}" disabled>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<BookmarkIcon/>
</svelte:fragment>
</Button>
<Button centered label="{lang.string('navigation.hashtags')}" disabled>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<HashtagIcon/>
</svelte:fragment>
</Button>
</div>
<Button filled label="{lang.string('compose')}" on:click={() => dispatch("compose")}>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<PostIcon/>
</svelte:fragment>
{lang.string('compose')}
2024-07-04 16:55:57 +01:00
</Button>
</div>
<div id="account-items">
<div class="flex-row">
<Button centered label="{lang.string('navigation.profile_information')}" disabled>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<InfoIcon/>
</svelte:fragment>
</Button>
<Button centered label="{lang.string('navigation.settings')}" disabled>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<SettingsIcon/>
</svelte:fragment>
</Button>
<Button centered label="{lang.string('navigation.log_out')}" on:click={() => log_out()}>
2024-06-30 19:57:32 +01:00
<svelte:fragment slot="icon">
<LogoutIcon/>
</svelte:fragment>
</Button>
</div>
<div id="account-button">
2025-07-14 00:19:42 +01:00
<img src={$account.avatar_url} class="account-avatar" height="64px" alt="" aria-hidden="true" on:click={() => gotoProfile()}>
<div class="account-name" aria-hidden="true">
<a href="/{$server.host}/@{$account.username}" class="nickname" title={$account.nickname}>{@html $account.rich_name}</a>
<span class="username" title={`@${$account.username}@${$account.host}`}>
{$account.fqn}
</span>
</div>
</div>
</div>
{/if}
<span class="version">
2024-06-30 17:37:19 +01:00
campfire v{VERSION}
<br>
<ul>
<li><a href="https://git.arimelody.me/blisstown/campfire">{lang.string('source')}</a></li>
<li><a href="https://codeberg.org/arimelody/campfire/issues">{lang.string('issues')}</a></li>
</ul>
</span>
</div>
<style>
#navigation {
display: flex;
flex-direction: column;
position: fixed;
top: 16px;
width: 300px;
height: calc(100vh - 32px);
border-radius: 8px;
background-color: var(--bg-800);
2025-07-10 18:11:49 +01:00
transition: background-color .1s linear;
user-select: none;
}
.server-header {
width: 100%;
height: 172px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
background-position: center;
background-size: cover;
background-color: var(--bg-600);
background-image: linear-gradient(to top, var(--bg-800), var(--bg-600));
2025-07-10 18:11:49 +01:00
transition: background .1s linear;
}
.server-icon {
height: 50%;
border-radius: 8px;
}
2024-06-30 21:08:14 +01:00
.app-logo {
max-width: 70%;
max-height: 70%;
margin: auto;
}
2024-06-30 21:08:14 +01:00
.app-logo :global(svg) {
width: 100%;
height: 100%;
}
#nav-items {
margin-bottom: auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 8px;
}
.notification-count {
position: relative;
transform: translate(22px, -16px);
min-width: 12px;
height: 28px;
margin-left: auto;
padding: 0 8px;
display: flex;
justify-content: center;
align-items: center;
text-align: right;
border-radius: 8px;
font-weight: 700;
color: var(--bg-1000);
background-color: var(--accent);
box-shadow: 0 0 32px color-mix(in srgb, transparent, var(--accent) 100%);
}
#account-items {
padding: 16px;
display: flex;
flex-direction: column;
gap: 8px;
}
.version {
margin-bottom: 16px;
font-style: italic;
font-size: .9em;
opacity: .6;
text-align: center;
2025-07-10 18:11:49 +01:00
user-select: text;
}
.version ul {
margin: 0;
padding: 0;
display: flex;
gap: 8px;
justify-content: center;
list-style: none;
}
.version ul li {
margin: 0;
}
.version ul li:not(:first-child):before {
content: '•';
margin-right: 8px;
color: inherit;
opacity: .7;
}
.version a {
color: inherit;
text-decoration: none;
opacity: .7;
}
.version a:hover {
text-decoration: underline;
}
#account-button {
width: calc(100% - 16px);
height: 48px;
padding: 8px;
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
font-family: inherit;
font-size: 1rem;
font-weight: 600;
border-radius: 8px;
background-color: var(--bg-700);
color: var(--text);
border-color: transparent;
transition-property: border-color, background-color, color;
transition-timing-function: ease-out;
transition-duration: .1s;
cursor: pointer;
}
.account-avatar {
width: 48px;
height: 48px;
border-radius: 8px;
transition: transform .1s ease-out, box-shadow .2s;
}
.account-avatar:hover {
transform: scale(1.05);
box-shadow: 0 0 16px color-mix(in srgb, transparent, var(--accent) 25%);
}
.account-avatar:active {
transform: scale(.95);
box-shadow: 0 0 16px var(--bg-1000);
}
.account-name {
/* width: 152px; */
display: flex;
flex-direction: column;
gap: 2px;
}
.username, .nickname {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: .8em;
2024-07-01 03:58:31 +01:00
color: inherit;
}
.username {
opacity: .8;
font-size: .65em;
}
.nickname :global(.emoji) {
height: 1.2em;
margin: -.1em 0;
}
.flex-row {
display: flex;
flex-direction: row;
gap: 8px;
}
</style>