From e9ae332cba3106916b1065aba72bb8aed826f3cb Mon Sep 17 00:00:00 2001 From: junghyeonsu Date: Tue, 31 Oct 2023 15:39:22 +0900 Subject: [PATCH] feat: JuneConext & track deploy --- figma-plugin/ui-src/contexts/JuneContext.tsx | 98 ++++++++++++++++++++ figma-plugin/ui-src/hooks/useJune.ts | 62 ------------- figma-plugin/ui-src/main.tsx | 16 +++- figma-plugin/ui-src/pages/App.tsx | 13 +-- figma-plugin/ui-src/pages/Deploy.tsx | 28 ++++-- 5 files changed, 136 insertions(+), 81 deletions(-) create mode 100644 figma-plugin/ui-src/contexts/JuneContext.tsx delete mode 100644 figma-plugin/ui-src/hooks/useJune.ts diff --git a/figma-plugin/ui-src/contexts/JuneContext.tsx b/figma-plugin/ui-src/contexts/JuneContext.tsx new file mode 100644 index 0000000..b9ca69e --- /dev/null +++ b/figma-plugin/ui-src/contexts/JuneContext.tsx @@ -0,0 +1,98 @@ +import React from "react"; + +interface JuneContextType { + identify: (props: IdentifyProps) => Promise; + track: (props: TrackProps) => Promise; +} + +interface JuneProviderProps { + writeKey: string; + children: React.ReactNode; + + disabled?: boolean; +} + +interface TrackProps { + event: string; + timestamp?: Date; + userId?: string; + anonymousId?: string; + properties?: Record; + context?: Record; +} + +interface IdentifyProps { + userId?: string; + anonymousId?: string; + traits?: Record; + timestamp?: Date; + context?: Record; +} + +const BASE_URL = "https://api.june.so/sdk"; + +const JuneContext = React.createContext({ + identify: async () => {}, + track: async () => {}, +}); + +export const JuneProvider: React.FC = ({ + children, + writeKey, + disabled = false, +}) => { + if (!writeKey) { + throw new Error("[useJune] writeKey is required"); + } + + const headers = { + Authorization: `Basic ${writeKey}`, + "Content-Type": "application/json", + }; + + const track = async (props: TrackProps) => { + if (disabled) return; + + try { + await fetch(`${BASE_URL}/track`, { + method: "POST", + headers, + body: JSON.stringify({ + ...props, + }), + }); + } catch (error) { + console.error(error); + } + }; + + const identify = async (props: IdentifyProps) => { + if (!props.userId || disabled) return; + + try { + await fetch(`${BASE_URL}/identify`, { + method: "POST", + headers, + body: JSON.stringify({ + ...props, + }), + }); + } catch (error) { + console.error(error); + } + }; + + return ( + + {children} + + ); +}; + +export const useJune = () => { + const context = React.useContext(JuneContext); + if (!context) { + throw new Error("[useJune] must be used within a JuneProvider"); + } + return context; +}; diff --git a/figma-plugin/ui-src/hooks/useJune.ts b/figma-plugin/ui-src/hooks/useJune.ts deleted file mode 100644 index 25c3636..0000000 --- a/figma-plugin/ui-src/hooks/useJune.ts +++ /dev/null @@ -1,62 +0,0 @@ -interface UseJuneProps { - writeKey: string; - devMode?: boolean; -} - -interface IdentifyProps { - userId?: string; - email?: string; - userName?: string; -} - -const BASE_URL = "https://api.june.so/sdk"; - -export function useJune({ writeKey }: UseJuneProps) { - if (!writeKey) { - throw new Error("[useJune] writeKey is required"); - } - - const headers = { - Authorization: `Basic ${writeKey}`, - "Content-Type": "application/json", - }; - - const track = async (event: string) => { - try { - await fetch(`${BASE_URL}/track`, { - method: "POST", - headers, - body: JSON.stringify({ - event, - }), - }); - } catch (error) { - console.error(error); - } - }; - - /** - * 현재는 figma에서는 이메일을 받아올 수 있는 수단이 없음 - */ - const identify = async ({ userName }: IdentifyProps) => { - if (!userName) { - return; - } - - try { - await fetch(`${BASE_URL}/identify`, { - method: "POST", - headers, - body: JSON.stringify({ - traits: { - username: userName, - }, - }), - }); - } catch (error) { - console.error(error); - } - }; - - return { track, identify }; -} diff --git a/figma-plugin/ui-src/main.tsx b/figma-plugin/ui-src/main.tsx index 64941af..484c306 100644 --- a/figma-plugin/ui-src/main.tsx +++ b/figma-plugin/ui-src/main.tsx @@ -6,15 +6,21 @@ import * as React from "react"; import { createRoot } from "react-dom/client"; import { AppProvider } from "./contexts/AppContext"; +import { JuneProvider } from "./contexts/JuneContext"; import App from "./pages/App"; const root = createRoot(document.getElementById("root")!); root.render( - - - - - + + + + + + + , ); diff --git a/figma-plugin/ui-src/pages/App.tsx b/figma-plugin/ui-src/pages/App.tsx index 77c8e05..901f2a7 100644 --- a/figma-plugin/ui-src/pages/App.tsx +++ b/figma-plugin/ui-src/pages/App.tsx @@ -2,19 +2,20 @@ import { Box, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; import * as React from "react"; import { useAppState } from "../contexts/AppContext"; -import { useJune } from "../hooks/useJune"; +import { useJune } from "../contexts/JuneContext"; import * as styles from "./App.css"; import Deploy from "./Deploy"; import Setting from "./Setting"; const App = () => { const { userName } = useAppState(); - const { track, identify } = useJune({ - writeKey: import.meta.env.VITE_JUNE_SO_WRITE_KEY, - }); + const { track } = useJune(); - identify({ userName }); - track("icona:plugin_open"); + track({ + event: "icona:plugin_open", + properties: { userName }, + timestamp: new Date(), + }); return ( diff --git a/figma-plugin/ui-src/pages/Deploy.tsx b/figma-plugin/ui-src/pages/Deploy.tsx index 4b68878..4740239 100644 --- a/figma-plugin/ui-src/pages/Deploy.tsx +++ b/figma-plugin/ui-src/pages/Deploy.tsx @@ -3,12 +3,14 @@ import * as React from "react"; import { ACTION, DATA, STATUS } from "../../common/constants"; import { useAppDispatch, useAppState } from "../contexts/AppContext"; +import { useJune } from "../contexts/JuneContext"; import * as styles from "./Deploy.css"; const Deploy = () => { const dispatch = useAppDispatch(); const { deployIconStatus, githubData, iconPreview } = useAppState(); const icons = Object.entries(iconPreview); + const { track } = useJune(); const buttonInfo = { [STATUS.IDLE]: { @@ -29,6 +31,23 @@ const Deploy = () => { }, }; + const deploy = () => { + dispatch({ + type: ACTION.DEPLOY_ICON, + payload: { + githubData, + }, + }); + track({ + event: "icona:deploy_icon", + properties: { + githubRepositoryName: githubData.name, + githubRepositoryOwner: githubData.owner, + }, + timestamp: new Date(), + }); + }; + return ( @@ -66,14 +85,7 @@ const Deploy = () => { deployIconStatus === STATUS.SUCCESS || deployIconStatus === STATUS.ERROR } - onClick={() => - dispatch({ - type: ACTION.DEPLOY_ICON, - payload: { - githubData, - }, - }) - } + onClick={deploy} colorScheme={buttonInfo[deployIconStatus].colorScheme} > {buttonInfo[deployIconStatus].children}