8000 Feature: 리팩토링 by guesung · Pull Request #127 · guesung/Web-Memo · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Feature: 리팩토링 #127

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

Merged
merged 32 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
83103fb
feat: .gitignore에 windsurfrules 추가
guesung Dec 25, 2024
ce50fca
feat: Node버전 명시
guesung Dec 25, 2024
8bd8903
feat: getCategoryText를 컴포넌트 바깥으로 분리
guesung Dec 25, 2024
0b2c6c2
feat: 더 구체적인 에러메시지 출력
guesung Dec 25, 2024
2a77c2b
feat: delay 유틸함수 구현
guesung Dec 25, 2024
5d2b664
feat: Suspense를 세분화하여 로딩 상태 구역별로 보여주기
guesung Dec 25, 2024
ac9a72c
refactor: ErrorBoudnary를 데이터 패칭 컴포넌트 별로 감싸기
guesung Dec 25, 2024
3a0ccc0
refactor: 빈 배열을 기본값 사용
guesung Dec 25, 2024
4b4a009
refactor: meta + s 키로 저장 제거
guesung Dec 25, 2024
9382d87
fix: 웹에서 업데이트 시, SidePanel에서도 상태 업데이트
guesung Dec 25, 2024
ad9f425
refactor: 네이밍 통일
guesung Dec 26, 2024
564ed0c
refactor: 인자에 직접 name 전달
guesung Dec 26, 2024
9dd6193
design : 현재 선택된 카테고리 활성화
guesung Dec 26, 2024
7af21aa
refactor: `(no-auth)`와 `(auth)`로 그룹화
guesung Dec 26, 2024
af01a82
refactor: 미사용 컴포넌트 제거
guesung Dec 26, 2024
f55f8f9
refactor: 메모 필터링 관련 로직을 `useMemosQuery`에 응집
guesung Dec 26, 2024
4a2d544
refactor: searchQuery 관련 비즈니스 로직을 분리
guesung Dec 26, 2024
8f19ca2
refactor: `SearchForm` 컴포넌트를 뷰로부터 분리
guesung Dec 26, 2024
0f82b40
chore: 외부에서 사용하지 않는 컴포넌트 제거
guesung Dec 26, 2024
800a073
refactor: pathname 변수화
guesung Dec 26, 2024
eb162bb
feat: FormProvider 추가
guesung Dec 26, 2024
496f7ad
refactor: Array.from으로 리팩토링
guesung Dec 26, 2024
6f5724a
refactor: memo가 아니라 id를 상태값으로 관리
guesung Dec 26, 2024
4380445
refacotr: 삼항 연산자 제거
guesung Dec 26, 2024
9718b08
refactor: `onRequestAppend` 함수 분리
guesung Dec 26, 2024
0ddbcd3
refactor: 함수 순서 변경
guesung Dec 26, 2024
f96a2bb
refactor: item과 memo 분리하던 상태를 합치기
guesung Dec 26, 2024
a94ea3d
refactor: 각 컴포넌트 보여줄지 여부 결정을 컴포넌트 내부에서 결정
guesung Dec 26, 2024
3fcbc43
fix: 잘못 설정한 `number[]`타입 수정
guesung Dec 26, 2024
5b5f5b5
fix: engines설정 제거
guesung Dec 26, 2024
6da7ee6
fix: isShowingOption을 인자로 전달
guesung Dec 26, 2024
4c9c35c
fix: 빌드 이슈 해결
guesung Jan 1, 2025
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ vite.config.**

# Turborepo
.turbo

