Skip to content

Commit

Permalink
add refreshing animation
Browse files Browse the repository at this point in the history
  • Loading branch information
zaknesler committed Jun 1, 2024
1 parent fe71541 commit 4670e76
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 21 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"partysocket": "^1.0.1",
"solid-icons": "^1.1.0",
"solid-js": "^1.8.17",
"solid-transition-group": "^0.2.3",
"wretch": "^2.8.1"
},
"devDependencies": {
Expand Down
24 changes: 24 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import defaultTheme from 'tailwindcss/defaultTheme';
import kobaltePlugin from '@kobalte/tailwindcss';
import formsPlugin from '@tailwindcss/forms';
import typographyPlugin from '@tailwindcss/typography';
import type { Config } from 'tailwindcss';
import colors from 'tailwindcss/colors';
import defaultTheme from 'tailwindcss/defaultTheme';
import plugin from 'tailwindcss/plugin';
import formsPlugin from '@tailwindcss/forms';
import kobaltePlugin from '@kobalte/tailwindcss';
import typographyPlugin from '@tailwindcss/typography';
import { screens } from './ui/src/constants/screens';

export default {
Expand Down
39 changes: 31 additions & 8 deletions ui/src/components/feed/feed-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,42 @@ import { cx } from 'class-variance-authority';
import { HiSolidRss } from 'solid-icons/hi';
import { type Component, type JSX, Match, type Setter, Show, Switch, createMemo, createSignal } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import { Transition } from 'solid-transition-group';
import { useNotifications } from '~/contexts/notifications-context';
import { useQueryState } from '~/contexts/query-state-context';
import { useFeedsStats } from '~/hooks/queries/use-feeds-stats';
import type { Feed } from '~/types/bindings';
import { Spinner } from '../ui/spinner';

type FeedItemProps = {
feed: Feed;
};

export const FeedItem: Component<FeedItemProps> = props => {
const location = useLocation();
const state = useQueryState();

const [open, setOpen] = createSignal(false);
const location = useLocation();

const { stats } = useFeedsStats();
const notifications = useNotifications();

const [open, setOpen] = createSignal(false);

const getPath = createMemo(() => `/feeds/${props.feed.uuid}`);
const isActive = createMemo(() => location.pathname.startsWith(getPath()));
const getStats = createMemo(() => stats.data?.find(item => item.uuid === props.feed.uuid));

const getFaviconSrc = () => props.feed.favicon_b64 || props.feed.favicon_url;

const isLoading = createMemo(() => notifications.feedsRefreshing().includes(props.feed.uuid));

return (
<BaseFeedItem
href={getPath().concat(state.getQueryString())}
title={props.feed.title_display || props.feed.title}
open={open()}
active={isActive()}
setOpen={setOpen}
active={isActive()}
loading={isLoading()}
unread_count={getStats()?.count_unread}
favicon_src={getFaviconSrc()}
/>
Expand All @@ -41,11 +48,12 @@ export const FeedItem: Component<FeedItemProps> = props => {

type BaseFeedItemProps = {
href: string;
unread_count?: number;
title?: string;
active: boolean;
open: boolean;
setOpen: Setter<boolean>;
active: boolean;
title?: string;
loading?: boolean;
unread_count?: number;
favicon_src?: string;
icon?: () => JSX.Element;
};
Expand All @@ -63,7 +71,7 @@ export const BaseFeedItem: Component<BaseFeedItemProps> = props => (
: 'border-transparent dark:hover:bg-gray-800 hover:bg-gray-200 dark:hover:text-white hover:text-gray-900',
)}
>
<div class="flex size-7 shrink-0 items-center justify-center overflow-hidden rounded-md md:h-5 md:w-5 md:rounded">
<div class="relative flex size-7 shrink-0 items-center justify-center overflow-hidden rounded-md md:h-5 md:w-5 md:rounded">
<Switch fallback={<RssIcon />}>
<Match when={props.favicon_src}>
<Image fallbackDelay={500} class="size-full">
Expand All @@ -76,6 +84,21 @@ export const BaseFeedItem: Component<BaseFeedItemProps> = props => (
<Dynamic component={props.icon} />
</Match>
</Switch>

<Transition
enterActiveClass="transition duration-50 ease-in-out"
exitActiveClass="transition duration-50 ease-in-out"
enterClass="opacity-0"
exitToClass="opacity-0"
>
<Show when={props.loading}>
<div class="absolute z-10 size-full bg-gray-100/50 backdrop-blur">
<div class="size-full scale-75">
<Spinner class="size-full" />
</div>
</div>
</Show>
</Transition>
</div>

<span class="flex-1 overflow-x-hidden truncate">{props.title}</span>
Expand Down
12 changes: 3 additions & 9 deletions ui/src/contexts/notifications-context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WebSocket } from 'partysocket';
import { createContext, createEffect, createSignal, useContext } from 'solid-js';
import { createContext, createSignal, useContext } from 'solid-js';
import type { Notification } from '~/types/bindings';
import { wsUrl } from '~/utils/url';
import { useInvalidateFeed } from '../hooks/queries/use-invalidate-feed';
Expand All @@ -15,19 +15,13 @@ export const useNotifications = () => {
};

export const makeNotificationsContext = () => {
const [feedsRefreshing, setFeedsRefreshing] = createSignal<string[]>([]);
const invalidateFeed = useInvalidateFeed();

createEffect(() => {
const refreshing = feedsRefreshing();
if (!refreshing.length) return;

console.log(`refreshing ${refreshing.length} feeds`);
});
const [feedsRefreshing, setFeedsRefreshing] = createSignal<string[]>([]);

const socket = new WebSocket(wsUrl('/notifications'), undefined, {
connectionTimeout: 1000,
maxRetries: 10,
maxRetries: 20,
});

socket.addEventListener('open', () => console.info('[ws] connection established'));
Expand Down

0 comments on commit 4670e76

Please sign in to comment.