diff --git a/packages/desktop2/package.json b/packages/desktop2/package.json
index e054479..3f61c9d 100644
--- a/packages/desktop2/package.json
+++ b/packages/desktop2/package.json
@@ -90,6 +90,8 @@
"typescript": "~4.5.4"
},
"dependencies": {
+ "@getstation/electron-google-oauth2": "^2.1.0",
+ "@mui/icons-material": "^5.5.1",
"axios": "^0.26.1",
"electron-squirrel-startup": "^1.0.0",
"electron-store": "^8.0.1",
diff --git a/packages/desktop2/src/app.tsx b/packages/desktop2/src/app.tsx
index 5a8ef9b..1139c81 100644
--- a/packages/desktop2/src/app.tsx
+++ b/packages/desktop2/src/app.tsx
@@ -3,11 +3,10 @@ import "./index.css";
import * as React from "react";
import * as ReactDOM from "react-dom";
+import NirvanaApp from "./nirvanaApp";
+
function render() {
- ReactDOM.render(
-
Hello from React!
,
- document.getElementById("root")
- );
+ ReactDOM.render(, document.getElementById("root"));
}
render();
diff --git a/packages/desktop2/src/components/Logo/index.tsx b/packages/desktop2/src/components/Logo/index.tsx
new file mode 100644
index 0000000..0f615a5
--- /dev/null
+++ b/packages/desktop2/src/components/Logo/index.tsx
@@ -0,0 +1,93 @@
+export enum LogoType {
+ primary = "primary",
+ small = "small",
+}
+
+export default function Logo({
+ className,
+ type,
+}: {
+ className: string;
+ type?: LogoType;
+}) {
+ if (type === LogoType.small) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+}
diff --git a/packages/desktop2/src/components/ProtectedRoute/index.tsx b/packages/desktop2/src/components/ProtectedRoute/index.tsx
new file mode 100644
index 0000000..cdddf77
--- /dev/null
+++ b/packages/desktop2/src/components/ProtectedRoute/index.tsx
@@ -0,0 +1,22 @@
+import { Route, useNavigate } from "react-router-dom";
+
+import { useGetUserDetails } from "../../controller/index";
+
+export default function ProtectedRoute({ ...children }) {
+ const { isLoading, isError } = useGetUserDetails();
+
+ const navigate = useNavigate();
+
+ if (isLoading) return please wait while we authenticate you;
+
+ if (isError) {
+ // toast.error("Please sign in first!");
+ navigate("/login");
+
+ return you are not allowed here!;
+ }
+
+ // if we can successfully get user details, we are good to continue
+
+ return <>{children}>;
+}
diff --git a/packages/desktop2/src/controller/index.tsx b/packages/desktop2/src/controller/index.tsx
new file mode 100644
index 0000000..adccbbb
--- /dev/null
+++ b/packages/desktop2/src/controller/index.tsx
@@ -0,0 +1,12 @@
+import { nirvanaApi } from "./nirvanaApi";
+import { useQuery } from "react-query";
+
+export enum Querytypes {
+ GET_USER_DETAILS = "GET_USER_DETAILS",
+}
+
+export function useGetUserDetails() {
+ return useQuery(Querytypes.GET_USER_DETAILS, nirvanaApi.getUserDetails, {
+ retry: true,
+ });
+}
diff --git a/packages/desktop2/src/controller/nirvanaApi.ts b/packages/desktop2/src/controller/nirvanaApi.ts
new file mode 100644
index 0000000..f7a200c
--- /dev/null
+++ b/packages/desktop2/src/controller/nirvanaApi.ts
@@ -0,0 +1,40 @@
+import axios, { AxiosRequestConfig } from "axios";
+
+// export const localHost = process.env.REACT_APP_API_DOMAIN;
+
+export const localHost = "http://localhost:5000";
+
+class NirvanaApi {
+ // auth token from google that our backend will use
+ private _authToken?: string;
+
+ setPrivateToken(_googleIdToken: string) {
+ this._authToken = _googleIdToken;
+ }
+
+ async fetch(options: AxiosRequestConfig, privateRoute = false) {
+ // use the auth token if it's a private route
+ // error if no auth token and it's a private route
+ // throw error and show message on anything that is an error from the backend
+ if (privateRoute && this._authToken) {
+ return await axios({
+ ...options,
+ headers: { Authorization: this._authToken },
+ });
+ }
+
+ return axios(options);
+ }
+
+ async getUserDetails() {
+ return await this.fetch(
+ {
+ method: "GET",
+ url: localHost + `/users`,
+ },
+ true
+ );
+ }
+}
+
+export const nirvanaApi = new NirvanaApi();
diff --git a/packages/desktop2/src/electron/constants.ts b/packages/desktop2/src/electron/constants.ts
new file mode 100644
index 0000000..ae527f1
--- /dev/null
+++ b/packages/desktop2/src/electron/constants.ts
@@ -0,0 +1,6 @@
+enum Channels {
+ ACTIVATE_LOG_IN = "ACTIVATE_LOG_IN",
+ AUTH_TOKENS = "AUTH_TOKENS",
+}
+
+export default Channels;
diff --git a/packages/desktop2/src/electron/handleLogin.ts b/packages/desktop2/src/electron/handleLogin.ts
new file mode 100644
index 0000000..6cd0ed6
--- /dev/null
+++ b/packages/desktop2/src/electron/handleLogin.ts
@@ -0,0 +1,41 @@
+import Channels from "./constants";
+import ElectronGoogleOAuth2 from "@getstation/electron-google-oauth2";
+import { browserWindow } from "../index";
+import { ipcMain } from "electron";
+import store from "./store";
+
+const myApiOauth = new ElectronGoogleOAuth2(
+ "423533244953-banligobgbof8hg89i6cr1l7u0p7c2pk.apps.googleusercontent.com",
+ "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",
+ [""],
+ { successRedirectURL: "https://usenirvana.com" }
+);
+
+export async function handleLogin() {
+ // read saved refresh token if any
+ // todo: fix this...should be working
+ const refreshToken = await store.get("tokens");
+
+ // todo: remove this when I have way of getting access token from a refresh token
+ // if (refreshToken) {
+ // console.log("have a refresh token from user auth previously", refreshToken);
+ // // myApiOauth.setTokens({ refresh_token: refreshToken });
+
+ // // send token to client
+ // browserWindow.webContents.send(Channels.AUTH_TOKENS, refreshToken);
+ // } else {
+ // const token = await myApiOauth.openAuthWindowAndGetTokens();
+
+ // // store the refresh token in cookies for app reopen
+ // store.set("tokens", token);
+
+ // // todo: send the access token to the renderer
+ // // send token to client
+ // browserWindow.webContents.send(Channels.AUTH_TOKENS, token);
+ // }
+
+ const tokens = await myApiOauth.openAuthWindowAndGetTokens();
+ store.set("tokens", tokens);
+
+ browserWindow.webContents.send(Channels.AUTH_TOKENS, tokens);
+}
diff --git a/packages/desktop2/src/electron/store.ts b/packages/desktop2/src/electron/store.ts
new file mode 100644
index 0000000..69dbb96
--- /dev/null
+++ b/packages/desktop2/src/electron/store.ts
@@ -0,0 +1,4 @@
+import Store from "electron-store";
+const store = new Store();
+
+export default store;
diff --git a/packages/desktop2/src/index.css b/packages/desktop2/src/index.css
index e757dd6..5216226 100644
--- a/packages/desktop2/src/index.css
+++ b/packages/desktop2/src/index.css
@@ -1,9 +1,6 @@
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
Arial, sans-serif;
- margin: auto;
- max-width: 38rem;
- padding: 2rem;
}
@tailwind base;
diff --git a/packages/desktop2/src/index.ts b/packages/desktop2/src/index.ts
index 99fa399..b4d152e 100644
--- a/packages/desktop2/src/index.ts
+++ b/packages/desktop2/src/index.ts
@@ -1,44 +1,65 @@
-import { app, BrowserWindow } from 'electron';
+import { BrowserWindow, app, ipcMain } from "electron";
+
+import Channels from "./electron/constants";
+import { handleLogin } from "./electron/handleLogin";
+
// This allows TypeScript to pick up the magic constant that's auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
-if (require('electron-squirrel-startup')) {
+if (require("electron-squirrel-startup")) {
// eslint-disable-line global-require
app.quit();
}
+// Keep a global reference of the window object, if you don't, the window will
+// be closed automatically when the JavaScript object is garbage collected.
+export let browserWindow: BrowserWindow;
+
const createWindow = (): void => {
// Create the browser window.
- const mainWindow = new BrowserWindow({
+ browserWindow = new BrowserWindow({
height: 600,
width: 800,
+ webPreferences: {
+ nodeIntegration: true,
+ },
});
// and load the index.html of the app.
- mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
+ browserWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
// Open the DevTools.
- mainWindow.webContents.openDevTools();
+ browserWindow.webContents.openDevTools();
};
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
-app.on('ready', createWindow);
+app
+ .whenReady()
+ .then(createWindow)
+ .then(() => {
+ console.log("hahha");
+
+ // End of the file
+ ipcMain.on(Channels.ACTIVATE_LOG_IN, async (event, arg) => {
+ await handleLogin();
+ });
+ });
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
-app.on('window-all-closed', () => {
- if (process.platform !== 'darwin') {
+app.on("window-all-closed", () => {
+ if (process.platform !== "darwin") {
app.quit();
}
});
-app.on('activate', () => {
+app.on("activate", () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
diff --git a/packages/desktop2/src/nirvanaApp.tsx b/packages/desktop2/src/nirvanaApp.tsx
new file mode 100644
index 0000000..330d355
--- /dev/null
+++ b/packages/desktop2/src/nirvanaApp.tsx
@@ -0,0 +1,40 @@
+import { HashRouter, Route, Routes } from "react-router-dom";
+import { QueryClient, QueryClientProvider } from "react-query";
+
+import Home from "./pages/Home";
+import Login from "./pages/Login";
+import ProtectedRoute from "./components/ProtectedRoute";
+import testConnection from "@nirvana/core";
+
+testConnection();
+
+// Create a client
+const queryClient = new QueryClient();
+
+function NirvanaApp() {
+ return (
+ <>
+
+
+
+
+ } />
+
+
+
+ }
+ />
+ {/*
+ */}
+
+
+
+
+ >
+ );
+}
+
+export default NirvanaApp;
diff --git a/packages/desktop2/src/pages/Home/index.tsx b/packages/desktop2/src/pages/Home/index.tsx
new file mode 100644
index 0000000..ed80486
--- /dev/null
+++ b/packages/desktop2/src/pages/Home/index.tsx
@@ -0,0 +1,64 @@
+import {
+ Add,
+ CardGiftcardRounded,
+ ContactsRounded,
+ LinkRounded,
+ PushPinRounded,
+} from "@mui/icons-material";
+import Logo, { LogoType } from "../../components/Logo";
+
+export default function Home() {
+ return (
+
+ {/* header */}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* actions navbar */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* pinned conversations */}
+
+
+ );
+}
diff --git a/packages/desktop2/src/pages/Login/index.tsx b/packages/desktop2/src/pages/Login/index.tsx
new file mode 100644
index 0000000..db83f9d
--- /dev/null
+++ b/packages/desktop2/src/pages/Login/index.tsx
@@ -0,0 +1,65 @@
+import { Button, CircularProgress } from "@mui/material";
+import { useEffect, useState } from "react";
+
+import Channels from "../../electron/constants";
+import Logo from "../../components/Logo";
+import { UserInfo } from "@nirvana/core/models";
+import axios from "axios";
+import { useGetUserDetails } from "../../controller";
+import { useNavigate } from "react-router-dom";
+
+export default function Login() {
+ const navigate = useNavigate();
+
+ const logIn = () => {
+ // send to main process
+ // ipcRenderer.send(Channels.ACTIVATE_LOG_IN);
+ };
+
+ // useEffect(() => {
+ // window.API.receive(channels.AUTH_TOKENS, async (tokens: any) => {
+ // console.log(tokens);
+
+ // const { access_token, id_token, refresh_token } = tokens;
+
+ // // get user info from access token
+ // const userInfo: UserInfo = await (
+ // await fetch(
+ // `https://www.googleapis.com/oauth2/v1/userinfo?access_token=${access_token}`
+ // )
+ // ).json();
+
+ // console.log("user data: ");
+ // console.log(userInfo);
+
+ // // if user is an existing user, then continue to next route
+ // axios({
+ // method: "GET",
+ // url: localHost + `/user`,
+ // headers: {
+ // Authorization:
+ // "eyJhbGciOiJSUzI1NiIsImtpZCI6ImQ2M2RiZTczYWFkODhjODU0ZGUwZDhkNmMwMTRjMzZkYzI1YzQyOTIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI0MjM1MzMyNDQ5NTMtYmFubGlnb2JnYm9mOGhnODlpNmNyMWw3dTBwN2MycGsuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI0MjM1MzMyNDQ5NTMtYmFubGlnb2JnYm9mOGhnODlpNmNyMWw3dTBwN2MycGsuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTM0NzA3ODY2OTAzNTMxMDkwODYiLCJlbWFpbCI6InBhdGVsLmFyanVuNTBAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF0X2hhc2giOiJKRlBBOC15TGgxeGlGOW5acUZQU0FBIiwibmFtZSI6IkFyanVuIFBhdGVsIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hLS9BT2gxNEdqbTI5U2QxV25tOE52WmJYa29zdmY2U29JRDZrQlA1T0hSTFZJT0JRPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IkFyanVuIiwiZmFtaWx5X25hbWUiOiJQYXRlbCIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNjQ3Mzc4MzgzLCJleHAiOjE2NDczODE5ODN9.llIYMEjq3Ye1UFxiKIsgtjnRho2Ad7dQjCSFVDDlsiH3AVq_MssoKTXMS0tuHpQAZAfyqbi1kRpp4Ax3mFJb44z2OIyzOTDzhcNB_L_3C8U3gRuSmUaVfBa7EdkobkxByee3ddvjekD7Y9dobtXs3IllldjewD9Eg9FZmEkuSN6yYVxxRLYNYTjk_BAA17QKKkPPpyanAHHXLwxpMFhQc34tuAYCYmmDQQijV8YVvU4r9ruPpysumVXlUmX3CuNg9P415Dq1lUHQxaf5hWf_6RxIsw9PNg1wZ8CuJGvmLFnGBRQACKgnAkI9E7-tu2tWe3glRhORZlQmgRCKqG5xoA",
+ // },
+ // }).then((res) => console.log(res));
+
+ // navigate("/home");
+
+ // // if user is not an existing user, then show create account page
+ // });
+
+ // // return () => {
+ // // ipcRenderer.removeAllListeners(channels.AUTH_TOKEN);
+ // // };
+ // }, []);
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/packages/desktop2/src/pages/Profile/index.tsx b/packages/desktop2/src/pages/Profile/index.tsx
new file mode 100644
index 0000000..3f0ea80
--- /dev/null
+++ b/packages/desktop2/src/pages/Profile/index.tsx
@@ -0,0 +1,3 @@
+export default function Profile() {
+ return this is the profile page;
+}
diff --git a/packages/desktop2/webpack.renderer.config.js b/packages/desktop2/webpack.renderer.config.js
index 753e39d..7338e9b 100644
--- a/packages/desktop2/webpack.renderer.config.js
+++ b/packages/desktop2/webpack.renderer.config.js
@@ -1,8 +1,6 @@
const rules = require("./webpack.rules");
const plugins = require("./webpack.plugins");
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-
const path = require("path");
rules.push({
diff --git a/yarn.lock b/yarn.lock
index d8efe34..c3490e2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1579,7 +1579,7 @@
"@getstation/electron-google-oauth2@^2.1.0":
version "2.1.0"
- resolved "https://registry.npmjs.org/@getstation/electron-google-oauth2/-/electron-google-oauth2-2.1.0.tgz"
+ resolved "https://registry.yarnpkg.com/@getstation/electron-google-oauth2/-/electron-google-oauth2-2.1.0.tgz#ed0c489c7f97bf9e4542b599c45e6f7a6ece8b54"
integrity sha512-lWoyxkeqFP1eHkka918eq9649vtiKjQaaQlPH22f6DTMGXg+K3HmzgaE3cPGUIavQFHoDpPJE138V/FiJnPVYw==
dependencies:
google-auth-library "^5.9.2"
@@ -1847,6 +1847,13 @@
dependencies:
"@babel/runtime" "^7.17.2"
+"@mui/icons-material@^5.5.1":
+ version "5.5.1"
+ resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.5.1.tgz#848a57972617411370775980cbc6990588d4aafb"
+ integrity sha512-40f68p5+Yhq3dCn3QYHqQt5RETPyR3AkDw+fma8PtcjqvZ+d+jF84kFmT6NqwA3he7TlwluEtkyAmPzUE4uPdA==
+ dependencies:
+ "@babel/runtime" "^7.17.2"
+
"@mui/material@^5.5.0":
version "5.5.0"
resolved "https://registry.npmjs.org/@mui/material/-/material-5.5.0.tgz"
@@ -8405,14 +8412,7 @@ min-indent@^1.0.0:
resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
-mini-css-extract-plugin@^2.4.5:
- version "2.6.0"
- resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.0.tgz"
- integrity sha512-ndG8nxCEnAemsg4FSgS+yNyHKgkTB4nPKqCOgh65j3/30qqC5RaSQQXMm++Y6sb6E1zRSxPkztj9fqxhS1Eo6w==
- dependencies:
- schema-utils "^4.0.0"
-
-mini-css-extract-plugin@^2.6.0:
+mini-css-extract-plugin@^2.4.5, mini-css-extract-plugin@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.0.tgz#578aebc7fc14d32c0ad304c2c34f08af44673f5e"
integrity sha512-ndG8nxCEnAemsg4FSgS+yNyHKgkTB4nPKqCOgh65j3/30qqC5RaSQQXMm++Y6sb6E1zRSxPkztj9fqxhS1Eo6w==