Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

Commit

Permalink
adds top toolbar, including undo (#187)
Browse files Browse the repository at this point in the history
* adds top toolbar

* remove background change

* add long lived connection for top -> main nest component comms

* improve source value

* add undo doesnt handle empty edits / edge cases

* add message to move reactor back into mocksi-extension.tsx

* make sure top iframe knows when # edits updates

* cleanup logging, make sure edits are passed to extension/top

* remove unused listener

* coderrabbit PR feedback

* save merge conflict resolved

---------

Co-authored-by: Kayla Fitzsimmons <[email protected]>
Co-authored-by: Kayla Fitzsimmons <[email protected]>
  • Loading branch information
3 people authored Sep 20, 2024
1 parent f5ec020 commit 52be27e
Show file tree
Hide file tree
Showing 5 changed files with 405 additions and 248 deletions.
96 changes: 73 additions & 23 deletions apps/mocksi-lite-next/src/pages/background/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { AppEvents, AuthEvents, LayoutEvents } from "@pages/events";
import {
AppEvents,
AuthEvents,
DemoEditEvents,
LayoutEvents,
} from "@pages/events";
import { jwtDecode } from "jwt-decode";

console.log("background script loaded");
Expand Down Expand Up @@ -109,6 +114,19 @@ addEventListener("install", () => {
});
});

let mainIframeSrcPort: null | chrome.runtime.Port = null;
let topIframeSrcPort: null | chrome.runtime.Port = null;

chrome.runtime.onConnectExternal.addListener((port) => {
console.log("connecting...", port);
if (port.name === "extension/main") {
mainIframeSrcPort = port;
}
if (port.name === "extension/top") {
topIframeSrcPort = port;
}
});

// when user clicks toolbar mount extension
chrome.action.onClicked.addListener((tab) => {
if (!tab?.id) {
Expand All @@ -131,28 +149,33 @@ chrome.action.onClicked.addListener((tab) => {
}
});

chrome.runtime.onMessage.addListener(
(request, _sender, sendResponse): boolean => {
sendResponse({
data: request.data,
message: request.message,
status: "ok",
});
return true;
},
);

chrome.runtime.onMessageExternal.addListener(
(request, _sender, sendResponse) => {
console.log("on message external: ", request);

// execute in async block so that we return true
// synchronously, telling chrome to wait for the response
(async () => {
if (
request.source === "extension/top" &&
request.message === AppEvents.EDIT_DEMO_STOP
) {
if (mainIframeSrcPort) {
// notify extension/main that demo edit mode exited in extension/top
mainIframeSrcPort.postMessage({
...request,
message: AppEvents.EDIT_DEMO_STOP,
});
} else {
console.log("mainIframeSrcPort is not connected");
}
}

if (request.message === AuthEvents.AUTH_ERROR) {
await clearAuth();
sendResponse({
message: AuthEvents.RETRY,
source: "background",
status: "ok",
});
} else if (request.message === AuthEvents.UNAUTHORIZED) {
Expand All @@ -162,12 +185,14 @@ chrome.runtime.onMessageExternal.addListener(
const tab = await getCurrentTab();
sendResponse({
message: { accessToken, email, url: tab?.url },
source: "background",
status: "ok",
});
} else {
await showAuthTab(true);
sendResponse({
message: AuthEvents.AUTHENTICATING,
source: "background",
status: "ok",
});
}
Expand All @@ -176,9 +201,11 @@ chrome.runtime.onMessageExternal.addListener(
if (!tab?.id) {
sendResponse({
message: LayoutEvents.NO_TAB,
source: "background",
status: "ok",
});
return;
console.error("No tab found");
return true;
}

if (
Expand All @@ -201,16 +228,39 @@ chrome.runtime.onMessageExternal.addListener(
showDefaultIcon(tab.id);
}

chrome.tabs.sendMessage(
tab.id,
{
data: request.data,
message: request.message,
},
(response) => {
sendResponse(response);
},
);
// send message to iframes and reactor in mocksi-extension
chrome.tabs.sendMessage(tab.id, request, async (response) => {
console.log("response from content script in background:", response);
if (response.message === DemoEditEvents.UNDO) {
// pass updated modifications from reactor to extension/main to store
if (mainIframeSrcPort) {
await mainIframeSrcPort.postMessage({
...response,
status: "ok", // response handler expects status
});
} else {
console.log("mainIframeSrcPort is not connected");
}
}
if (
request.message === AppEvents.EDIT_DEMO_START ||
request.message === DemoEditEvents.NEW_EDIT ||
request.message === DemoEditEvents.CHAT_RESPONSE
) {
// notify extension/top # of edits changed
if (topIframeSrcPort) {
await topIframeSrcPort.postMessage({
...response,
status: "ok",
});
} else {
console.log("topIframeSrcPort is not connected");
}
}

sendResponse(response);
});
return true;
}
})();

Expand Down
123 changes: 123 additions & 0 deletions apps/mocksi-lite-next/src/pages/content/main-iframe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React from "react";
import ReactDOM from "react-dom";
import { LayoutEvents } from "../events";

export enum IframePosition {
BOTTOM_CENTER = "BOTTOM_CENTER",
TOP_RIGHT = "TOP_RIGHT",
BOTTOM_RIGHT = "BOTTOM_RIGHT",
}

export interface IframeResizeArgs {
height: number;
id?: string;
position:
| IframePosition.BOTTOM_CENTER
| IframePosition.BOTTOM_RIGHT
| IframePosition.TOP_RIGHT;
width: number;
}

function getIframeStyles({ height, position, width }: IframeResizeArgs) {
if (!height || !width || !position) {
console.error(
"Cannot update iframe size / position, make sure 'request.data.iframe' has 'height', 'width', and 'position' set correctly",
);
return;
}

const bounds = document.body.getBoundingClientRect();

let styles = {};
switch (position) {
case IframePosition.BOTTOM_CENTER:
styles = {
bottom: "0px",
right: `${bounds.width / 2 - width / 2}px`,
top: "auto",
};
break;
case IframePosition.BOTTOM_RIGHT:
styles = {
bottom: "10px",
right: "10px",
top: "auto",
};
break;
case IframePosition.TOP_RIGHT:
styles = {
bottom: "auto",
display: "block",
right: "10px",
top: "10px",
};
break;
}

return Object.assign(
{
bottom: "auto",
display: "block",
height: `${height}px`,
left: "auto",
right: "auto",
top: "auto",
width: `${width}px`,
},
styles,
);
}

function MainIframe() {
const iframeRef = React.useRef<HTMLIFrameElement>(null);

React.useEffect(() => {
chrome.runtime.onMessage.addListener((request) => {
if (iframeRef.current) {
switch (request.message) {
case LayoutEvents.HIDE:
iframeRef.current.style.display = "none";
break;
case LayoutEvents.RESIZE:
const styles = getIframeStyles(request.data.iframe);
Object.assign(iframeRef.current.style, styles);
break;
case LayoutEvents.SHOW:
iframeRef.current.style.display = "block";
break;
}
}
return true;
});
}, []);

return (
<>
{ReactDOM.createPortal(
<iframe
ref={iframeRef}
seamless={true}
src={`${import.meta.env.VITE_NEST_APP}/extension/main`}
style={{
backgroundColor: "transparent",
border: "none",
bottom: "10px",
boxShadow: "none",
colorScheme: "light",
display: "block",
height: "600px",
left: "auto",
position: "fixed",
right: "10px",
top: "auto",
width: "500px",
zIndex: 99998,
}}
/>,
document.body,
)}
</>
);
}

export default MainIframe;
Loading

0 comments on commit 52be27e

Please sign in to comment.