Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connections - YouTube Uploading #407

Merged
merged 24 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,619 changes: 1,284 additions & 1,335 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
},
"dependencies": {
"@reduxjs/toolkit": "^1.9.1",
"axios": "^1.3.2",
"electron-updater": "^5.3.0",
"nouislider": "^15.6.1",
"react": "^18.2.0",
Expand All @@ -39,7 +40,7 @@
"@types/react": "^18.0.20",
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "5.45.0",
"@vitejs/plugin-react": "^2.2.0",
"@vitejs/plugin-react": "^3.1.0",
"autoprefixer": "^10.4.13",
"cross-env": "^7.0.3",
"electron": "^22.0.2",
Expand All @@ -57,7 +58,7 @@
"sass": "^1.55.0",
"tailwindcss": "^3.2.4",
"typescript": "^4.9.4",
"vite": "^3.0.3",
"vite": "^4.1.1",
"vite-plugin-commonjs-externals": "^0.1.1"
},
"browserslist": [
Expand Down
31 changes: 27 additions & 4 deletions src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import RecordingsManager from "@/libs/recorder/recordingsManager";
import File from "@/libs/helpers/file";
import type { Video } from "@/videos/types";
import { logger } from "@/libs/logger";
import uploadersSlice from "@/libs/uploaders/uploadersSlice";

const saver = (store: any) => (next: Dispatch<AnyAction>) => async (action: AnyAction) => {
try {
Expand All @@ -18,7 +19,7 @@ const saver = (store: any) => (next: Dispatch<AnyAction>) => async (action: AnyA
// Call the next dispatch method in the middleware chain.
const returnValue = next(action);

if (action.type.includes("settings/")) {
if (action.type.startsWith("settings/")) {
// Remove app settings - we don't want them in the settings.json file.
const settingsState: any = {};
Object.assign(settingsState, store.getState().settings);
Expand All @@ -28,9 +29,15 @@ const saver = (store: any) => (next: Dispatch<AnyAction>) => async (action: AnyA
fs.writeFile(settingsFile, JSON.stringify(settingsState, null, 2)).catch((e) => {
throw new Error(`Error writing updated settings to ${settingsFile}:`, e);
});
}
} else if (action.type.startsWith("uploaders/")) {
const state: any = {};
Object.assign(state, store.getState().uploaders);

if (action.type.includes("videos/")) {
const file = await PathHelper.getFile("uploaders");
fs.writeFile(file, JSON.stringify(state, null, 2)).catch((e) => {
throw new Error(`Error writing updated connections to ${file}:`, e);
});
} else if (action.type.startsWith("videos/")) {
// HACK sorta.. might need to change how this works if there is
// ever an action that doesn't have isClip accessible like this
const isClip = action.payload.isClip;
Expand Down Expand Up @@ -72,12 +79,14 @@ const rehydrated = async () => {
// Create reh var and clone default values into it.
const reh = {
settings: { ...DEFAULT_SETTINGS },
uploaders: {},
videos: {
recordings: [] as Video[],
clips: [] as Video[]
}
};

// Settings
try {
const stgsFile = await PathHelper.getFile("settings");
const r = await fs.readFile(stgsFile, "utf-8");
Expand All @@ -91,6 +100,18 @@ const rehydrated = async () => {
logger.error("rehydrate", "Couldn't restore settings:", err);
}

// Uploaders
try {
const file = await PathHelper.getFile("uploaders");
const r = await fs.readFile(file, "utf-8");
if (r) {
const rjson = JSON.parse(r);
reh.uploaders = rjson;
}
} catch (err) {
logger.error("rehydrate", "Couldn't restore uploaders:", err);
}

const readVideoFile = async (clips: boolean): Promise<Video[]> => {
return (
(await File.readContinuousJsonFile(await PathHelper.getFile(clips ? "clips" : "recordings"))) as Video[]
Expand All @@ -115,6 +136,7 @@ const rehydrated = async () => {

console.groupCollapsed("Restored State");
logger.info("rehydrate", "Settings", reh.settings);
logger.info("rehydrate", "Uploaders"); // Don't want peoples access tokens logged
logger.info("rehydrate", "Recordings", reh.videos.recordings);
logger.info("rehydrate", "Clips", reh.videos.clips);
console.groupEnd();
Expand All @@ -130,7 +152,8 @@ export const store = configureStore({
app: appSlice,
videos: videosSlice,
settings: settingsSlice,
recorder: recorderSlice
recorder: recorderSlice,
uploaders: uploadersSlice
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(saver),
preloadedState: await rehydrated()
Expand Down
130 changes: 118 additions & 12 deletions src/common/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface IconProps {
direction?: IconDirection;

className?: string;
onClick?: () => void;
onClick?: React.MouseEventHandler;
}

export type Icons =
Expand All @@ -31,7 +31,12 @@ export type Icons =
| "time"
| "edit"
| "search"
| "pin";
| "pin"
| "info"
| "wifi"
| "globe"
| "youtube"
| "folder";

export type IconDirection = "up" | "down" | "left" | "right";

Expand Down Expand Up @@ -85,9 +90,12 @@ function getIcon(name: Icons): { viewBox: string; el: JSX.Element } {
};
case "upload":
return {
viewBox: "0 0 16 18",
viewBox: "0 0 512 512",
el: (
<path fillRule="evenodd" clipRule="evenodd" fill="currentcolor" d="M0 2V0H16V2H0ZM7 8H4L8 4L12 8H9V18H7V8Z" />
<path
fill="currentColor"
d="M473.66 210c-14-10.38-31.2-18-49.36-22.11a16.11 16.11 0 01-12.19-12.22c-7.8-34.75-24.59-64.55-49.27-87.13C334.15 62.25 296.21 47.79 256 47.79c-35.35 0-68 11.08-94.37 32.05a150.07 150.07 0 00-42.06 53 16 16 0 01-11.31 8.87c-26.75 5.4-50.9 16.87-69.34 33.12C13.46 197.33 0 227.24 0 261.39c0 34.52 14.49 66 40.79 88.76 25.12 21.69 58.94 33.64 95.21 33.64h104V230.42l-36.69 36.69a16 16 0 01-23.16-.56c-5.8-6.37-5.24-16.3.85-22.39l63.69-63.68a16 16 0 0122.62 0L331 244.14c6.28 6.29 6.64 16.6.39 22.91a16 16 0 01-22.68.06L272 230.42v153.37h124c31.34 0 59.91-8.8 80.45-24.77 23.26-18.1 35.55-44 35.55-74.83 0-29.94-13.26-55.61-38.34-74.19zM240 448.21a16 16 0 1032 0v-64.42h-32z"
/>
)
};
case "volumeMute":
Expand Down Expand Up @@ -248,14 +256,16 @@ function getIcon(name: Icons): { viewBox: string; el: JSX.Element } {
return {
viewBox: "0 0 512 512",
el: (
<path
fill="currentcolor"
d="M496.063 62.299l-46.396-46.4c-21.2-21.199-55.69-21.198-76.888
0l-18.16 18.161 123.284 123.294 18.16-18.161c21.248-21.249
21.251-55.643 0-76.894zM22.012 376.747L.251 494.268a15.002
15.002 0 0017.48 17.482l117.512-21.763-113.231-113.24zM333.407
55.274L38.198 350.506l123.284 123.293 295.209-295.231z"
></path>
<g>
<path
fill="currentcolor"
d="M459.94 53.25a16.06 16.06 0 00-23.22-.56L424.35 65a8 8 0 000 11.31l11.34 11.32a8 8 0 0011.34 0l12.06-12c6.1-6.09 6.67-16.01.85-22.38zM399.34 90L218.82 270.2a9 9 0 00-2.31 3.93L208.16 299a3.91 3.91 0 004.86 4.86l24.85-8.35a9 9 0 003.93-2.31L422 112.66a9 9 0 000-12.66l-9.95-10a9 9 0 00-12.71 0z"
/>
<path
fill="currentcolor"
d="M386.34 193.66L264.45 315.79A41.08 41.08 0 01247.58 326l-25.9 8.67a35.92 35.92 0 01-44.33-44.33l8.67-25.9a41.08 41.08 0 0110.19-16.87l122.13-121.91a8 8 0 00-5.65-13.66H104a56 56 0 00-56 56v240a56 56 0 0056 56h240a56 56 0 0056-56V199.31a8 8 0 00-13.66-5.65z"
/>
</g>
)
};
case "search":
Expand Down Expand Up @@ -303,6 +313,102 @@ function getIcon(name: Icons): { viewBox: string; el: JSX.Element } {
</g>
)
};
case "info":
return {
viewBox: "0 0 512 512",
el: (
<g>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="40"
d="M196 220h64v172"
/>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeMiterlimit="10"
strokeWidth="40"
d="M187 396h138"
/>
<path fill="currentColor" d="M256 160a32 32 0 1132-32 32 32 0 01-32 32z" />
</g>
)
};
case "wifi":
return {
viewBox: "0 0 512 512",
el: (
<g>
<path
d="M332.41 310.59a115 115 0 00-152.8 0M393.46 249.54a201.26 201.26 0 00-274.92 0M447.72 182.11a288 288 0 00-383.44 0"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="32"
/>
<path fill="currentColor" d="M256 416a32 32 0 1132-32 32 32 0 01-32 32z" />
</g>
)
};
case "globe":
return {
viewBox: "0 0 512 512",
el: (
<g>
<path
d="M256 48C141.13 48 48 141.13 48 256s93.13 208 208 208 208-93.13 208-208S370.87 48 256 48z"
fill="none"
stroke="currentColor"
strokeMiterlimit="10"
strokeWidth="32"
/>
<path
d="M256 48c-58.07 0-112.67 93.13-112.67 208S197.93 464 256 464s112.67-93.13 112.67-208S314.07 48 256 48z"
fill="none"
stroke="currentColor"
strokeMiterlimit="10"
strokeWidth="32"
/>
<path
d="M117.33 117.33c38.24 27.15 86.38 43.34 138.67 43.34s100.43-16.19 138.67-43.34M394.67 394.67c-38.24-27.15-86.38-43.34-138.67-43.34s-100.43 16.19-138.67 43.34"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="32"
/>
<path fill="none" stroke="currentColor" strokeMiterlimit="10" strokeWidth="32" d="M256 48v416M464 256H48" />
</g>
)
};
case "youtube":
return {
viewBox: "0 0 443 443",
el: (
<g>
<path fill="#fff" d="M177.582 133.776l.049 175.613 129.993-87.97-130.042-87.643z" />
<path
fill="red"
d="M440.093 128.738c0-38.935-28.639-70.257-64.027-70.257-47.933-2.241-96.818-3.106-146.776-3.106h-15.574c-49.837 0-98.809.865-146.742 3.115-35.302 0-63.94 31.494-63.94 70.43C.87 159.714-.047 190.516.005 221.318c-.086 30.803.894 61.634 2.942 92.494 0 38.935 28.639 70.516 63.94 70.516 50.356 2.337 102.01 3.375 154.529 3.288 52.606.173 104.115-.923 154.529-3.288 35.388 0 64.027-31.581 64.027-70.516 2.076-30.889 3.028-61.691 2.941-92.58a1257.74 1257.74 0 0 0-2.82-92.494zh0zm-260.986 177.46V136.179l125.457 84.966-125.457 85.053z"
/>
</g>
)
};
case "folder":
return {
viewBox: "0 0 512 512",
el: (
<path
fill="currentColor"
d="M408 96H252.11a23.89 23.89 0 01-13.31-4L211 73.41A55.77 55.77 0 00179.89 64H104a56.06 56.06 0 00-56 56v24h416c0-30.88-25.12-48-56-48zM423.75 448H88.25a56 56 0 01-55.93-55.15L16.18 228.11v-.28A48 48 0 0164 176h384.1a48 48 0 0147.8 51.83v.28l-16.22 164.74A56 56 0 01423.75 448zm56.15-221.45z"
/>
)
};
default:
return {
viewBox: "0 -10 1000 1000",
Expand Down
5 changes: 3 additions & 2 deletions src/common/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Progress from "./Progress";
import TickBox from "./TickBox";

export default function Popup(props: PopupOptions) {
const { title, percentage, loader, showCancel = false, buttons, tickBoxes } = props;
const { title, percentage, loader, showCancel = false, buttons, tickBoxes, message } = props;

const [tbc, setTbc] = useState<string[]>(
tickBoxes
Expand Down Expand Up @@ -41,8 +41,9 @@ export default function Popup(props: PopupOptions) {
}}
/>
)}
<span className="p-1.5 text-xl font-bold capitalize">{title}</span>
<span className="p-1 text-xl font-bold capitalize">{title}</span>

{message && <span>{message}</span>}
{loader && <Loader />}
{percentage && <Progress p={percentage} />}

Expand Down
67 changes: 62 additions & 5 deletions src/common/SubNav.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,85 @@
import { useEffect, useRef } from "react";
import { NavLink, useLocation } from "react-router-dom";
import Icon, { type Icons } from "./Icon";

interface SubNavProps {
children: React.ReactNode;
}

export default function SubNav({ children }: SubNavProps) {
return <ul className="flex row justify-center items-center mb-4 text-2xl">{children}</ul>;
return <ul className="flex justify-between row justify-center items-center mb-4 text-2xl">{children}</ul>;
}

interface SubNavItemProps {
icon: Icons;
text: string;
}

export function SubNavItem({ text }: SubNavItemProps) {
export function SubNavItem({ icon, text }: SubNavItemProps) {
const to = text.replace(" ", "").toLowerCase();
const linkRef = useRef<HTMLAnchorElement>(null);
const isActive =
useLocation()
.pathname.match(/[^/]*$/)?.[0]
.toLowerCase() === to;

const inactiveLinkClass = ["w-[50px]", "min-w-[50px]"];
const inactiveTextClass = ["invisible", "group-hover:visible", "group-hover:w-min", "w-0"];

const hovIn = () => {
if (isActive) return;
const el = document.querySelector<HTMLElement>(".navlink.active");
const elIcon = document.querySelector<HTMLElement>(".navlink.active .navlink-icon");
const elText = document.querySelector<HTMLElement>(".navlink.active .navlink-text");
if (el && elIcon && elText) {
el.classList.add(...inactiveLinkClass);
elText.classList.add(...inactiveTextClass, "!w-0");
elIcon.classList.remove("!mr-2");
}
};

const hovOut = () => {
if (isActive) return;
const el = document.querySelector<HTMLElement>(".navlink.active");
const elIcon = document.querySelector<HTMLElement>(".navlink.active .navlink-icon");
const elText = document.querySelector<HTMLElement>(".navlink.active .navlink-text");
if (el && elIcon && elText) {
el.classList.remove(...inactiveLinkClass);
elText.classList.remove(...inactiveTextClass, "!w-0");
elIcon.classList.add("!mr-2");
}
};

useEffect(() => {
linkRef.current?.addEventListener("mouseenter", hovIn);
linkRef.current?.addEventListener("mouseleave", hovOut);

return () => {
linkRef.current?.removeEventListener("mouseenter", hovIn);
linkRef.current?.removeEventListener("mouseleave", hovOut);
};
}, [linkRef, isActive]);

return (
<NavLink to={to} className={["mx-2", isActive ? `text-white-100` : `text-white-25 hover:text-white-50`].join(" ")}>
<span className={`cursor-pointer`}>{text}</span>
<div className={["h-0.5", isActive ? "rounded-full bg-white-100" : ""].join(" ")}></div>
<NavLink
to={to}
ref={linkRef}
className={[
"navlink group hover:w-full overflow-hidden items-center mx-2 transition-all",
isActive ? `text-white-100 w-full` : `${inactiveLinkClass.join(" ")} text-white-25 hover:text-white-50`
].join(" ")}
>
<div className="flex justify-center items-center m-2 outline outline-offset-4 outline-color-white outline-2 rounded">
<Icon i={icon} className={["navlink-icon group-hover:mr-2", isActive ? "!mr-2" : ""].join(" ")} />
<div
className={[
`navlink-text transition-all cursor-pointer whitespace-nowrap`,
isActive ? "visible w-min" : `${inactiveTextClass.join(" ")} `
].join(" ")}
>
{text}
</div>
</div>
</NavLink>
);
}
Loading