BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: use aliases

+23 -17
+10 -5
docs/tasks/04-notifications.md
··· 2 2 3 3 Spec: [feeds.md](../specs/feeds.md) 4 4 5 - ## Steps 5 + ## Tasks 6 + 7 + ### Tauri 6 8 7 9 - [ ] Create `src-tauri/src/notifications.rs` 8 10 - `src-tauri/src/commands/notifications.rs` for Tauri commands ··· 10 12 - [ ] `update_seen()` — `app.bsky.notification.updateSeen` 11 13 - [ ] `get_unread_count()` — `app.bsky.notification.getUnreadCount` 12 14 - [ ] Background polling: spawn async task on login, poll every 30s, emit Tauri event on new notifications 13 - - [ ] **Frontend**: notifications panel with two tabs — Mentions / Activity (Aeronaut pattern) 14 - - [ ] **Frontend**: unread badge on sidebar notification icon with `Motion` scale-in pop 15 - - [ ] **Frontend**: new notification items `Motion` slide-in from top 16 - - [ ] **Frontend**: tab switch `Presence` crossfade between Mentions/Activity 17 15 - [ ] System notifications via `tauri-plugin-notification` for mentions when app is in background 16 + 17 + ## Frontend 18 + 19 + - [ ] notifications panel with two tabs — Mentions / Activity (Aeronaut pattern) 20 + - [ ] unread badge on sidebar notification icon with `Motion` scale-in pop 21 + - [ ] new notification items `Motion` slide-in from top 22 + - [ ] tab switch `Presence` crossfade between Mentions/Activity
+1 -1
src/components/LoginPanel.tsx
··· 1 + import type { LoginSuggestion } from "$/lib/types"; 1 2 import { invoke } from "@tauri-apps/api/core"; 2 3 import { createEffect, createSignal, For, onCleanup, onMount, Show } from "solid-js"; 3 4 import { Motion } from "solid-motionone"; 4 - import type { LoginSuggestion } from "../lib/types"; 5 5 import { AvatarBadge } from "./AvatarBadge"; 6 6 import { Icon } from "./shared/Icon"; 7 7 import { LazuriteLogo } from "./Wordmark";
+1 -1
src/components/account/AccountButtons.tsx
··· 1 + import { Icon } from "$/components/shared/Icon"; 1 2 import type { AccountSummary } from "$/lib/types"; 2 3 import { createMemo, Show } from "solid-js"; 3 - import { Icon } from "../shared/Icon"; 4 4 5 5 export function AccountSwitchButton( 6 6 props: {
+6 -5
src/components/account/AccountSwitcher.tsx
··· 1 + import { ArrowIcon } from "$/components/shared/Icon"; 1 2 import type { AccountSummary, ActiveSession } from "$/lib/types"; 2 3 import { createMemo, onCleanup, onMount, Show } from "solid-js"; 3 - import { ArrowIcon } from "../shared/Icon"; 4 4 import { SwitcherIdentity } from "./AccountSwitcherIdentity"; 5 5 import { AccountSwitcherMenuList } from "./AccountSwitcherMenuList"; 6 6 ··· 71 71 <div 72 72 class="relative mt-auto w-full transition-[width,max-width] duration-300 ease-out max-[1180px]:mt-0 max-[1180px]:max-w-none" 73 73 classList={{ 74 + "z-40": props.open, 74 75 "w-auto": !!props.compact, 75 76 "max-[1180px]:col-start-3 max-[1180px]:row-start-1 max-[1180px]:justify-self-end": !!props.narrow, 76 77 "max-[1180px]:col-span-full max-[1180px]:justify-self-stretch": !props.narrow, ··· 82 83 class="relative w-full min-w-0 cursor-pointer border-0 bg-white/4 text-on-surface shadow-[inset_0_0_0_1px_rgba(255,255,255,0.04)] transition duration-150 ease-out hover:-translate-y-px hover:bg-white/8" 83 84 classList={{ 84 85 "rounded-xl py-[0.95rem] pr-10 pl-4": !props.compact, 85 - "grid h-14 w-14 place-items-center rounded-full p-0": !!props.compact, 86 + "grid h-14 w-14 place-items-center overflow-visible rounded-full p-0": !!props.compact, 86 87 }} 87 88 type="button" 88 89 aria-haspopup="menu" ··· 97 98 name={identity().name} 98 99 tone={identity().tone} /> 99 100 <span 100 - class="absolute flex items-center text-on-surface-variant" 101 + class="absolute flex items-center justify-center text-on-surface-variant" 101 102 classList={{ 102 103 "right-[0.95rem] top-1/2 -translate-y-1/2": !props.compact, 103 - "bottom-0 right-0 h-5 w-5 rounded-full bg-surface-container text-xs shadow-[inset_0_0_0_1px_rgba(255,255,255,0.04)]": 104 + "bottom-0 right-0 h-5 w-5 translate-x-[8%] translate-y-[8%] rounded-full bg-surface-container text-[0.7rem] leading-none shadow-[0_0_0_2px_rgba(8,8,8,0.9),inset_0_0_0_1px_rgba(255,255,255,0.05)]": 104 105 !!props.compact, 105 106 }} 106 107 aria-hidden="true"> ··· 112 113 113 114 <Show when={props.open}> 114 115 <div 115 - class="absolute z-20 rounded-2xl bg-(--surface-container-highest) p-4 shadow-[0_24px_40px_rgba(0,0,0,0.28)] backdrop-blur-[20px] max-[1180px]:bottom-auto max-[1180px]:top-[calc(100%+0.75rem)]" 116 + class="absolute z-50 rounded-2xl bg-(--surface-container-highest) p-4 shadow-[0_24px_40px_rgba(0,0,0,0.28)] backdrop-blur-[20px] max-[1180px]:bottom-auto max-[1180px]:top-[calc(100%+0.75rem)]" 116 117 classList={{ 117 118 "inset-x-0 bottom-[calc(100%+0.75rem)]": !props.compact, 118 119 "bottom-0 left-[calc(100%+0.85rem)] w-[19rem]": !!props.compact && !props.narrow,
+1 -1
src/components/account/AccountSwitcherIdentity.tsx
··· 1 + import { AvatarBadge } from "$/components/AvatarBadge"; 1 2 import { Show } from "solid-js"; 2 - import { AvatarBadge } from "../AvatarBadge"; 3 3 4 4 export function SwitcherIdentity( 5 5 props: {
+1 -1
src/components/feeds/FeedDrawer.tsx
··· 1 + import { Icon } from "$/components/shared/Icon"; 1 2 import { getFeedName } from "$/lib/feeds"; 2 3 import type { FeedGeneratorView, SavedFeedItem } from "$/lib/types"; 3 4 import { For, Show } from "solid-js"; 4 5 import { Motion, Presence } from "solid-motionone"; 5 - import { Icon } from "../shared/Icon"; 6 6 import { FeedChipAvatar } from "./FeedChipAvatar"; 7 7 8 8 export function SavedFeedsDrawer(
+1 -1
src/components/feeds/FeedEmpty.tsx
··· 1 + import { Icon } from "$/components/shared/Icon"; 1 2 import { Show } from "solid-js"; 2 - import { Icon } from "../shared/Icon"; 3 3 4 4 export function LoadingMoreIndicator(props: { loading: boolean }) { 5 5 return (
+2 -2
src/components/feeds/FeedTabs.tsx
··· 1 + import { Icon } from "$/components/shared/Icon"; 1 2 import { getFeedName } from "$/lib/feeds"; 2 3 import type { FeedGeneratorView, SavedFeedItem } from "$/lib/types"; 3 4 import { For } from "solid-js"; 4 - import { Icon } from "../shared/Icon"; 5 5 import { FeedChipAvatar } from "./FeedChipAvatar"; 6 6 7 7 export function FeedTabBar( ··· 49 49 type="button" 50 50 onClick={() => props.onSelect(props.feed.id)}> 51 51 <FeedChipAvatar feed={props.feed} generator={props.generator} /> 52 - <span class="max-w-[11rem] truncate max-[900px]:max-w-[9rem] max-[720px]:max-w-[7.5rem]"> 52 + <span class="max-w-44 truncate max-[900px]:max-w-36 max-[720px]:max-w-30"> 53 53 {getFeedName(props.feed, props.generator?.displayName)} 54 54 </span> 55 55 </button>