Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Persistent cache #3721

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions packages/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@dailydotdev/shared": "*",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-query-devtools": "^4.35.3",
"@tanstack/react-query-persist-client": "^4.36.1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will conflict with: #3718
Just to note we might have to choose how to release

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, also looking at your comments in that PR, I will have to change some syntax here too when it comes to queryKeys and queryFns

"classnames": "^2.5.1",
"date-fns": "^2.25.0",
"date-fns-tz": "1.0.0",
Expand Down
15 changes: 3 additions & 12 deletions packages/extension/src/companion/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { ReactElement, useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import browser from 'webextension-polyfill';
import { Boot, BootApp } from '@dailydotdev/shared/src/lib/boot';
import { AuthContextProvider } from '@dailydotdev/shared/src/contexts/AuthContext';
Expand All @@ -15,13 +14,14 @@ import {
ExtensionMessageType,
getCompanionWrapper,
} from '@dailydotdev/shared/src/lib/extension';
import { defaultQueryClientConfig } from '@dailydotdev/shared/src/lib/query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { PromptElement } from '@dailydotdev/shared/src/components/modals/Prompt';
import { GrowthBookProvider } from '@dailydotdev/shared/src/components/GrowthBookProvider';
import { NotificationsContextProvider } from '@dailydotdev/shared/src/contexts/NotificationsContext';
import { useEventListener } from '@dailydotdev/shared/src/hooks';
import { structuredCloneJsonPolyfill } from '@dailydotdev/shared/src/lib/structuredClone';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { defaultQueryClientConfig } from '@dailydotdev/shared/src/lib/query';
import Companion from './Companion';
import CustomRouter from '../lib/CustomRouter';
import { companionFetch } from './companionFetch';
Expand All @@ -34,14 +34,7 @@ const router = new CustomRouter();

export type CompanionData = { url: string; deviceId: string } & Pick<
Boot,
| 'postData'
| 'settings'
| 'alerts'
| 'user'
| 'visit'
| 'accessToken'
| 'squads'
| 'exp'
'postData' | 'settings' | 'alerts' | 'user' | 'visit' | 'accessToken' | 'exp'
>;

const app = BootApp.Companion;
Expand All @@ -55,7 +48,6 @@ export default function App({
alerts,
visit,
accessToken,
squads,
exp,
}: CompanionData): ReactElement {
useError();
Expand Down Expand Up @@ -105,7 +97,6 @@ export default function App({
tokenRefreshed
getRedirectUri={() => browser.runtime.getURL('index.html')}
updateUser={() => null}
squads={squads}
>
<SettingsContextProvider settings={settings}>
<AlertContextProvider alerts={alerts}>
Expand Down
1 change: 0 additions & 1 deletion packages/extension/src/companion/Companion.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ const renderComponent = (postdata, settings): RenderResult => {
user={defaultUser}
deviceId="123"
accessToken={{ token: '', expiresIn: '' }}
squads={[]}
/>,
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ const defaultBootData: BootCacheData = {
alerts: defaultAlerts,
user: { ...defaultUser, createdAt: '2024-02-16T00:00:00.000Z' },
settings: defaultSettings,
squads: [],
notifications: { unreadNotificationsCount: 0 },
feeds: [],
};
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@
"dependencies": {
"@growthbook/growthbook": "https://gitpkg.now.sh/dailydotdev/growthbook/packages/sdk-js?b8f31f9e80879fe2bcc42b275087b50e1357f1cb",
"@growthbook/growthbook-react": "^0.17.0",
"@tanstack/query-sync-storage-persister": "4.36.1",
"@tanstack/react-query-persist-client": "4.36.1",
"@tippyjs/react": "^4.2.6",
"check-password-strength": "^2.0.10",
"graphql-ws": "^5.5.5",
Expand Down
27 changes: 27 additions & 0 deletions packages/shared/src/components/sidebar/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SidebarMenuItem,
} from './common';
import { AuthTriggersType } from '../../lib/auth';
import { TextPlaceholder } from '../widgets/common';

export interface SectionCommonProps
extends Pick<ItemInnerProps, 'shouldShowLabel'> {
Expand All @@ -24,8 +25,32 @@ interface SectionProps extends SectionCommonProps {
title?: string;
items: SidebarMenuItem[];
isItemsButton: boolean;
isLoading?: boolean;
}

const ItemsSkeleton = () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a quick loading animation for the squad section in the sidebar, as squads may not always be loaded at render time

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably good to check how many squads the average normal user has to avoid shifts for the mass

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a query on db, looks like only ~10.000 has more then 5, so probably not bad default

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found a better query, i think

select count(*) from public.user u where (select count(1) from source_member sm where sm."userId" = u.id) > 5

9,009

and more then 1 is 55,435

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so probably 1 is the best 😆

return (
<>
<div className="flex items-center">
<TextPlaceholder className="m-3 h-5 w-5 rounded-full" />
<TextPlaceholder className="w-[40%]" />
</div>
<div className="flex items-center">
<TextPlaceholder className="m-3 h-5 w-5 rounded-full" />
<TextPlaceholder className="w-[40%]" />
</div>
<div className="flex items-center">
<TextPlaceholder className="m-3 h-5 w-5 rounded-full" />
<TextPlaceholder className="w-[40%]" />
</div>
<div className="flex items-center">
<TextPlaceholder className="m-3 h-5 w-5 rounded-full" />
<TextPlaceholder className="w-[40%]" />
</div>
</>
);
};

export function Section({
title,
items,
Expand All @@ -35,6 +60,7 @@ export function Section({
activePage,
isItemsButton,
className,
isLoading,
}: SectionProps): ReactElement {
const { user, showLogin } = useContext(AuthContext);

Expand All @@ -57,6 +83,7 @@ export function Section({
{title}
</NavHeader>
)}
{isLoading && <ItemsSkeleton />}
{items.filter(mobileItemsFilter).map((item) => (
<NavItem
key={`${item.title}-${item.path}`}
Expand Down
5 changes: 3 additions & 2 deletions packages/shared/src/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { getFeedName } from '../../lib/feed';
import { LazyModal } from '../modals/common/types';
import { useLazyModal } from '../../hooks/useLazyModal';
import Logo, { LogoPosition } from '../Logo';
import { useFeeds, useViewSize, ViewSize } from '../../hooks';
import { useFeeds, useSquads, useViewSize, ViewSize } from '../../hooks';
import {
Button,
ButtonIconPosition,
Expand Down Expand Up @@ -74,7 +74,8 @@ export default function Sidebar({
onLogoClick,
}: SidebarProps): ReactElement {
const router = useRouter();
const { user, isLoggedIn, squads, isAuthReady } = useAuthContext();
const { user, isLoggedIn, isAuthReady } = useAuthContext();
const { squads } = useSquads();
const { alerts } = useAlertsContext();
const {
toggleSidebarExpanded,
Expand Down
9 changes: 5 additions & 4 deletions packages/shared/src/components/sidebar/SquadSection.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, { ReactElement, useContext } from 'react';
import React, { ReactElement } from 'react';
import { ListIcon, SidebarMenuItem } from './common';
import { Section, SectionCommonProps } from './Section';
import { NewSquadIcon, DefaultSquadIcon, SourceIcon } from '../icons';
import { Origin } from '../../lib/log';
import { useSquadNavigation } from '../../hooks';
import AuthContext from '../../contexts/AuthContext';
import { useSquadNavigation, useSquads } from '../../hooks';
import { SquadImage } from '../squads/SquadImage';

export function SquadSection(props: SectionCommonProps): ReactElement {
const { squads } = useContext(AuthContext);
const { squads, isLoading } = useSquads();

const { openNewSquad } = useSquadNavigation();

const squadMenuItems: SidebarMenuItem[] = [
Expand Down Expand Up @@ -49,6 +49,7 @@ export function SquadSection(props: SectionCommonProps): ReactElement {
items={squadMenuItems}
{...props}
isItemsButton={false}
isLoading={isLoading}
/>
);
}
9 changes: 9 additions & 0 deletions packages/shared/src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import { isCompanionActivated } from '../lib/element';
import { AuthTriggers, AuthTriggersType } from '../lib/auth';
import { Squad } from '../graphql/sources';
import { checkIsExtension } from '../lib/func';
import {
persistedQueryClient,
queryClientPersister,
} from '../lib/persistedQuery';

export interface LoginState {
trigger: AuthTriggersType;
Expand Down Expand Up @@ -84,6 +88,11 @@ export const REGISTRATION_PATH = '/register';

const logout = async (reason: string): Promise<void> => {
await dispatchLogout(reason);
// Removes the in memory cache
persistedQueryClient.clear();
// Removes the persisted cache, which is localStorage in our case.
queryClientPersister.removeClient();

const params = getQueryParams();
if (params.redirect_uri) {
window.location.replace(params.redirect_uri);
Expand Down
4 changes: 1 addition & 3 deletions packages/shared/src/contexts/BootProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ const updateLocalBootData = (
'notifications',
'user',
'lastModifier',
'squads',
'exp',
'feeds',
]);
Expand Down Expand Up @@ -182,7 +181,7 @@ export const BootDataProvider = ({

const isBootReady = isFetched && !isError;
const loadedFromCache = !!bootData;
const { user, settings, alerts, notifications, squads } = bootData || {};
const { user, settings, alerts, notifications } = bootData || {};

useRefreshToken(bootData?.accessToken, refetch);
const updatedAtActive = user ? dataUpdatedAt : null;
Expand Down Expand Up @@ -277,7 +276,6 @@ export const BootDataProvider = ({
isLegacyLogout={bootData?.isLegacyLogout}
accessToken={bootData?.accessToken}
isPastRegistration={isInitialFetch.current}
squads={squads}
>
<SettingsContextProvider
settings={settings}
Expand Down
37 changes: 37 additions & 0 deletions packages/shared/src/graphql/squads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,27 @@ export const SQUAD_QUERY = gql`
${SQUAD_BASE_FRAGMENT}
`;

export const SQUAD_MEMBERSHIPS_QUERY = gql`
query SourceMemberships {
mySourceMemberships {
pageInfo {
endCursor
hasNextPage
}
edges {
node {
source {
id
name
image
permalink
}
}
}
}
}
`;

export const SQUAD_STATIC_FIELDS_QUERY = gql`
query Source($handle: ID!) {
source(id: $handle) {
Expand Down Expand Up @@ -358,6 +379,10 @@ export interface SquadEdgesData {
sourceMembers: Connection<SourceMember>;
}

export type SourceMembershipsData = {
mySourceMemberships: Connection<SourceMember>;
};

interface SquadMemberMutationProps {
sourceId: string;
memberId: string;
Expand Down Expand Up @@ -393,6 +418,18 @@ export async function getSquad(handle: string): Promise<Squad> {
return res.source;
}

export async function getSquads(userId: string): Promise<Squad[]> {
const res = await gqlClient.request<SourceMembershipsData>(
SQUAD_MEMBERSHIPS_QUERY,
{
id: userId,
},
);
const squads = res.mySourceMemberships.edges.map((edge) => edge.node.source);
const sortedByName = squads.sort((a, b) => a.name.localeCompare(b.name));
return sortedByName;
}

export async function getSquadMembers(id: string): Promise<SourceMember[]> {
const res = await gqlClient.request<SquadEdgesData>(SQUAD_MEMBERS_QUERY, {
id,
Expand Down
23 changes: 22 additions & 1 deletion packages/shared/src/hooks/squads/useSquad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query';
import { ClientError } from 'graphql-request';
import { useContext } from 'react';
import { Squad } from '../../graphql/sources';
import { getSquad } from '../../graphql/squads';
import { getSquad, getSquads } from '../../graphql/squads';
import { ApiError, ApiErrorResult, getApiError } from '../../graphql/common';
import AuthContext from '../../contexts/AuthContext';
import { isNullOrUndefined } from '../../lib/func';
Expand Down Expand Up @@ -41,3 +41,24 @@ export const useSquad = ({ handle }: UseSquadProps): UseSquad => {
: !!getApiError(error as ApiErrorResult, ApiError.Forbidden),
};
};

type UseSquads = {
squads: Squad[];
isLoading: boolean;
};

export const useSquads = (): UseSquads => {
const { isFetched: isBootFetched, user } = useContext(AuthContext);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we care if boot is fetched?
Basically if you have a user you can go right?


const { data: squads, isLoading } = useQuery(
[RequestKey.Squads],
() => getSquads(user?.id),
{
enabled: isBootFetched && !!user,
},
);
Comment on lines +53 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I noticed is that if I create a new squad on another device, or another tab not sharing local storage the cache will never be updated, probably only after stale time goes by. It should always invalidate in the bg I think.

return {
squads,
isLoading,
};
};
Loading