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

[view] Theme Selector 테마 전역 저장 및 코드 리팩토링 #760

Merged
merged 6 commits into from
Oct 10, 2024
Merged
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
11 changes: 7 additions & 4 deletions packages/view/public/index.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
<script>
window.isProduction = false;
window.primaryColor = '#ffbbff';
window.isProduction = false;
window.theme = "githru";
</script>
<title>Githru</title>
</head>
Expand Down
93 changes: 54 additions & 39 deletions packages/view/src/components/ThemeSelector/ThemeSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import "./ThemeSelector.scss";
import AutoAwesomeIcon from "@mui/icons-material/AutoAwesome";
import CloseIcon from "@mui/icons-material/Close";

const themes = [
import { setCustomTheme } from "services";

type ThemeInfo = {
title: string;
value: string;
colors: {
primary: string;
secondary: string;
tertiary: string;
};
};

type ThemeIconsProps = ThemeInfo & {
onClick: () => void;
};

const themeInfo: ThemeInfo[] = [
{
title: "Githru",
value: "githru",
Expand Down Expand Up @@ -52,83 +68,82 @@ const themes = [
},
];

type ThemeIconsProps = {
title: string;
value: string;
colors: {
primary: string;
secondary: string;
tertiary: string;
};
onClick: () => void;
};

const ThemeIcons = ({ title, value, colors, onClick }: ThemeIconsProps) => {
const [isSelected, setIsSelected] = useState<string>("");
const [selectedItem, setSelectedItem] = useState<string>("");

useEffect(() => {
const selectedTheme = document.documentElement.getAttribute("custom-type");
if (selectedTheme) setIsSelected(selectedTheme);
if (selectedTheme) setSelectedItem(selectedTheme);
}, []);

return (
<div
className={`theme-icon${isSelected === value ? "--selected" : ""}`}
className={`theme-icon${selectedItem === value ? "--selected" : ""}`}
onClick={onClick}
role="presentation"
>
<div className="theme-icon__container">
<div
className="theme-icon__color"
style={{ backgroundColor: colors.primary }}
/>
<div
className="theme-icon__color"
style={{ backgroundColor: colors.secondary }}
/>
<div
className="theme-icon__color"
style={{ backgroundColor: colors.tertiary }}
/>
{Object.values(colors).map((color, index) => (
<div
key={Number(index)}
className="theme-icon__color"
style={{ backgroundColor: color }}
/>
))}
</div>
Comment on lines +86 to +92
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분 너무 깔끔하고 좋은거 같습니다!!

<p className="theme-icon__title">{title}</p>
</div>
);
};

const ThemeSelector = () => {
const [open, setOpen] = useState<boolean>(false);
const [isOpen, setIsOpen] = useState<boolean>(false);
const themeSelectorRef = useRef<HTMLDivElement>(null);

const handleTheme = (value: string) => {
setCustomTheme(value);
document.documentElement.setAttribute("custom-type", value);
};

useEffect(() => {
document.documentElement.setAttribute("custom-type", "githru");
const handleClickOutside = (event: MouseEvent) => {
if (themeSelectorRef.current && !themeSelectorRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);

Comment on lines +109 to +118
Copy link
Member

Choose a reason for hiding this comment

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

(궁금💭)

if (themeSelectorRef.current && !themeSelectorRef.current.contains(event.target as Node))

해당 조건이 어떤 이유로 필요한지 궁금합니다! ?ㅅ?

Copy link
Contributor Author

@taboowiths taboowiths Oct 7, 2024

Choose a reason for hiding this comment

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

해당 부분은 theme selector의 바깥 영역을 확인하는 코드인데요,
"mousedown" 이벤트가 발생해 handleClickOutside() 함수가 실행되면,

  1. themeSelectorRef.current 코드를 통해 현재 시점에서 themeSelectorRef가 DOM에 있는지 확인하게 됩니다!
  2. 그리고 DOM에 있다면, !themeSelectorRef.current.contains(event.target as Node) 코드를 통해 현재 이벤트의 타겟인 클릭 지점이 theme selector의 영역 안에 있는지 확인합니다. 클릭 지점이 ref의 영역 안에 있으면 setIsOpen(false)를 실행하지 않아 창이 닫히지 않게 되고, 영역 밖에 있으면 닫히게 됩니다!!

useEffect(() => {
document.documentElement.setAttribute("custom-type", window.theme);
}, []);

return (
<div className="theme-selector">
<AutoAwesomeIcon onClick={() => setOpen(true)} />
{open && (
<div
className="theme-selector"
ref={themeSelectorRef}
>
<AutoAwesomeIcon onClick={() => setIsOpen(true)} />
{isOpen && (
<div className="theme-selector__container">
<div className="theme-selector__header">
<p>Theme</p>
<CloseIcon
fontSize="small"
onClick={() => setOpen(false)}
onClick={() => setIsOpen(false)}
/>
</div>
<div className="theme-selector__list">
{themes.map((theme) => (
{themeInfo.map((theme) => (
<ThemeIcons
key={theme.value}
title={theme.title}
value={theme.value}
colors={theme.colors}
{...theme}
onClick={() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍👍👍👍👍👍👍

handleTheme(theme.value);
setOpen(false);
setIsOpen(false);
}}
/>
))}
Expand Down
8 changes: 4 additions & 4 deletions packages/view/src/ide/FakeIDEAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ export default class FakeIDEAdapter implements IDEPort {
this.sendMessageToMe(message);
}

public setPrimaryColor(color: string) {
public setCustomTheme(color: string) {
sessionStorage.setItem("PRIMARY_COLOR", color);
const message: IDEMessage = {
command: "updatePrimaryColor",
command: "updateCustomTheme",
};
this.sendMessageToMe(message);
}
Expand All @@ -76,10 +76,10 @@ export default class FakeIDEAdapter implements IDEPort {
command,
payload: JSON.stringify(fakeBranchList),
};
case "updatePrimaryColor":
case "updateCustomTheme":
return {
command,
payload: sessionStorage.getItem("PRIMARY_COLOR") as string,
payload: sessionStorage.getItem("CUSTOM_THEME") as string,
};
default:
return {
Expand Down
2 changes: 1 addition & 1 deletion packages/view/src/ide/IDEPort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ export default interface IDEPort {
sendRefreshDataMessage: (payload?: string) => void;
sendFetchAnalyzedDataMessage: (payload?: string) => void;
sendFetchBranchListMessage: () => void;
setPrimaryColor: (color: string) => void;
setCustomTheme: (theme: string) => void;
Copy link
Contributor

@xxxjinn xxxjinn Oct 7, 2024

Choose a reason for hiding this comment

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

사소한 부분이지만 실제 setCustomTheme 함수의 매개변수는 color라고 되어있는 것 같습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

setCustomTheme() 함수를 여러 군데에서 이름만 동일하게 사용하다보니 헷갈려 이런 문제가 생기네요 ... !

어느 부분에서 사용되는 함수인지 명시적으로 사용해야겠습니다 ............
찾아주셔서 감사합니다 !!!!!!!!!!! 👍👍👍👍👍👍

}
6 changes: 3 additions & 3 deletions packages/view/src/ide/VSCodeIDEAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ export default class VSCodeIDEAdapter implements IDEPort {
this.sendMessageToIDE(message);
}

public setPrimaryColor(color: string) {
public setCustomTheme(theme: string) {
const message: IDEMessage = {
command: "updatePrimaryColor",
payload: JSON.stringify({ primary: color }),
command: "updateCustomTheme",
payload: JSON.stringify({ theme }),
};
this.sendMessageToIDE(message);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/view/src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { container } from "tsyringe";

import type IDEPort from "ide/IDEPort";

export const setPrimaryColor = (color: string) => {
export const setCustomTheme = (color: string) => {
const ideAdapter = container.resolve<IDEPort>("IDEAdapter");
ideAdapter.setPrimaryColor(color);
ideAdapter.setCustomTheme(color);
};

export const sendFetchAnalyzedDataCommand = (selectedBranch?: string) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/view/src/types/IDEMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ export type IDEMessageCommandNames =
| "fetchAnalyzedData"
| "fetchBranchList"
| "fetchCurrentBranch"
| "updatePrimaryColor";
| "updateCustomTheme";
2 changes: 1 addition & 1 deletion packages/view/src/types/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ interface Window {
githruNodesData: unknown;
githruBranchesData: unknown;
isProduction: boolean;
primaryColor: string;
theme: string;
}

declare module "*.svg" {
Expand Down
6 changes: 3 additions & 3 deletions packages/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@
"configuration": {
"title": "Githru",
"properties": {
"githru.color.primary": {
"githru.theme": {
"type": "string",
"default": "#ff8272",
"description": "Insert your primary color."
"default": "githru",
"description": "Insert your theme name: githru, hacker-blue, aqua, cotton-candy, mono"
}
}
}
Expand Down
23 changes: 11 additions & 12 deletions packages/vscode/src/setting-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as vscode from "vscode";

const SETTING_PROPERTY_NAMES = {
GITHUB_TOKEN: "githru.github.token",
PRIMARY_COLOR: "githru.color.primary",
THEME: "githru.theme",
};

export const getGithubToken = async (secrets: vscode.SecretStorage) => {
Expand All @@ -14,22 +14,21 @@ export const setGithubToken = async (secrets: vscode.SecretStorage, newGithubTok
};

export const deleteGithubToken = async (secrets: vscode.SecretStorage) => {
return await secrets.delete(SETTING_PROPERTY_NAMES.GITHUB_TOKEN);
}
return await secrets.delete(SETTING_PROPERTY_NAMES.GITHUB_TOKEN);
};

export const setPrimaryColor = (color: string) => {
export const setTheme = async (newTheme: string) => {
const configuration = vscode.workspace.getConfiguration();
configuration.update(SETTING_PROPERTY_NAMES.PRIMARY_COLOR, color);
configuration.update(SETTING_PROPERTY_NAMES.THEME, newTheme);
};

export const getPrimaryColor = (): string => {
export const getTheme = async (): Promise<string> => {
Comment on lines +20 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

(궁금) async를 왜 달아주셨는지 궁금합니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이 부분 트러블슈팅 하다가 configuration 가져오는 부분이 비동기인가 싶어서 추가했던 것 같습니다 .. !
지금 확인해보니 async처리 하지 않아도 잘 동작하네요 ㅎㅎ ...

다음 이슈에서 수정하도록 하겠습니다 !!

const configuration = vscode.workspace.getConfiguration();
const primaryColor = configuration.get(SETTING_PROPERTY_NAMES.PRIMARY_COLOR) as string;
const theme = configuration.get(SETTING_PROPERTY_NAMES.THEME) as string;

if (!primaryColor) {
configuration.update(SETTING_PROPERTY_NAMES.PRIMARY_COLOR, "#ff8272");
return "#ff8272";
} else {
return primaryColor;
if (!theme) {
await setTheme("githru");
return "githru";
}
return theme;
};
24 changes: 15 additions & 9 deletions packages/vscode/src/webview-loader.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as path from "path";
import * as vscode from "vscode";

import { getPrimaryColor, setPrimaryColor } from "./setting-repository";
import { getTheme, setTheme } from "./setting-repository";
import type { ClusterNode } from "./types/Node";

const ANALYZE_DATA_KEY = "memento_analyzed_data";
Expand Down Expand Up @@ -31,13 +31,13 @@

const icon_path = vscode.Uri.file(path.join(this.extensionPath, "images", "logo.png"));
this._panel.iconPath = icon_path;
let analyzedData;

Check warning on line 34 in packages/vscode/src/webview-loader.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'analyzedData' is defined but never used
this._panel.webview.onDidReceiveMessage(async (message: { command: string; payload?: string }) => {
try {
const { command, payload } = message;

if (command === "fetchAnalyzedData" || command === "refresh") {
const baseBranchName = (payload && JSON.parse(payload)) ?? (await fetchCurrentBranch());

Check warning on line 40 in packages/vscode/src/webview-loader.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'baseBranchName' is assigned a value but never used
try {
const baseBranchName = (payload && JSON.parse(payload)) ?? (await fetchCurrentBranch());
const storedAnalyzedData = context.workspaceState.get<ClusterNode[]>(
Expand Down Expand Up @@ -77,10 +77,10 @@
});
}

if (command === "updatePrimaryColor") {
if (command === "updateCustomTheme") {
const colorCode = payload && JSON.parse(payload);
if (colorCode.primary) {
setPrimaryColor(colorCode.primary);
if (colorCode.theme) {
setTheme(colorCode.theme);
}
}
} catch (e) {
Expand All @@ -92,7 +92,7 @@
// this.dispose();
// throw new Error("Project not connected to Git.");
// }
this._panel.webview.html = this.getWebviewContent(this._panel.webview);
this.setWebviewContent();
}

dispose() {
Expand All @@ -111,13 +111,12 @@
});
}

private getWebviewContent(webview: vscode.Webview): string {
private async getWebviewContent(webview: vscode.Webview): Promise<string> {
const reactAppPathOnDisk = vscode.Uri.file(path.join(this.extensionPath, "dist", "webviewApp.js"));
const reactAppUri = webview.asWebviewUri(reactAppPathOnDisk);
// const reactAppUri = reactAppPathOnDisk.with({ scheme: "vscode-resource" });

const primaryColor = getPrimaryColor();

const theme = await getTheme();
const returnString = `
<!DOCTYPE html>
<html lang="en">
Expand All @@ -127,7 +126,7 @@
<title>githru-vscode-ext webview</title>
<script>
window.isProduction = true;
window.primaryColor = "${primaryColor}";
window.theme = "${theme}";
</script>
</head>
<body>
Expand All @@ -141,6 +140,13 @@
`;
return returnString;
}

private async setWebviewContent() {
if (this._panel) {
this._panel.webview.html = await this.getWebviewContent(this._panel.webview);
}
}

public setGlobalOwnerAndRepo(owner: string, repo: string) {
if (this._panel) {
this._panel.webview.postMessage({
Expand Down
Loading