Skip to content

Commit

Permalink
[NC] [MOC-18] Play mode UI flow (#67)
Browse files Browse the repository at this point in the history
* [NC]: Adds UI changes to allow play mode

* [NC]: Adds hidden state and new toast component

* [NC]: Reset mocksi icon message

* biome fixes

* [NC]: Minor icon render fix

* [NC]: missing demo date on hidden toast

* [NC]: merge fixes

* [NC]: merge fixes

* [NC]: highlight changes on play mode

* [NC]: load highlighted alterations on edit press

* [NC]: root position fix

* [NC]: root position fix
  • Loading branch information
nicolaschapur committed Jun 21, 2024
1 parent dd9eaac commit 9f6e8bc
Show file tree
Hide file tree
Showing 18 changed files with 391 additions and 150 deletions.
15 changes: 15 additions & 0 deletions apps/mocksi-lite/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,21 @@ chrome.runtime.onMessage.addListener(
return true;
}

if (request.message === "updateToPauseIcon") {
chrome.action.setIcon({ path: "./public/pause-icon.png" });
return true;
}

if (request.message === "updateToPlayIcon") {
chrome.action.setIcon({ path: "./public/play-icon.png" });
return true;
}

if (request.message === "resetIcon") {
chrome.action.setIcon({ path: "./public/mocksi-icon.png" });
return true;
}

sendResponse({ message: request.message, status: "fail" });
return false; // No async response for other messages
},
Expand Down
2 changes: 1 addition & 1 deletion apps/mocksi-lite/common/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const getButtonStyles = (variant: Variant) => {
case Variant.primary:
return "bg-[#E8F3EC] border-[#E8F3EC] px-6";
case Variant.icon:
return "bg-[#E8F3EC] border-[#E8F3EC] p-3";
return "bg-[#E8F3EC] border-[#E8F3EC] p-3 max-h-[42px] h-[42px]";
case Variant.secondary:
return "border-[#009875] px-6";
default:
Expand Down
3 changes: 3 additions & 0 deletions apps/mocksi-lite/consts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const MOCKSI_RECORDING_STATE = "mocksi-recordingState";
export const MOCKSI_RECORDING_ID = "mocksi-recordingId";
export const MOCKSI_ALTERATIONS = "mocksi-alterations";
export const MOCKSI_MODIFICATIONS = "mocksi-modifications";
export const COOKIE_NAME = "sessionid";
export const MOCKSI_ACCESS_TOKEN = "mocksi-accessToken";
Expand All @@ -23,6 +24,8 @@ export enum RecordingState {
ANALYZING = "ANALYZING",
CREATE = "CREATE",
EDITING = "EDITING",
PLAY = "PLAY",
HIDDEN = "HIDDEN",
}

export const popupTitle = "Tip & Tricks";
Expand Down
142 changes: 27 additions & 115 deletions apps/mocksi-lite/content/ContentApp.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,23 @@
import { useEffect, useState } from "react";
import useShadow from "use-shadow-dom";
import TextField from "../common/TextField";
import {
MOCKSI_RECORDING_ID,
MOCKSI_RECORDING_STATE,
RecordingState,
} from "../consts";
import closeIcon from "../public/close-icon.png";
import mocksiLogo from "../public/mocksi-logo.png";
import { MOCKSI_RECORDING_STATE, RecordingState } from "../consts";
import { setRootPosition } from "../utils";
import { setEditorMode } from "./EditMode/editMode";
import { getHighlighter } from "./EditMode/highlighter";
import Popup from "./Popup";
import { RecordButton } from "./RecordButton";
import EditToast from "./Toast/EditToast";
import HiddenToast from "./Toast/HiddenToast";
import PlayToast from "./Toast/PlayToast";
import RecordingToast from "./Toast/RecordingToast";

interface ContentProps {
isOpen?: boolean;
email: string | null;
}
const recordingLabel = (currentStatus: RecordingState) => {
switch (currentStatus) {
case RecordingState.READY:
return "Start recording";
case RecordingState.RECORDING:
return "Mocksi Recording";
case RecordingState.EDITING:
return "Editing Template";
case RecordingState.ANALYZING:
return "Analyzing...";
case RecordingState.UNAUTHORIZED:
return "Login to record";
default:
return "Start recording";
}
};

function ShadowContentApp({ isOpen, email }: ContentProps) {
const [isDialogOpen, setIsDialogOpen] = useState(isOpen || false);
const [areChangesHighlighted, setAreChangesHighlighted] = useState(true);
const [state, setState] = useState<RecordingState>(
RecordingState.UNAUTHORIZED,
);
const ContentHighlighter = getHighlighter();

useEffect(() => {
// Load initial state from chrome storage
Expand All @@ -59,29 +35,13 @@ function ShadowContentApp({ isOpen, email }: ContentProps) {
chrome.storage.local.set({ [MOCKSI_RECORDING_STATE]: newState });
};

const onChecked = () => {
setAreChangesHighlighted((prevValue) => {
ContentHighlighter.showHideHighlights(!prevValue);
return !prevValue;
});
};

const loadRecordingId = async () => {
return new Promise<string | undefined>((resolve) => {
chrome.storage.local.get([MOCKSI_RECORDING_ID], (result) => {
resolve(result[MOCKSI_RECORDING_ID]);
});
});
};

if (!isDialogOpen) {
return null;
}
if (state === RecordingState.READY || state === RecordingState.CREATE) {
return (
<Popup
state={state}
label={recordingLabel(state)}
close={() => setIsDialogOpen(false)}
setState={onChangeState}
email={email}
Expand All @@ -90,81 +50,33 @@ function ShadowContentApp({ isOpen, email }: ContentProps) {
}

if (state === RecordingState.EDITING) {
return <EditToast state={state} onChangeState={onChangeState} />;
}

if (state === RecordingState.PLAY) {
return (
<div className="border border-solid border-grey/40 rounded-l bg-white mt-3 min-w-64 p-3 flex flex-row items-center gap-6">
<div
className="cursor-pointer"
onClick={async () => {
onChangeState(RecordingState.CREATE);
const recordingId = await loadRecordingId();
setEditorMode(false, recordingId);
}}
onKeyUp={async (event) => {
if (event.key === "Escape") {
onChangeState(RecordingState.CREATE);
const recordingId = await loadRecordingId();
setEditorMode(false, recordingId);
}
}}
>
<img src={closeIcon} alt="closeIcon" />
</div>
<div className={"flex flex-col gap-2"}>
<TextField variant={"title"}>{recordingLabel(state)}</TextField>
<div className="flex gap-2 items-center">
<input
checked={areChangesHighlighted}
onChange={() => onChecked()}
type="checkbox"
className="h-5 w-5 !rounded-lg"
/>
<div className={"text-[13px] leading-[15px]"}>
Highlight All Previous Changes
</div>
</div>
</div>
<div
className="cursor-pointer text-[#009875]"
onClick={async () => {
onChangeState(RecordingState.CREATE);
const recordingId = await loadRecordingId();
setEditorMode(false, recordingId);
}}
onKeyUp={async (event) => {
if (event.key === "Enter") {
onChangeState(RecordingState.CREATE);
const recordingId = await loadRecordingId();
setEditorMode(false, recordingId);
}
}}
>
Done
</div>
</div>
<PlayToast
onChangeState={onChangeState}
close={() => setIsDialogOpen(false)}
/>
);
}

if (state === RecordingState.HIDDEN) {
return (
<HiddenToast
onChangeState={onChangeState}
close={() => setIsDialogOpen(false)}
/>
);
}

return (
<div className="border border-solid border-grey/40 rounded bg-white h-11 w-64 mt-4 mr-8 flex flex-row items-center justify-between">
<div className="flex flex-row gap-2 items-center">
<div
className="ml-2 cursor-pointer"
onClick={() => setIsDialogOpen(false)}
onKeyUp={(event) => {
event.key === "Escape" && setIsDialogOpen(false);
}}
>
<img src={closeIcon} alt="closeIcon" />
</div>
<img className="w-[30px] h-[20px]" src={mocksiLogo} alt="mocksiLogo" />
<span className="font-medium text-[#000F0C] text-sm">
{recordingLabel(state)}
</span>
</div>
{state !== RecordingState.UNAUTHORIZED && (
<RecordButton state={state} onRecordChange={onChangeState} />
)}
</div>
<RecordingToast
close={() => setIsDialogOpen(false)}
state={state}
onChangeState={onChangeState}
/>
);
}

Expand Down
23 changes: 18 additions & 5 deletions apps/mocksi-lite/content/Popup/CreateDemo/DemoItem.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import type { Recording } from "../../../background";
import Button, { Variant } from "../../../common/Button";
import TextField from "../../../common/TextField";
import { RecordingState } from "../../../consts";
import {
MOCKSI_ALTERATIONS,
MOCKSI_RECORDING_ID,
RecordingState,
} from "../../../consts";
import editIcon from "../../../public/edit-icon.png";
import exportIcon from "../../../public/export-icon.png";
import playIcon from "../../../public/play-icon.png";
import { loadAlterations } from "../../../utils";
import { setEditorMode } from "../../EditMode/editMode";

Expand All @@ -25,6 +29,13 @@ const DemoItem = ({
setState(RecordingState.EDITING);
};

const handlePlay = async () => {
await chrome.storage.local.set({ [MOCKSI_ALTERATIONS]: alterations });
await chrome.storage.local.set({ [MOCKSI_RECORDING_ID]: uuid });
loadAlterations(alterations, false);
setState(RecordingState.PLAY);
};

const domain = new URL(url).hostname;
return (
<div className={"flex justify-between w-full px-6"}>
Expand All @@ -34,7 +45,9 @@ const DemoItem = ({
</TextField>
<TextField className={"truncate"}>{customer_name}</TextField>
<a href={url} target={"_blank"} rel={"noreferrer"}>
<TextField className={"text-xs underline"}>{domain}</TextField>
<TextField className={"text-xs underline truncate"}>
{domain}
</TextField>
</a>
</div>
<div className={"flex gap-3"}>
Expand All @@ -47,14 +60,14 @@ const DemoItem = ({
</Button>
<Button
variant={Variant.icon}
onClick={() => loadAlterations(alterations, false)}
onClick={handlePlay}
disabled={
!url.includes(window.location.hostname) ||
!alterations ||
!alterations.length
}
>
<img src={exportIcon} alt={"exportIcon"} />
<img src={playIcon} alt={"playIcon"} />
</Button>
</div>
</div>
Expand Down
5 changes: 3 additions & 2 deletions apps/mocksi-lite/content/Popup/RecordDemo.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import TextField from "../../common/TextField";
import { type RecordingState, popupContent, popupTitle } from "../../consts";
import { recordingLabel } from "../../utils";
import { RecordButton } from "../RecordButton";

interface RecordDemoProps {
label: string;
state: RecordingState;
setState: (s: RecordingState) => void;
}

const RecordDemo = ({ label, state, setState }: RecordDemoProps) => {
const RecordDemo = ({ state, setState }: RecordDemoProps) => {
const label = recordingLabel(state);
return (
<>
<div
Expand Down
12 changes: 4 additions & 8 deletions apps/mocksi-lite/content/Popup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,17 @@ import RecordDemo from "./RecordDemo";

interface PopupProps {
close: () => void;
label: string;
setState: (r: RecordingState) => void;
state: RecordingState;
email: string | null;
}

const Popup = ({ label, close, setState, state, email }: PopupProps) => {
const Popup = ({ close, setState, state, email }: PopupProps) => {
const [createForm, setCreateForm] = useState<boolean>(false);

// NOTE: useEffect will retrigger on every render, because there is no second argument. This is fine because the debounce function will prevent the sendMessage from being called too often.
useEffect(() => {
debounce_leading(() => {
sendMessage("getRecordings");
}, 500);
});
sendMessage("getRecordings");
}, []);
const renderContent = () => {
switch (state) {
case RecordingState.CREATE:
Expand All @@ -36,7 +32,7 @@ const Popup = ({ label, close, setState, state, email }: PopupProps) => {
/>
);
default:
return <RecordDemo label={label} state={state} setState={setState} />;
return <RecordDemo state={state} setState={setState} />;
}
};

Expand Down
Loading

0 comments on commit 9f6e8bc

Please sign in to comment.