Skip to content

Commit

Permalink
Embed Community (#1303)
Browse files Browse the repository at this point in the history
* embed discuss

* update sidebar
  • Loading branch information
an-lee authored Mar 7, 2025
1 parent e96581f commit ff0f279
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 39 deletions.
1 change: 1 addition & 0 deletions enjoy/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const AI_WORKER_ENDPOINT = "https://ai-worker.enjoy.bot";

export const WEB_API_URL = "https://enjoy.bot";
export const WS_URL = "wss://enjoy.bot";
export const DISCUSS_URL = "https://discuss.enjoy.bot";

export const DOWNLOAD_URL = "https://1000h.org/enjoy-app/install.html";

Expand Down
104 changes: 103 additions & 1 deletion enjoy/src/main/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import settings from "@main/settings";
import downloader from "@main/downloader";
import fs from "fs-extra";
import log from "@main/logger";
import { REPO_URL, WS_URL } from "@/constants";
import { DISCUSS_URL, REPO_URL, WEB_API_URL, WS_URL } from "@/constants";
import { AudibleProvider, TedProvider, YoutubeProvider } from "@main/providers";
import Ffmpeg from "@main/ffmpeg";
import { Waveform } from "./waveform";
Expand Down Expand Up @@ -238,6 +238,108 @@ main.init = async () => {
view.webContents.loadURL(url);
}
);

ipcMain.handle(
"view-load-community",
(
event,
bounds: { x: number; y: number; width: number; height: number },
options?: {
navigatable?: boolean;
accessToken?: string;
url?: string;
ssoUrl?: string;
}
) => {
const {
x = 0,
y = 0,
width = mainWindow.getBounds().width,
height = mainWindow.getBounds().height,
} = bounds;
const { navigatable = false, accessToken, url = `${DISCUSS_URL}/login`, ssoUrl = `${WEB_API_URL}/discourse/sso` } = options || {};

logger.debug("view-load-community", url);
const view = new WebContentsView();
mainWindow.contentView.addChildView(view);

view.setBounds({
x: Math.round(x),
y: Math.round(y),
width: Math.round(width),
height: Math.round(height),
});

view.webContents.on("did-navigate", (_event, url) => {
event.sender.send("view-on-state", {
state: "did-navigate",
url,
});
});
view.webContents.on(
"did-fail-load",
(_event, _errorCode, errrorDescription, validatedURL) => {
event.sender.send("view-on-state", {
state: "did-fail-load",
error: errrorDescription,
url: validatedURL,
});
(view.webContents as any).destroy();
mainWindow.contentView.removeChildView(view);
}
);

view.webContents.on("will-redirect", (details) => {
const { url } = details;
event.sender.send("view-on-state", {
state: "will-redirect",
url,
});
// Login via SSO
if (url.includes(ssoUrl)) {
details.preventDefault();
// Auto login using access token
view.webContents.loadURL(url, {
extraHeaders: `Authorization: Bearer ${accessToken}\n`,
});
logger.debug("loading", url, "accessToken:", accessToken);
} else {
logger.debug("will-redirect", url);
}
});

view.webContents.on("will-navigate", (details) => {
const { url } = details;
event.sender.send("view-on-state", {
state: "will-navigate",
url,
});

logger.debug("will-navigate", url);

// Open in browser if not navigatable
if (!navigatable) {
logger.debug("prevent navigation", url);
details.preventDefault();
shell.openExternal(url);
}
});
view.webContents.loadURL(url);
}
);

ipcMain.handle("view-resize", (_event, bounds: { x: number; y: number; width: number; height: number }) => {
logger.debug("view-resize", bounds);
const view = mainWindow.contentView.children[0];
if (!view) return;

view.setBounds({
x: Math.round(bounds.x),
y: Math.round(bounds.y),
width: Math.round(bounds.width),
height: Math.round(bounds.height),
});
});

