Skip to content

Commit

Permalink
v1.0.0 beta.2 (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
woong-jae authored Jan 1, 2024
1 parent 7dab13b commit 428e0f1
Show file tree
Hide file tree
Showing 88 changed files with 804 additions and 584 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
dist
node_modules
src/base/ui
src/base/components
4 changes: 2 additions & 2 deletions components.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"prefix": ""
},
"aliases": {
"components": "~/base",
"utils": "~/base/ui/utils"
"components": "~/components",
"utils": "~/base/components/utils"
}
}
22 changes: 14 additions & 8 deletions src/app/chrome-extension/background/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import Url from '@base/const/url';
import { url } from '@base/const/url';
import {
alertToWorld,
crossContextConfirm,
} from '~/base/infra/chrome-extension';
import { Solution } from '~/modules/solution';
import { initBojSolutionCatcherFromBackground } from '~/modules/solution/domain/platforms/boj';
import { initLeetcodeSolutionCatcherFromBackground } from '~/modules/solution/domain/platforms/leetcode';
import createSolutionTracker from '~/modules/solution/infra/solution-tracker';
import { accessTokenStorage } from '~/features/auth';
import { saveSolution } from '~/features/solution';
import { initBojSolutionCatcherFromBackground } from '~/features/solution/platforms/boj';
import { initLeetcodeSolutionCatcherFromBackground } from '~/features/solution/platforms/leetcode';
import createSolutionTracker from '~/features/solution/solution-tracker';

initBojSolutionCatcherFromBackground();
initLeetcodeSolutionCatcherFromBackground();
Expand All @@ -21,22 +22,27 @@ solutionTracker.onSolve(async (solution) => {

if (!isConfirm) return;

const isSuccess = await Solution.saveSolution(solution);
const accessToken = await accessTokenStorage.retrieve();
if (!accessToken) {
alertToWorld('저장에 실패했습니다');
return;
}

const isSuccess = await saveSolution({ solution, accessToken });
alertToWorld(isSuccess ? '성공적으로 저장했습니다' : '저장에 실패했습니다');
});

/* Chrome extension 아이콘 클릭시 설정 페이지 탭 열기 */
chrome.action.onClicked.addListener(() => {
chrome.tabs.create({
url: Url.SETTING_PAGE,
url: url.SETTING_PAGE,
});
});

/* 설치 후 설정 페이지 탭 열기 */
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason !== 'install') return;
chrome.tabs.create({
url: Url.SETTING_PAGE,
url: url.SETTING_PAGE,
});
});
2 changes: 1 addition & 1 deletion src/app/chrome-extension/scripts/boj/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initBojSolutionCatcherFromWorld } from '~/modules/solution/domain/platforms/boj';
import { initBojSolutionCatcherFromWorld } from '~/features/solution/platforms/boj';

console.log('CodeVault loaded...');

Expand Down
2 changes: 1 addition & 1 deletion src/app/chrome-extension/scripts/leetcode/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initLeetcodeSolutionCatcher } from '~/modules/solution/domain/platforms/leetcode';
import { initLeetcodeSolutionCatcher } from '~/features/solution/platforms/leetcode';

console.log('CodeVault loaded...');

Expand Down
2 changes: 1 addition & 1 deletion src/app/chrome-extension/scripts/programmers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initProgrammersSolutionCatcher } from '~/modules/solution/domain/platforms/programmers';
import { initProgrammersSolutionCatcher } from '~/features/solution/platforms/programmers';

console.log('CodeVault loaded...');

Expand Down
9 changes: 4 additions & 5 deletions src/app/user-config/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Loading } from '~/contexts/loading';
import UserSettingPage from '~/pages/user-setting';

const queryClient = new QueryClient();

export default function App() {
return (
<QueryClientProvider client={queryClient}>
<>
<UserSettingPage />
</QueryClientProvider>
<Loading />
</>
);
}
25 changes: 23 additions & 2 deletions src/app/user-config/runApp.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createRoot } from 'react-dom/client';
import App from './App';
import '../index.css';
import { accessTokenStorage } from '~/features/auth';
import { createAuthProvider } from '~/features/auth/context';
import App from './App';

const queryClient = new QueryClient();

export function runApp() {
const $app = document.getElementById('app');
if (!$app) {
throw new Error('No element with "id=app"');
}
const root = createRoot($app);
root.render(<App />);

accessTokenStorage.retrieve().then((accessToken) => {
const authInfo = {
isLoggedIn: accessToken !== null,
accessToken: accessToken || ('' as AccessToken),
};

const AuthProvider = createAuthProvider(authInfo);

root.render(
<QueryClientProvider client={queryClient}>
<AuthProvider>
<App />
</AuthProvider>
</QueryClientProvider>,
);
});
}
4 changes: 2 additions & 2 deletions src/assets/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"manifest_version": 3,
"name": "Code-Vault",
"description": "Code-Vault에서 제공하는 크롬 확장도구입니다.",
"version": "1.0.0.1",
"version_name": "1.0.0-beta.1",
"version": "1.0.0.2",
"version_name": "1.0.0-beta.2",
"author": "[email protected]",
"homepage_url": "https://github.com/woong-jae/code-vault",
"action": {},
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as AvatarPrimitive from '@radix-ui/react-avatar';
import * as React from 'react';