# windsurf
.windsurfrules
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@
"prettier . --write"
]
},
"sideEffects": false,
"packageManager": "pnpm@9.5.0",
"engines": {
"node": ">=18.12.0"
},
"sideEffects": false
}
}
2 changes: 1 addition & 1 deletion packages/shared/src/constants/Path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const PATHS = {
memos: '/memos',
auth: '/auth',
introduce: '/introduce',
updates: '/updates',
update: '/update',
memosWish: '/memos?isWish=true',
memosSetting: '/memos/setting',
callbackOAuth: '/auth/callback',
Expand Down
43 changes: 38 additions & 5 deletions packages/shared/src/hooks/supabase/useMemosQuery.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,55 @@
import { getChoseong } from 'es-hangul';
import { QUERY_KEY } from '@src/constants';
import { MemoService } from '@src/utils';
import { useSuspenseQuery } from '@tanstack/react-query';

import useSupabaseClientQuery from './useSupabaseClientQuery';
import { MemoRow } from '@src/types';

type QueryData = Awaited<ReturnType<MemoService['getMemos']>>;
type QueryFnData = Awaited<ReturnType<MemoService['getMemos']>>;
type QueryError = Error;
type QueryData = MemoRow[];

export default function useMemosQuery() {
interface UseMemosQueryProps {
category?: string;
isWish?: string;
searchQuery?: string;
searchTarget?: string;
}

export default function useMemosQuery({ category, isWish, searchQuery, searchTarget }: UseMemosQueryProps = {}) {
const { data: supabaseClient } = useSupabaseClientQuery();
const searchQueryLower = searchQuery?.toLowerCase();

const query = useSuspenseQuery<QueryFnData, QueryError, QueryData>({
queryFn: new MemoService(supabaseClient).getMemos,
select: ({ data: memos }) => {
return (
memos
?.filter(memo => !!isWish === !!memo.isWish)
.filter(memo => (category ? memo.category?.name === category : true))
.filter(memo => {
if (!searchQueryLower) return true;

const isTitleMatch =
memo.title?.toLowerCase().includes(searchQueryLower) ||
getChoseong(memo.title).includes(searchQueryLower);
const isMemoMatch =
memo.memo?.toLowerCase().includes(searchQueryLower) || getChoseong(memo.memo).includes(searchQueryLower);
const isCategoryMatch = memo.category?.name.toLowerCase().includes(searchQueryLower);

const query = useSuspenseQuery<QueryData, QueryError>({
queryFn: () => new MemoService(supabaseClient).getMemos(),
if (isCategoryMatch) return true;
if (searchTarget === 'title') return isTitleMatch;
if (searchTarget === 'memo') return isMemoMatch;
return isTitleMatch || isMemoMatch;
}) ?? []
);
},
queryKey: QUERY_KEY.memos(),
});

return {
...query,
memos: query.data?.data ?? [],
memos: query.data ?? [],
};
}
209 changes: 154 additions & 55 deletions packages/shared/src/modules/extension-bridge/ExtensionBridge.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,93 @@
import { EXTENSION } from '@src/constants';
import { Runtime, Tab } from '@src/utils/extension';
import { YoutubeTranscript } from 'youtube-transcript';

import { BRIDGE_MESSAGE_TYPES, Category } from './constant';
import { ExtensionError, ExtensionErrorCode, PageContent } from './types';

export default class ExtensionBridge {
static requestGetSidePanelOpen(callbackFn: () => void) {
chrome.runtime.sendMessage(EXTENSION.id, { type: BRIDGE_MESSAGE_TYPES.GET_SIDE_PANEL_OPEN }, callbackFn);
static async requestGetSidePanelOpen(callbackFn: () => void) {
try {
chrome.runtime.sendMessage(
EXTENSION.id,
{
type: BRIDGE_MESSAGE_TYPES.GET_SIDE_PANEL_OPEN,
},
callbackFn,
);
} catch (error) {
throw new ExtensionError(
'Failed to request side panel open status',
ExtensionErrorCode.COMMUNICATION_ERROR,
error,
);
}
}

static responseGetSidePanelOpen() {
Runtime.onMessageExternal(BRIDGE_MESSAGE_TYPES.GET_SIDE_PANEL_OPEN, (_, __, sendResponse) => {
sendResponse(true);
});
}

static requestOpenSidePanel() {
return Runtime.sendMessage(BRIDGE_MESSAGE_TYPES.OPEN_SIDE_PANEL);
try {
Runtime.onMessageExternal(BRIDGE_MESSAGE_TYPES.GET_SIDE_PANEL_OPEN, (_, __, sendResponse) => {
sendResponse(true);
});
} catch (error) {
throw new ExtensionError(
'Failed to respond to side panel open status request',
ExtensionErrorCode.RUNTIME_ERROR,
error,
);
}
}

static async requestOpenSidePanel() {
try {
return await Runtime.sendMessage(BRIDGE_MESSAGE_TYPES.OPEN_SIDE_PANEL);
} catch (error) {
throw new ExtensionError('Failed to request side panel open', ExtensionErrorCode.RUNTIME_ERROR, error);
}
}

static responseOpenSidePanel() {
chrome.runtime.onMessage.addListener((request, sender) => {
if (request.type === BRIDGE_MESSAGE_TYPES.OPEN_SIDE_PANEL) {
if (!sender.tab?.windowId) return;
chrome.sidePanel.open({ windowId: sender.tab.windowId });
}
});
}

static requestGetTabs() {
return Runtime.sendMessage<null, chrome.tabs.Tab>(BRIDGE_MESSAGE_TYPES.GET_TABS);
try {
chrome.runtime.onMessage.addListener((request, sender) => {
if (request.type === BRIDGE_MESSAGE_TYPES.OPEN_SIDE_PANEL) {
if (!sender.tab?.windowId) {
throw new ExtensionError('No window ID found', ExtensionErrorCode.TAB_ERROR);
}
chrome.sidePanel.open({ windowId: sender.tab.windowId });
}
});
} catch (error) {
throw new ExtensionError('Failed to respond to open side panel request', ExtensionErrorCode.RUNTIME_ERROR, error);
}
}

static async requestGetTabs() {
try {
return await Runtime.sendMessage<null, chrome.tabs.Tab>(BRIDGE_MESSAGE_TYPES.GET_TABS);
} catch (error) {
throw new ExtensionError('Failed to request tabs', ExtensionErrorCode.TAB_ERROR, error);
}
}

static async responseGetTabs() {
const tabs = await Tab.get();

Runtime.onMessage(BRIDGE_MESSAGE_TYPES.GET_TABS, (_, __, sendResponse) => {
sendResponse(tabs);
});
try {
const tabs = await Tab.get();
Runtime.onMessage(BRIDGE_MESSAGE_TYPES.GET_TABS, (_, __, sendResponse) => {
sendResponse(tabs);
});
} catch (error) {
throw new ExtensionError('Failed to respond to get tabs request', ExtensionErrorCode.TAB_ERROR, error);
}
}

static requestPageContent() {
return Tab.sendMessage<void, { content: string; category: Category }>(BRIDGE_MESSAGE_TYPES.PAGE_CONTENT);
static async requestPageContent(): Promise<PageContent> {
try {
return await Tab.sendMessage<void, PageContent>(BRIDGE_MESSAGE_TYPES.PAGE_CONTENT);
} catch (error) {
throw new ExtensionError('Failed to request page content', ExtensionErrorCode.CONTENT_ERROR, error);
}
}

private static checkYoutube(url: string) {
private static checkYoutube(url: string): boolean {
const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+$/;
return youtubeRegex.test(url);
}
Expand All @@ -55,56 +98,112 @@ export default class ExtensionBridge {
}

static async responsePageContent() {
const url = location.href;
const category = this.getCategory(url);
const content = await this._getContent(url, category);

Runtime.onMessage(BRIDGE_MESSAGE_TYPES.PAGE_CONTENT, async (_, __, sendResponse) => {
sendResponse({ content, category });
return true;
});
try {
const url = location.href;
const category = this.getCategory(url);
const content = await this._getContent(url, category);

Runtime.onMessage(BRIDGE_MESSAGE_TYPES.PAGE_CONTENT, async (_, __, sendResponse) => {
try {
sendResponse({ content, category });
return true;
} catch (error) {
throw new ExtensionError('Failed to send page content response', ExtensionErrorCode.CONTENT_ERROR, error);
}
});
} catch (error) {
throw new ExtensionError('Failed to process page content response', ExtensionErrorCode.CONTENT_ERROR, error);
}
}

private static async _getContent(url: string, category: Category) {
if (category === 'youtube') return await this._getContentFromYoutube(url);
return this._getContentFromWeb();
try {
if (category === 'youtube') return await this._getContentFromYoutube(url);
return this._getContentFromWeb();
} catch (error) {
throw new ExtensionError('Failed to get content', ExtensionErrorCode.CONTENT_ERROR, error);
}
}

private static async _getContentFromYoutube(url: string) {
const transcripts = await YoutubeTranscript.fetchTranscript(url);
return transcripts.map(transcript => transcript.text).join('\n');
try {
const transcripts = await YoutubeTranscript.fetchTranscript(url);
return transcripts.map(transcript => transcript.text).join('\n');
} catch (error) {
throw new ExtensionError('Failed to get YouTube transcript', ExtensionErrorCode.YOUTUBE_ERROR, error);
}
}

private static _getContentFromWeb() {
return document.body.innerText;
try {
return document.body.innerText;
} catch (error) {
throw new ExtensionError('Failed to get web content', ExtensionErrorCode.CONTENT_ERROR, error);
}
}

static requestRefetchTheMemos() {
return chrome.runtime.sendMessage(EXTENSION.id, {
type: BRIDGE_MESSAGE_TYPES.REFETCH_THE_MEMO_LIST,
});
static async requestRefetchTheMemos() {
try {
return await chrome.runtime.sendMessage(EXTENSION.id, {
type: BRIDGE_MESSAGE_TYPES.REFETCH_THE_MEMO_LIST,
});
} catch (error) {
throw new ExtensionError('Failed to request memo list refresh', ExtensionErrorCode.COMMUNICATION_ERROR, error);
}
}

static responseRefetchTheMemos(callbackFn: () => void) {
return Runtime.onMessageExternal(BRIDGE_MESSAGE_TYPES.REFETCH_THE_MEMO_LIST, callbackFn);
try {
return Runtime.onMessageExternal(BRIDGE_MESSAGE_TYPES.REFETCH_THE_MEMO_LIST, callbackFn);
} catch (error) {
throw new ExtensionError(
'Failed to respond to memo list refresh request',
ExtensionErrorCode.RUNTIME_ERROR,
error,
);
}
}

static requestUpdateSidePanel() {
return Runtime.sendMessage(BRIDGE_MESSAGE_TYPES.UPDATE_SIDE_PANEL);
static async requestUpdateSidePanel() {
try {
return await Runtime.sendMessage(BRIDGE_MESSAGE_TYPES.UPDATE_SIDE_PANEL);
} catch (error) {
throw new ExtensionError('Failed to request side panel update', ExtensionErrorCode.COMMUNICATION_ERROR, error);
}
}

static responseUpdateSidePanel(callbackFn: Parameters<typeof Runtime.onMessage>[1]) {
return Runtime.onMessage(BRIDGE_MESSAGE_TYPES.UPDATE_SIDE_PANEL, callbackFn);
try {
return Runtime.onMessage(BRIDGE_MESSAGE_TYPES.UPDATE_SIDE_PANEL, callbackFn);
} catch (error) {
throw new ExtensionError(
'Failed to respond to side panel update request',
ExtensionErrorCode.RUNTIME_ERROR,
error,
);
}
}

static requestGetExtensionManifest(callbackFn: (response: chrome.runtime.Manifest) => void) {
chrome.runtime.sendMessage(EXTENSION.id, { type: BRIDGE_MESSAGE_TYPES.GET_EXTENSION_MANIFEST }, callbackFn);
static async requestGetExtensionManifest(callbackFn: (response: chrome.runtime.Manifest) => void) {
try {
await chrome.runtime.sendMessage(EXTENSION.id, { type: BRIDGE_MESSAGE_TYPES.GET_EXTENSION_MANIFEST }, callbackFn);
} catch (error) {
throw new ExtensionError('Failed to request extension manifest', ExtensionErrorCode.RUNTIME_ERROR, error);
}
}

static responseGetExtensionManifest() {
const manifest = chrome.runtime.getManifest();
Runtime.onMessageExternal(BRIDGE_MESSAGE_TYPES.GET_EXTENSION_MANIFEST, (_, __, sendResponse) =>
sendResponse(manifest),
);
try {
const manifest = chrome.runtime.getManifest();
Runtime.onMessageExternal(BRIDGE_MESSAGE_TYPES.GET_EXTENSION_MANIFEST, (_, __, sendResponse) =>
sendResponse(manifest),
);
} catch (error) {
throw new ExtensionError(
'Failed to respond to extension manifest request',
ExtensionErrorCode.RUNTIME_ERROR,
error,
);
}
}
}
26 changes: 26 additions & 0 deletions packages/shared/src/modules/extension-bridge/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Category } from './constant';

export class ExtensionError extends Error {
constructor(
message: string,
public code: ExtensionErrorCode,
public originalError?: unknown,
) {
super(message);
this.name = 'ExtensionError';
}
}

export enum ExtensionErrorCode {
COMMUNICATION_ERROR = 'COMMUNICATION_ERROR',
PERMISSION_ERROR = 'PERMISSION_ERROR',
CONTENT_ERROR = 'CONTENT_ERROR',
RUNTIME_ERROR = 'RUNTIME_ERROR',
TAB_ERROR = 'TAB_ERROR',
YOUTUBE_ERROR = 'YOUTUBE_ERROR',
}

export interface PageContent {
content: string;
category: Category;
}
1 change: 1 addition & 0 deletions packages/shared/src/utils/Time.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const delay = () => new Promise(resolve => setTimeout(resolve, 3000));
1 change: 1 addition & 0 deletions packages/shared/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './String';
export * from './Supabase';
export * from './Tailwind';
export * from './Url';
export * from './Time';
Loading
Loading
0