ipcMain.handle("view-remove", () => {
logger.debug("view-remove");
Expand Down
15 changes: 15 additions & 0 deletions enjoy/src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,21 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", {
scrape: (url: string) => {
return ipcRenderer.invoke("view-scrape", url);
},
resize: (bounds: {
x: number;
y: number;
width: number;
height: number;
}) => {
return ipcRenderer.invoke("view-resize", bounds);
},
loadCommunity: (
url: string,
bounds: { x: number; y: number; width: number; height: number },
options?: { navigatable?: boolean; accessToken?: string }
) => {
return ipcRenderer.invoke("view-load-community", url, bounds, options);
},
onViewState: (
callback: (
event: IpcRendererEvent,
Expand Down
16 changes: 9 additions & 7 deletions enjoy/src/renderer/components/layouts/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ export const Sidebar = (props: {
isCollapsed={isCollapsed}
/>

<SidebarItem
href="/community"
label={t("sidebar.community")}
tooltip={t("sidebar.community")}
active={activeTab.startsWith("/community")}
Icon={UsersRoundIcon}
isCollapsed={isCollapsed}
/>

<Separator />

<SidebarItem
Expand Down Expand Up @@ -346,13 +355,6 @@ const SidebarHeader = (props: { isCollapsed: boolean }) => {
<span>{t("sidebar.profile")}</span>
<UserIcon className="size-4 ml-auto" />
</DropdownMenuItem>
<DropdownMenuItem
className="cursor-pointer"
onSelect={() => navigate("/community")}
>
<span>{t("sidebar.community")}</span>
<UsersRoundIcon className="size-4 ml-auto" />
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onSelect={() => setDisplayDepositDialog(true)}
Expand Down
89 changes: 58 additions & 31 deletions enjoy/src/renderer/pages/community.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,61 @@
import {
Button,
Tabs,
TabsList,
TabsContent,
TabsTrigger,
} from "@renderer/components/ui";
import { UsersRankings, Posts } from "@renderer/components";
import { ChevronLeftIcon } from "lucide-react";
import { useNavigate } from "react-router-dom";
import { t } from "i18next";
import { useContext, useEffect, useRef } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
import debounce from "lodash/debounce";
import { DISCUSS_URL, WEB_API_URL } from "@/constants";

export default () => {
const navigate = useNavigate();

return (
<div className="min-h-full px-4 lg:px-8 py-6 max-w-screen-md mx-auto">
<Tabs defaultValue="square">
<TabsList className="mb-4">
<TabsTrigger value="square">{t("square")}</TabsTrigger>
<TabsTrigger value="rankings">{t("rankings")}</TabsTrigger>
</TabsList>

<TabsContent value="square">
<Posts />
</TabsContent>

<TabsContent value="rankings">
<UsersRankings />
</TabsContent>
</Tabs>
</div>
);
const containerRef = useRef<HTMLDivElement>(null);
const { EnjoyApp, user, webApi } = useContext(AppSettingsProviderContext);

const loadCommunity = async () => {
let url = `${DISCUSS_URL}/login`;
let ssoUrl = `${WEB_API_URL}/discourse/sso`;

try {
const { discussUrl, discussSsoUrl } = await webApi.config("discuss");
if (discussUrl) {
url = discussUrl;
}
if (discussSsoUrl) {
ssoUrl = discussSsoUrl;
}
} catch (error) {
console.error(error);
}

const { x, y, width, height } =
containerRef.current.getBoundingClientRect();
EnjoyApp.view.loadCommunity(
{ x, y, width, height },
{ navigatable: false, accessToken: user?.accessToken, url, ssoUrl }
);
};

const resize = debounce(() => {
const { x, y, width, height } =
containerRef.current.getBoundingClientRect();
EnjoyApp.view.resize({ x, y, width, height });
}, 100);

useEffect(() => {
if (!containerRef.current) return;

loadCommunity();
const observer = new ResizeObserver(() => {
resize();
});
observer.observe(containerRef.current);

return () => {
observer.disconnect();
};
}, []);

useEffect(() => {
return () => {
EnjoyApp.view.remove();
};
}, []);

return <div ref={containerRef} className="w-full h-full"></div>;
};
15 changes: 15 additions & 0 deletions enjoy/src/types/enjoy-app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ type EnjoyAppType = {
hide: () => Promise<void>;
remove: () => Promise<void>;
scrape: (url: string) => Promise<void>;
loadCommunity: (
bounds: { x: number; y: number; width: number; height: number },
options?: {
navigatable?: boolean;
accessToken?: string;
url?: string;
ssoUrl?: string;
}
) => Promise<void>;
resize: (bounds: {
x: number;
y: number;
width: number;
height: number;
}) => Promise<void>;
onViewState: (
callback: (
event,
Expand Down

0 comments on commit ff0f279

Please sign in to comment.