import { cn } from '~/base/ui/utils';
import { cn } from '@base/components/utils';

const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
Expand Down
File renamed without changes.
File renamed without changes.
24 changes: 24 additions & 0 deletions src/base/components/Input/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react';
import { cn } from '../utils';

export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
ref={ref}
{...props}
/>
);
},
);
Input.displayName = 'Input';

export { Input };
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as LabelPrimitive from '@radix-ui/react-label';
import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';

import { cn } from '~/base/ui/utils';
import { cn } from '@base/components/utils';

const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import * as SelectPrimitive from '@radix-ui/react-select';
import * as React from 'react';

import { cn } from '~/base/ui/utils';
import { cn } from '@base/components/utils';

const Select = SelectPrimitive.Root;

Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/base/const/Url.ts → src/base/const/url.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const SETTING_PAGE = `chrome-extension://${chrome.runtime.id}/setting.html`;

export default {
export const url = {
SETTING_PAGE,
GITHUB_OAUTH_CODE: `https://github.com/login/oauth/authorize?client_id=e1f73f73ee1f2865bcd5&scope=user,repo&redirect_uri=${SETTING_PAGE}`,
};
9 changes: 0 additions & 9 deletions src/base/infra/persistence/chromeLocalStorage.ts

This file was deleted.

8 changes: 0 additions & 8 deletions src/base/infra/persistence/localStorage.ts

This file was deleted.

20 changes: 18 additions & 2 deletions src/base/services/github/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Octokit } from 'octokit';
import { stringToBase64 } from '~/base/lib/file';
import { stringToBase64 } from '@base/utils/file';

const clientId = 'e1f73f73ee1f2865bcd5';
const clientSecret = 'aceb34e7192ba7b6181d0c0649373b9fce57cda0';
Expand Down Expand Up @@ -101,7 +101,6 @@ export class Github {

const base64Content = stringToBase64(content);

this.githubApiClient.rest.git.createTree;
this.githubApiClient.rest.repos.createOrUpdateFileContents({
owner,
repo,
Expand All @@ -121,4 +120,21 @@ export class Github {
return false;
}
}

async createRepository({
name,
description,
}: {
name: string;
description: string;
}) {
const { data } =
await this.githubApiClient.rest.repos.createForAuthenticatedUser({
name,
description,
});

if (!data) return false;
return true;
}
}
8 changes: 8 additions & 0 deletions src/base/shared.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ interface EventBus {
type PersistenceKey = string;
type Persist = <T>(key: PersistenceKey, value: T) => Promise<void>;
type Retrieve = <T>(key: PersistenceKey) => Promise<T | null>;

/* Github */
type AccessToken = Brand<string, 'accessToken'>;

/* Utility Type */
type Brand<T, K extends string> = T & {
_brand: K;
};
File renamed without changes.
47 changes: 47 additions & 0 deletions src/base/utils/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
export function createStorageKey(key: string) {
return `code-vault:${key}`;
}

export function defineBrowserStorage<T>({
storage,
key,
}: {
storage: Storage;
key: string;
}) {
const enrichedKey = createStorageKey(key);
return {
persist(data: T) {
storage.setItem(enrichedKey, JSON.stringify(data));
},
retrieve() {
return storage.getItem(enrichedKey);
},
clear() {
storage.removeItem(enrichedKey);
},
};
}

export function defineChromeExtensionStorage<T>({
storage,
key,
}: {
storage: chrome.storage.StorageArea;
key: string;
}) {
const enrichedKey = createStorageKey(key);
return {
async persist(data: T) {
await storage.set({ [enrichedKey]: JSON.stringify(data) });
},
async retrieve(): Promise<T | null> {
const res = await storage.get(enrichedKey);
const value = res?.[enrichedKey];
return value === undefined || value === null ? null : JSON.parse(value);
},
async clear() {
await storage.remove(enrichedKey);
},
};
}
22 changes: 22 additions & 0 deletions src/contexts/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ReloadIcon } from '@radix-ui/react-icons';
import { useIsMutating } from '@tanstack/react-query';
import { createPortal } from 'react-dom';

export function Loading() {
const isMutating = useIsMutating();

return (
<div>
{isMutating &&
createPortal(
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div className="flex flex-col items-center space-y-2 pb-3">
<ReloadIcon className="h-5 w-5 animate-spin" />
<p className="text-muted-foreground">잠시만 기다려주세요</p>
</div>
</div>,
document.body,
)}
</div>
);
}
33 changes: 33 additions & 0 deletions src/features/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Github } from '@base/services/github';
import { defineChromeExtensionStorage } from '@base/utils/storage';

const accessTokenStorage = defineChromeExtensionStorage<AccessToken>({
storage: chrome.storage.local,
key: 'access-token',
});

async function getAccessTokenFromSearchParams() {
const code = new URLSearchParams(location.search).get('code');
if (!code) return null;

history.replaceState('', '', location.pathname);

const accessToken = await Github.getAccessToken(code);
return accessToken as AccessToken;
}

async function login() {
const token = await getAccessTokenFromSearchParams();
if (!token) return undefined;

await accessTokenStorage.persist(token);

return token;
}

async function logout() {
await accessTokenStorage.clear();
return true;
}

export { login, logout, accessTokenStorage };
Loading

0 comments on commit 428e0f1

Please sign in to comment.