mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
Добавили заметки в сайдбар (#126)
* added notes sidebar * added note dropping and editing * added sidebar navigation * handling sidebarchanges over time * using router back for closing sidebar * fixed tripping inside single sidebar * added superpowers toggle to sidebar * user button opens sidebar now * added profile cover for profile sidebar * removed profile sidebar completely * ran prettier over project * added note not found error literal
This commit is contained in:
parent
fe3db608d6
commit
5d34090238
72 changed files with 1241 additions and 664 deletions
|
@ -35,14 +35,6 @@ export const useMessageEventReactions = () => {
|
|||
void createSocialAccount(path(['data', 'payload', 'token'], event));
|
||||
}
|
||||
break;
|
||||
case EventMessageType.OpenProfile:
|
||||
const username: string | undefined = path(['data', 'username'], event);
|
||||
if (!username) {
|
||||
return;
|
||||
}
|
||||
|
||||
showModal(Dialog.Profile, { username });
|
||||
break;
|
||||
default:
|
||||
console.log('unknown message', event.data);
|
||||
}
|
||||
|
|
9
src/hooks/auth/useSuperPowers.ts
Normal file
9
src/hooks/auth/useSuperPowers.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { useMemo } from "react";
|
||||
|
||||
import { useAuth } from "~/hooks/auth/useAuth";
|
||||
|
||||
export const useSuperPowers = () => {
|
||||
const { isTester, setIsTester } = useAuth();
|
||||
|
||||
return useMemo(() => ({ isTester, setIsTester }), [isTester, setIsTester]);
|
||||
};
|
|
@ -1,18 +1,16 @@
|
|||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect } from "react";
|
||||
|
||||
import isBefore from 'date-fns/isBefore';
|
||||
import isBefore from "date-fns/isBefore";
|
||||
|
||||
import { useRandomPhrase } from '~/constants/phrases';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
import { useLastSeenBoris } from '~/hooks/auth/useLastSeenBoris';
|
||||
import { useBorisStats } from '~/hooks/boris/useBorisStats';
|
||||
import { IComment } from '~/types';
|
||||
import { useRandomPhrase } from "~/constants/phrases";
|
||||
import { useLastSeenBoris } from "~/hooks/auth/useLastSeenBoris";
|
||||
import { useBorisStats } from "~/hooks/boris/useBorisStats";
|
||||
import { IComment } from "~/types";
|
||||
|
||||
export const useBoris = (comments: IComment[]) => {
|
||||
const title = useRandomPhrase('BORIS_TITLE');
|
||||
const title = useRandomPhrase("BORIS_TITLE");
|
||||
|
||||
const { lastSeen, setLastSeen } = useLastSeenBoris();
|
||||
const { isTester, setIsTester } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
const last_comment = comments[0];
|
||||
|
@ -32,12 +30,5 @@ export const useBoris = (comments: IComment[]) => {
|
|||
|
||||
const { stats, isLoading: isLoadingStats } = useBorisStats();
|
||||
|
||||
const setIsBetaTester = useCallback(
|
||||
(isTester: boolean) => {
|
||||
setIsTester(isTester);
|
||||
},
|
||||
[setIsTester]
|
||||
);
|
||||
|
||||
return { setIsBetaTester, isTester, stats, title, isLoadingStats };
|
||||
return { stats, title, isLoadingStats };
|
||||
};
|
||||
|
|
11
src/hooks/dom/useConfirmation.ts
Normal file
11
src/hooks/dom/useConfirmation.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { useCallback } from "react";
|
||||
|
||||
export const useConfirmation = () =>
|
||||
useCallback((prompt = "", onApprove: () => {}, onReject?: () => {}) => {
|
||||
if (!window.confirm(prompt || "Уверен?")) {
|
||||
onReject?.();
|
||||
return;
|
||||
}
|
||||
|
||||
onApprove();
|
||||
}, []);
|
|
@ -1,24 +1,30 @@
|
|||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
export const useWindowSize = () => {
|
||||
const [size, setSize] = useState({ innerWidth: 0, innerHeight: 0, isMobile: false });
|
||||
const [size, setSize] = useState({
|
||||
innerWidth: 0,
|
||||
innerHeight: 0,
|
||||
isTablet: false,
|
||||
isPhone: false,
|
||||
});
|
||||
|
||||
const onResize = useCallback(
|
||||
() =>
|
||||
setSize({
|
||||
innerWidth: window.innerWidth,
|
||||
innerHeight: window.innerHeight,
|
||||
isMobile: window.innerWidth < 768,
|
||||
isTablet: window.innerWidth < 768,
|
||||
isPhone: window.innerWidth < 500,
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onResize();
|
||||
|
||||
window.addEventListener('resize', onResize);
|
||||
window.addEventListener("resize", onResize);
|
||||
|
||||
return () => window.removeEventListener('resize', onResize);
|
||||
return () => window.removeEventListener("resize", onResize);
|
||||
}, []);
|
||||
|
||||
return size;
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite';
|
||||
import { apiGetNotes } from '~/api/notes';
|
||||
import { ApiGetNotesRequest } from '~/api/notes/types';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
import { GetLabNodesRequest, ILabNode } from '~/types/lab';
|
||||
import { flatten, uniqBy } from '~/utils/ramda';
|
||||
|
||||
const DEFAULT_COUNT = 20;
|
||||
|
||||
const getKey: (isUser: boolean, search: string) => SWRInfiniteKeyLoader = (isUser, search) => (
|
||||
index,
|
||||
prev: ILabNode[]
|
||||
) => {
|
||||
if (!isUser) return null;
|
||||
if (index > 0 && (!prev?.length || prev.length < 20)) return null;
|
||||
|
||||
const props: GetLabNodesRequest = {
|
||||
limit: DEFAULT_COUNT,
|
||||
offset: index * DEFAULT_COUNT,
|
||||
search: search || '',
|
||||
};
|
||||
|
||||
return JSON.stringify(props);
|
||||
};
|
||||
|
||||
const parseKey = (key: string): ApiGetNotesRequest => {
|
||||
try {
|
||||
return JSON.parse(key);
|
||||
} catch (error) {
|
||||
return { limit: DEFAULT_COUNT, offset: 0, search: '' };
|
||||
}
|
||||
};
|
||||
|
||||
export const useGetNotes = (search: string) => {
|
||||
const { isUser } = useAuth();
|
||||
|
||||
const { data, isValidating, size, setSize, mutate } = useSWRInfinite(
|
||||
getKey(isUser, search),
|
||||
async (key: string) => {
|
||||
const result = await apiGetNotes(parseKey(key));
|
||||
return result.list;
|
||||
},
|
||||
{
|
||||
dedupingInterval: 300,
|
||||
}
|
||||
);
|
||||
|
||||
const notes = useMemo(() => uniqBy(n => n.id, flatten(data || [])), [data]);
|
||||
const hasMore = (data?.[size - 1]?.length || 0) >= 1;
|
||||
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
||||
|
||||
return { notes, hasMore, loadMore, isLoading: !data && isValidating };
|
||||
};
|
113
src/hooks/notes/useNotes.ts
Normal file
113
src/hooks/notes/useNotes.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
import { useCallback, useMemo } from "react";
|
||||
|
||||
import useSWRInfinite, { SWRInfiniteKeyLoader } from "swr/infinite";
|
||||
|
||||
import {
|
||||
apiCreateNote,
|
||||
apiDeleteNote,
|
||||
apiListNotes,
|
||||
apiUpdateNote,
|
||||
} from "~/api/notes";
|
||||
import { ApiGetNotesRequest } from "~/api/notes/types";
|
||||
import { useAuth } from "~/hooks/auth/useAuth";
|
||||
import { GetLabNodesRequest, ILabNode } from "~/types/lab";
|
||||
import { Note } from "~/types/notes";
|
||||
import { flatten, uniqBy } from "~/utils/ramda";
|
||||
|
||||
const DEFAULT_COUNT = 20;
|
||||
|
||||
const getKey: (isUser: boolean, search: string) => SWRInfiniteKeyLoader = (
|
||||
isUser,
|
||||
search,
|
||||
) => (index, prev: ILabNode[]) => {
|
||||
if (!isUser) return null;
|
||||
if (index > 0 && (!prev?.length || prev.length < 20)) return null;
|
||||
|
||||
const props: GetLabNodesRequest = {
|
||||
limit: DEFAULT_COUNT,
|
||||
offset: index * DEFAULT_COUNT,
|
||||
search: search || "",
|
||||
};
|
||||
|
||||
return JSON.stringify(props);
|
||||
};
|
||||
|
||||
const parseKey = (key: string): ApiGetNotesRequest => {
|
||||
try {
|
||||
return JSON.parse(key);
|
||||
} catch (error) {
|
||||
return { limit: DEFAULT_COUNT, offset: 0, search: "" };
|
||||
}
|
||||
};
|
||||
|
||||
export const useNotes = (search: string) => {
|
||||
const { isUser } = useAuth();
|
||||
|
||||
const { data, isValidating, size, setSize, mutate } = useSWRInfinite(
|
||||
getKey(isUser, search),
|
||||
async (key: string) => {
|
||||
const result = await apiListNotes(parseKey(key));
|
||||
return result.list;
|
||||
},
|
||||
{
|
||||
dedupingInterval: 300,
|
||||
},
|
||||
);
|
||||
|
||||
const create = useCallback(
|
||||
async (text: string, onSuccess?: (note: Note) => void) => {
|
||||
const result = await apiCreateNote({ text });
|
||||
|
||||
if (data) {
|
||||
await mutate(
|
||||
data?.map((it, index) => (index === 0 ? [result, ...it] : it)),
|
||||
{ revalidate: false },
|
||||
);
|
||||
}
|
||||
|
||||
onSuccess?.(result);
|
||||
},
|
||||
[mutate, data],
|
||||
);
|
||||
|
||||
const remove = useCallback(
|
||||
async (id: number, onSuccess?: () => void) => {
|
||||
await apiDeleteNote(id);
|
||||
await mutate(
|
||||
data?.map(page => page.filter(it => it.id !== id)),
|
||||
{ revalidate: false },
|
||||
);
|
||||
onSuccess?.();
|
||||
},
|
||||
[mutate, data],
|
||||
);
|
||||
|
||||
const update = useCallback(
|
||||
async (id: number, text: string, onSuccess?: () => void) => {
|
||||
const result = await apiUpdateNote({ id, text });
|
||||
await mutate(
|
||||
data?.map(page => page.map(it => (it.id === id ? result : it))),
|
||||
{ revalidate: false },
|
||||
);
|
||||
onSuccess?.();
|
||||
},
|
||||
[mutate, data],
|
||||
);
|
||||
|
||||
const notes = useMemo(() => uniqBy(n => n.id, flatten(data || [])), [data]);
|
||||
const hasMore = (data?.[size - 1]?.length || 0) >= 1;
|
||||
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
notes,
|
||||
hasMore,
|
||||
loadMore,
|
||||
isLoading: !data && isValidating,
|
||||
create,
|
||||
remove,
|
||||
update,
|
||||
}),
|
||||
[notes, hasMore, loadMore, data, isValidating, create, remove],
|
||||
);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue