From 52069f703628d8ea06c443b585603082f79f8901 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 20 Aug 2024 21:27:34 -0300 Subject: [PATCH 01/13] feat: set typeorm synchrozine to false --- src/main/data-source.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/data-source.ts b/src/main/data-source.ts index 446ccbdc..826063e1 100644 --- a/src/main/data-source.ts +++ b/src/main/data-source.ts @@ -23,7 +23,7 @@ export const dataSource = new DataSource({ DownloadQueue, UserAuth, ], - synchronize: true, + synchronize: false, database: databasePath, migrations, }); From fb60c91c8399ef0b556da22892f14d12f3fc3b6e Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:23:32 -0300 Subject: [PATCH 02/13] feat: add undo friendship confirm modal --- src/locales/en/translation.json | 3 +- src/locales/pt/translation.json | 3 +- .../user-confirm-undo-friendship-modal.tsx | 42 +++++++++++++++++++ src/renderer/src/pages/user/user-content.tsx | 16 ++++--- 4 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index e2726b79..08c9fda2 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -270,6 +270,7 @@ "pending": "Pending", "no_pending_invites": "You have no pending invites", "no_blocked_users": "You have no blocked users", - "friend_code_copied": "Friend code copied" + "friend_code_copied": "Friend code copied", + "undo_friendship_modal_text": "This will undo your friendship with {{displayName}}" } } diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 36d38c96..a97a81cf 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -273,6 +273,7 @@ "pending": "Pendentes", "no_pending_invites": "Você não possui convites de amizade pendentes", "no_blocked_users": "Você não tem nenhum usuário bloqueado", - "friend_code_copied": "Código de amigo copiado" + "friend_code_copied": "Código de amigo copiado", + "undo_friendship_modal_text": "Isso irá remover sua amizade com {{displayName}}" } } diff --git a/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx new file mode 100644 index 00000000..cf2a0415 --- /dev/null +++ b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx @@ -0,0 +1,42 @@ +import { Button, Modal } from "@renderer/components"; +import * as styles from "./user.css"; +import { useTranslation } from "react-i18next"; + +export interface UserConfirmUndoFriendshipModalProps { + visible: boolean; + displayName: string; + onConfirm: () => void; + onClose: () => void; +} + +export const UserConfirmUndoFriendshipModal = ({ + visible, + displayName, + onConfirm, + onClose, +}: UserConfirmUndoFriendshipModalProps) => { + const { t } = useTranslation("user_profile"); + + return ( + <> + +
+

{t("undo_friendship_modal_text", { displayName })}

+
+ + + +
+
+
+ + ); +}; diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index c334389e..fb092d06 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -34,6 +34,7 @@ import { UserProfileSettingsModal } from "./user-profile-settings-modal"; import { UserSignOutModal } from "./user-sign-out-modal"; import { UserFriendModalTab } from "../shared-modals/user-friend-modal"; import { UserBlockModal } from "./user-block-modal"; +import { UserConfirmUndoFriendshipModal } from "./user-confirm-undo-friendship-modal"; const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120; @@ -68,6 +69,7 @@ export function UserContent({ useState(false); const [showSignOutModal, setShowSignOutModal] = useState(false); const [showUserBlockModal, setShowUserBlockModal] = useState(false); + const [showUndoFriendshipModal, setShowUndoFriendshipModal] = useState(false); const [currentGame, setCurrentGame] = useState(null); const { gameRunning } = useAppSelector((state) => state.gameRunning); @@ -213,17 +215,12 @@ export function UserContent({ } if (userProfile.relation.status === "ACCEPTED") { - const userId = - userProfile.relation.AId === userDetails?.id - ? userProfile.relation.BId - : userProfile.relation.AId; - return ( <> @@ -291,6 +288,13 @@ export function UserContent({ displayName={userProfile.displayName} /> + setShowUndoFriendshipModal(false)} + onConfirm={() => handleFriendAction(userProfile.id, "UNDO")} + displayName={userProfile.displayName} + /> +
Date: Mon, 26 Aug 2024 12:45:27 -0300 Subject: [PATCH 03/13] fix: profile display name --- src/renderer/src/pages/user/user-content.tsx | 4 +++- .../user-profile-settings-modal/user-edit-profile.tsx | 1 + src/renderer/src/pages/user/user.css.ts | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/pages/user/user-content.tsx b/src/renderer/src/pages/user/user-content.tsx index fb092d06..f4a46ccd 100644 --- a/src/renderer/src/pages/user/user-content.tsx +++ b/src/renderer/src/pages/user/user-content.tsx @@ -332,7 +332,9 @@ export function UserContent({
-

{userProfile.displayName}

+

+ {userProfile.displayName} +

{currentGame && (
setForm({ ...form, displayName: e.target.value })} /> diff --git a/src/renderer/src/pages/user/user.css.ts b/src/renderer/src/pages/user/user.css.ts index 4e1c2139..6bcb30b0 100644 --- a/src/renderer/src/pages/user/user.css.ts +++ b/src/renderer/src/pages/user/user.css.ts @@ -23,6 +23,7 @@ export const profileContentBox = style({ export const profileAvatarContainer = style({ width: "96px", + minWidth: "96px", height: "96px", borderRadius: "50%", display: "flex", @@ -100,6 +101,14 @@ export const profileInformation = style({ alignItems: "flex-start", color: "#c0c1c7", zIndex: 1, + overflow: "hidden", +}); + +export const profileDisplayName = style({ + fontWeight: "bold", + overflow: "hidden", + textOverflow: "ellipsis", + width: "100%", }); export const profileContent = style({ From 3293320926e74a867a39188231604840c0f0c3aa Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:21:21 -0300 Subject: [PATCH 04/13] fix: sidebar profile layout --- .../components/sidebar/sidebar-profile.css.ts | 20 +++++--------- .../components/sidebar/sidebar-profile.tsx | 27 ++++++++++--------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/renderer/src/components/sidebar/sidebar-profile.css.ts b/src/renderer/src/components/sidebar/sidebar-profile.css.ts index ba29c850..d97b0c44 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.css.ts +++ b/src/renderer/src/components/sidebar/sidebar-profile.css.ts @@ -7,22 +7,24 @@ export const profileContainerBackground = createVar(); export const profileContainer = style({ background: profileContainerBackground, position: "relative", + display: "flex", + gap: `${SPACING_UNIT}px`, cursor: "pointer", ":hover": { backgroundColor: "rgba(255, 255, 255, 0.15)", }, + borderBottom: `solid 1px ${vars.color.border}`, + boxShadow: "0px 0px 15px 0px rgb(0 0 0 / 70%)", + padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`, }); export const profileButton = style({ display: "flex", cursor: "pointer", transition: "all ease 0.1s", - padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 2}px`, color: vars.color.muted, - borderBottom: `solid 1px ${vars.color.border}`, - boxShadow: "0px 0px 15px 0px rgb(0 0 0 / 70%)", width: "100%", - zIndex: "10", + overflow: "hidden", }); export const profileButtonContent = style({ @@ -75,16 +77,6 @@ export const profileButtonTitle = style({ whiteSpace: "nowrap", }); -export const friendRequestContainer = style({ - position: "absolute", - padding: "8px", - right: `${SPACING_UNIT}px`, - display: "flex", - top: 0, - bottom: 0, - alignItems: "center", -}); - export const friendRequestButton = style({ color: vars.color.success, cursor: "pointer", diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 069831cb..e41d7da2 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -41,6 +41,9 @@ export function SidebarProfile() { return undefined; }, [profileBackground]); + const showPendingRequests = + userDetails && receivedRequests.length > -1 && !gameRunning; + return (
- {userDetails && receivedRequests.length > 0 && !gameRunning && ( -
- -
+ {showPendingRequests && ( + )}
); From 0bedb7c9b7e237e3bf872b1754f21782c624e4ce Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:12:42 -0300 Subject: [PATCH 05/13] fix: header overflow text --- src/renderer/src/components/header/header.css.ts | 5 +++++ src/renderer/src/components/header/header.tsx | 2 +- src/renderer/src/components/sidebar/sidebar-profile.tsx | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/components/header/header.css.ts b/src/renderer/src/components/header/header.css.ts index 0e82aaef..12855986 100644 --- a/src/renderer/src/components/header/header.css.ts +++ b/src/renderer/src/components/header/header.css.ts @@ -104,6 +104,7 @@ export const section = style({ alignItems: "center", gap: `${SPACING_UNIT * 2}px`, height: "100%", + overflow: "hidden", }); export const backButton = recipe({ @@ -136,11 +137,15 @@ export const backButton = recipe({ export const title = recipe({ base: { transition: "all ease 0.2s", + overflow: "hidden", + textOverflow: "ellipsis", + width: "100%", }, variants: { hasBackButton: { true: { transform: "translateX(28px)", + width: "calc(100% - 28px)", }, }, }, diff --git a/src/renderer/src/components/header/header.tsx b/src/renderer/src/components/header/header.tsx index d3315098..c37e4b44 100644 --- a/src/renderer/src/components/header/header.tsx +++ b/src/renderer/src/components/header/header.tsx @@ -72,7 +72,7 @@ export function Header({ onSearch, onClear, search }: HeaderProps) { isWindows: window.electron.platform === "win32", })} > -
+
+ +
+

{t("undo_friendship_modal_text", { displayName })}

+
+ - -
+
-
- +
+ ); }; From d4d94dfc4cf3ff401a846596d40a843fe32ff8e5 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Fri, 30 Aug 2024 10:13:18 -0300 Subject: [PATCH 10/13] feat: use function expression for component --- .../src/pages/user/user-confirm-undo-friendship-modal.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx index ad83bc00..cfdb5d06 100644 --- a/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx +++ b/src/renderer/src/pages/user/user-confirm-undo-friendship-modal.tsx @@ -9,12 +9,12 @@ export interface UserConfirmUndoFriendshipModalProps { onClose: () => void; } -export const UserConfirmUndoFriendshipModal = ({ +export function UserConfirmUndoFriendshipModal({ visible, displayName, onConfirm, onClose, -}: UserConfirmUndoFriendshipModalProps) => { +}: UserConfirmUndoFriendshipModalProps) { const { t } = useTranslation("user_profile"); return ( @@ -37,4 +37,4 @@ export const UserConfirmUndoFriendshipModal = ({
); -}; +} From 567d9f540d39cdb00b19a2c786f79d6c70e1925b Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:06:53 -0300 Subject: [PATCH 11/13] feat: refactor --- .eslintignore | 1 + package.json | 4 +- src/main/hydra.dev.db | Bin 0 -> 16384 bytes src/main/knex-client.ts | 17 +- src/main/knexfile.ts | 10 + src/main/migrations/001_Hydra_2_0_3.ts | 169 ----------------- src/main/migrations/002_RepackUris.ts | 56 ------ .../migrations/20240830143811_Hydra_2_0_3.ts | 171 ++++++++++++++++++ .../migrations/20240830143906_RepackUris.ts | 58 ++++++ src/main/migrations/index.ts | 2 - src/main/migrations/migration.stub | 11 ++ 11 files changed, 262 insertions(+), 237 deletions(-) create mode 100644 src/main/hydra.dev.db create mode 100644 src/main/knexfile.ts delete mode 100644 src/main/migrations/001_Hydra_2_0_3.ts delete mode 100644 src/main/migrations/002_RepackUris.ts create mode 100644 src/main/migrations/20240830143811_Hydra_2_0_3.ts create mode 100644 src/main/migrations/20240830143906_RepackUris.ts delete mode 100644 src/main/migrations/index.ts create mode 100644 src/main/migrations/migration.stub diff --git a/.eslintignore b/.eslintignore index a6f34fea..a9960b13 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,3 +2,4 @@ node_modules dist out .gitignore +migration.stub diff --git a/package.json b/package.json index 113b2e20..ff05b2ed 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "build:win": "electron-vite build && electron-builder --win", "build:mac": "electron-vite build && electron-builder --mac", "build:linux": "electron-vite build && electron-builder --linux", - "prepare": "husky" + "prepare": "husky", + "knex:migrate:make": "knex --knexfile src/main/knexfile.ts migrate:make --esm" }, "dependencies": { "@electron-toolkit/preload": "^3.0.0", @@ -106,6 +107,7 @@ "prettier": "^3.2.4", "react": "^18.2.0", "react-dom": "^18.2.0", + "ts-node": "^10.9.2", "typescript": "^5.3.3", "vite": "^5.0.12", "vite-plugin-svgr": "^4.2.0" diff --git a/src/main/hydra.dev.db b/src/main/hydra.dev.db new file mode 100644 index 0000000000000000000000000000000000000000..d8c65f28e86ecd378cedef374125790f7237c717 GIT binary patch literal 16384 zcmeI&O;5rw7zgl{5z&L-jcenH1)_;&HxHuWVmSE1#nVb6^V{i3_Vmz}MzEJe$K0?>azZF2*NhQD9NUYwZ(6|)e$MG;;g_>R z&fZ4Hc7s#CBb8?P)1J^E009U<00Izz00bZa0SG`~uLTyTPO0j7bkR4w8;PM3Ge4Gn z&5cad#E+72n0Kvs``BuBTCCH&X}4IAwDISZ4 zz0+M>(Ng-z_w&58GwBp|o|#Q{mFIV`e@ikABw(*xhXbx_7oO+Z%)8u#gKgIK!uCV> zMyC6F+=z7KrnR{(`q3Z&0SG_<0uX=z1Rwwb2tWV=5ctOeY5h+tAH*JL5P$##AOHaf zKmY;|fB*y_009W>w?LIT-cjafgw-z@>-v95D$D&&5Ya;b0uX=z1Rwwb2tWV=5P$## MAdoBIP=_3T0ok>*FaQ7m literal 0 HcmV?d00001 diff --git a/src/main/knex-client.ts b/src/main/knex-client.ts index ad6cf928..031760f6 100644 --- a/src/main/knex-client.ts +++ b/src/main/knex-client.ts @@ -1,19 +1,18 @@ import knex, { Knex } from "knex"; import { databasePath } from "./constants"; -import * as migrations from "./migrations"; +import { Hydra2_0_3 } from "./migrations/20240830143811_Hydra_2_0_3"; +import { RepackUris } from "./migrations/20240830143906_RepackUris"; -type Migration = Knex.Migration & { name: string }; +export type HydraMigration = Knex.Migration & { name: string }; -class MigrationSource implements Knex.MigrationSource { - getMigrations(): Promise { - return Promise.resolve( - Object.values(migrations).sort((a, b) => a.name.localeCompare(b.name)) - ); +class MigrationSource implements Knex.MigrationSource { + getMigrations(): Promise { + return Promise.resolve([Hydra2_0_3, RepackUris]); } - getMigrationName(migration: Migration): string { + getMigrationName(migration: HydraMigration): string { return migration.name; } - getMigration(migration: Migration): Promise { + getMigration(migration: HydraMigration): Promise { return Promise.resolve(migration); } } diff --git a/src/main/knexfile.ts b/src/main/knexfile.ts new file mode 100644 index 00000000..df7972a9 --- /dev/null +++ b/src/main/knexfile.ts @@ -0,0 +1,10 @@ +const config = { + development: { + migrations: { + extension: "ts", + stub: "migrations/migration.stub", + }, + }, +}; + +export default config; diff --git a/src/main/migrations/001_Hydra_2_0_3.ts b/src/main/migrations/001_Hydra_2_0_3.ts deleted file mode 100644 index 36a41739..00000000 --- a/src/main/migrations/001_Hydra_2_0_3.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { Knex } from "knex"; - -export const name = "001_Hydra_2_0_3"; - -export const up = async (knex: Knex) => { - const timestamp = new Date().getTime(); - - await knex.schema.hasTable("migrations").then(async (exists) => { - if (exists) { - await knex.schema.dropTable("migrations"); - } - }); - - await knex.schema.hasTable("download_source").then(async (exists) => { - if (!exists) { - await knex.schema.createTable("download_source", (table) => { - table.increments("id").primary(); - table - .text("url") - .unique({ indexName: "download_source_url_unique_" + timestamp }); - table.text("name").notNullable(); - table.text("etag"); - table.integer("downloadCount").notNullable().defaultTo(0); - table.text("status").notNullable().defaultTo(0); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - }); - } - }); - - await knex.schema.hasTable("repack").then(async (exists) => { - if (!exists) { - await knex.schema.createTable("repack", (table) => { - table.increments("id").primary(); - table - .text("title") - .notNullable() - .unique({ indexName: "repack_title_unique_" + timestamp }); - table - .text("magnet") - .notNullable() - .unique({ indexName: "repack_magnet_unique_" + timestamp }); - table.integer("page"); - table.text("repacker").notNullable(); - table.text("fileSize").notNullable(); - table.datetime("uploadDate").notNullable(); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - table - .integer("downloadSourceId") - .references("download_source.id") - .onDelete("CASCADE"); - }); - } - }); - - await knex.schema.hasTable("game").then(async (exists) => { - if (!exists) { - await knex.schema.createTable("game", (table) => { - table.increments("id").primary(); - table - .text("objectID") - .notNullable() - .unique({ indexName: "game_objectID_unique_" + timestamp }); - table - .text("remoteId") - .unique({ indexName: "game_remoteId_unique_" + timestamp }); - table.text("title").notNullable(); - table.text("iconUrl"); - table.text("folderName"); - table.text("downloadPath"); - table.text("executablePath"); - table.integer("playTimeInMilliseconds").notNullable().defaultTo(0); - table.text("shop").notNullable(); - table.text("status"); - table.integer("downloader").notNullable().defaultTo(1); - table.float("progress").notNullable().defaultTo(0); - table.integer("bytesDownloaded").notNullable().defaultTo(0); - table.datetime("lastTimePlayed"); - table.float("fileSize").notNullable().defaultTo(0); - table.text("uri"); - table.boolean("isDeleted").notNullable().defaultTo(0); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - table - .integer("repackId") - .references("repack.id") - .unique("repack_repackId_unique_" + timestamp); - }); - } - }); - - await knex.schema.hasTable("user_preferences").then(async (exists) => { - if (!exists) { - await knex.schema.createTable("user_preferences", (table) => { - table.increments("id").primary(); - table.text("downloadsPath"); - table.text("language").notNullable().defaultTo("en"); - table.text("realDebridApiToken"); - table - .boolean("downloadNotificationsEnabled") - .notNullable() - .defaultTo(0); - table - .boolean("repackUpdatesNotificationsEnabled") - .notNullable() - .defaultTo(0); - table.boolean("preferQuitInsteadOfHiding").notNullable().defaultTo(0); - table.boolean("runAtStartup").notNullable().defaultTo(0); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - }); - } - }); - - await knex.schema.hasTable("game_shop_cache").then(async (exists) => { - if (!exists) { - await knex.schema.createTable("game_shop_cache", (table) => { - table.text("objectID").primary().notNullable(); - table.text("shop").notNullable(); - table.text("serializedData"); - table.text("howLongToBeatSerializedData"); - table.text("language"); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - }); - } - }); - - await knex.schema.hasTable("download_queue").then(async (exists) => { - if (!exists) { - await knex.schema.createTable("download_queue", (table) => { - table.increments("id").primary(); - table - .integer("gameId") - .references("game.id") - .unique("download_queue_gameId_unique_" + timestamp); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - }); - } - }); - - await knex.schema.hasTable("user_auth").then(async (exists) => { - if (!exists) { - await knex.schema.createTable("user_auth", (table) => { - table.increments("id").primary(); - table.text("userId").notNullable().defaultTo(""); - table.text("displayName").notNullable().defaultTo(""); - table.text("profileImageUrl"); - table.text("accessToken").notNullable().defaultTo(""); - table.text("refreshToken").notNullable().defaultTo(""); - table.integer("tokenExpirationTimestamp").notNullable().defaultTo(0); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - }); - } - }); -}; - -export const down = async (knex: Knex) => { - await knex.schema.dropTableIfExists("game"); - await knex.schema.dropTableIfExists("repack"); - await knex.schema.dropTableIfExists("download_queue"); - await knex.schema.dropTableIfExists("user_auth"); - await knex.schema.dropTableIfExists("game_shop_cache"); - await knex.schema.dropTableIfExists("user_preferences"); - await knex.schema.dropTableIfExists("download_source"); -}; diff --git a/src/main/migrations/002_RepackUris.ts b/src/main/migrations/002_RepackUris.ts deleted file mode 100644 index 50d38d0f..00000000 --- a/src/main/migrations/002_RepackUris.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Knex } from "knex"; - -export const name = "002_RepackUris"; - -export const up = async (knex: Knex) => { - await knex.schema.createTable("temporary_repack", (table) => { - const timestamp = new Date().getTime(); - table.increments("id").primary(); - table - .text("title") - .notNullable() - .unique({ indexName: "repack_title_unique_" + timestamp }); - table - .text("magnet") - .notNullable() - .unique({ indexName: "repack_magnet_unique_" + timestamp }); - table.text("repacker").notNullable(); - table.text("fileSize").notNullable(); - table.datetime("uploadDate").notNullable(); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - table - .integer("downloadSourceId") - .references("download_source.id") - .onDelete("CASCADE"); - table.text("uris").notNullable().defaultTo("[]"); - }); - await knex.raw( - `INSERT INTO "temporary_repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"` - ); - await knex.schema.dropTable("repack"); - await knex.schema.renameTable("temporary_repack", "repack"); -}; - -export const down = async (knex: Knex) => { - await knex.schema.renameTable("repack", "temporary_repack"); - await knex.schema.createTable("repack", (table) => { - table.increments("id").primary(); - table.text("title").notNullable().unique(); - table.text("magnet").notNullable().unique(); - table.integer("page"); - table.text("repacker").notNullable(); - table.text("fileSize").notNullable(); - table.datetime("uploadDate").notNullable(); - table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); - table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); - table - .integer("downloadSourceId") - .references("download_source.id") - .onDelete("CASCADE"); - }); - await knex.raw( - `INSERT INTO "repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"` - ); - await knex.schema.dropTable("temporary_repack"); -}; diff --git a/src/main/migrations/20240830143811_Hydra_2_0_3.ts b/src/main/migrations/20240830143811_Hydra_2_0_3.ts new file mode 100644 index 00000000..6013f714 --- /dev/null +++ b/src/main/migrations/20240830143811_Hydra_2_0_3.ts @@ -0,0 +1,171 @@ +import type { HydraMigration } from "@main/knex-client"; +import type { Knex } from "knex"; + +export const Hydra2_0_3: HydraMigration = { + name: "Hydra_2_0_3", + up: async (knex: Knex) => { + const timestamp = new Date().getTime(); + + await knex.schema.hasTable("migrations").then(async (exists) => { + if (exists) { + await knex.schema.dropTable("migrations"); + } + }); + + await knex.schema.hasTable("download_source").then(async (exists) => { + if (!exists) { + await knex.schema.createTable("download_source", (table) => { + table.increments("id").primary(); + table + .text("url") + .unique({ indexName: "download_source_url_unique_" + timestamp }); + table.text("name").notNullable(); + table.text("etag"); + table.integer("downloadCount").notNullable().defaultTo(0); + table.text("status").notNullable().defaultTo(0); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + }); + } + }); + + await knex.schema.hasTable("repack").then(async (exists) => { + if (!exists) { + await knex.schema.createTable("repack", (table) => { + table.increments("id").primary(); + table + .text("title") + .notNullable() + .unique({ indexName: "repack_title_unique_" + timestamp }); + table + .text("magnet") + .notNullable() + .unique({ indexName: "repack_magnet_unique_" + timestamp }); + table.integer("page"); + table.text("repacker").notNullable(); + table.text("fileSize").notNullable(); + table.datetime("uploadDate").notNullable(); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + table + .integer("downloadSourceId") + .references("download_source.id") + .onDelete("CASCADE"); + }); + } + }); + + await knex.schema.hasTable("game").then(async (exists) => { + if (!exists) { + await knex.schema.createTable("game", (table) => { + table.increments("id").primary(); + table + .text("objectID") + .notNullable() + .unique({ indexName: "game_objectID_unique_" + timestamp }); + table + .text("remoteId") + .unique({ indexName: "game_remoteId_unique_" + timestamp }); + table.text("title").notNullable(); + table.text("iconUrl"); + table.text("folderName"); + table.text("downloadPath"); + table.text("executablePath"); + table.integer("playTimeInMilliseconds").notNullable().defaultTo(0); + table.text("shop").notNullable(); + table.text("status"); + table.integer("downloader").notNullable().defaultTo(1); + table.float("progress").notNullable().defaultTo(0); + table.integer("bytesDownloaded").notNullable().defaultTo(0); + table.datetime("lastTimePlayed"); + table.float("fileSize").notNullable().defaultTo(0); + table.text("uri"); + table.boolean("isDeleted").notNullable().defaultTo(0); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + table + .integer("repackId") + .references("repack.id") + .unique("repack_repackId_unique_" + timestamp); + }); + } + }); + + await knex.schema.hasTable("user_preferences").then(async (exists) => { + if (!exists) { + await knex.schema.createTable("user_preferences", (table) => { + table.increments("id").primary(); + table.text("downloadsPath"); + table.text("language").notNullable().defaultTo("en"); + table.text("realDebridApiToken"); + table + .boolean("downloadNotificationsEnabled") + .notNullable() + .defaultTo(0); + table + .boolean("repackUpdatesNotificationsEnabled") + .notNullable() + .defaultTo(0); + table.boolean("preferQuitInsteadOfHiding").notNullable().defaultTo(0); + table.boolean("runAtStartup").notNullable().defaultTo(0); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + }); + } + }); + + await knex.schema.hasTable("game_shop_cache").then(async (exists) => { + if (!exists) { + await knex.schema.createTable("game_shop_cache", (table) => { + table.text("objectID").primary().notNullable(); + table.text("shop").notNullable(); + table.text("serializedData"); + table.text("howLongToBeatSerializedData"); + table.text("language"); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + }); + } + }); + + await knex.schema.hasTable("download_queue").then(async (exists) => { + if (!exists) { + await knex.schema.createTable("download_queue", (table) => { + table.increments("id").primary(); + table + .integer("gameId") + .references("game.id") + .unique("download_queue_gameId_unique_" + timestamp); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + }); + } + }); + + await knex.schema.hasTable("user_auth").then(async (exists) => { + if (!exists) { + await knex.schema.createTable("user_auth", (table) => { + table.increments("id").primary(); + table.text("userId").notNullable().defaultTo(""); + table.text("displayName").notNullable().defaultTo(""); + table.text("profileImageUrl"); + table.text("accessToken").notNullable().defaultTo(""); + table.text("refreshToken").notNullable().defaultTo(""); + table.integer("tokenExpirationTimestamp").notNullable().defaultTo(0); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + }); + } + }); + }, + + down: async (knex: Knex) => { + await knex.schema.dropTableIfExists("game"); + await knex.schema.dropTableIfExists("repack"); + await knex.schema.dropTableIfExists("download_queue"); + await knex.schema.dropTableIfExists("user_auth"); + await knex.schema.dropTableIfExists("game_shop_cache"); + await knex.schema.dropTableIfExists("user_preferences"); + await knex.schema.dropTableIfExists("download_source"); + }, +}; diff --git a/src/main/migrations/20240830143906_RepackUris.ts b/src/main/migrations/20240830143906_RepackUris.ts new file mode 100644 index 00000000..0785d50d --- /dev/null +++ b/src/main/migrations/20240830143906_RepackUris.ts @@ -0,0 +1,58 @@ +import type { HydraMigration } from "@main/knex-client"; +import type { Knex } from "knex"; + +export const RepackUris: HydraMigration = { + name: "RepackUris", + up: async (knex: Knex) => { + await knex.schema.createTable("temporary_repack", (table) => { + const timestamp = new Date().getTime(); + table.increments("id").primary(); + table + .text("title") + .notNullable() + .unique({ indexName: "repack_title_unique_" + timestamp }); + table + .text("magnet") + .notNullable() + .unique({ indexName: "repack_magnet_unique_" + timestamp }); + table.text("repacker").notNullable(); + table.text("fileSize").notNullable(); + table.datetime("uploadDate").notNullable(); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + table + .integer("downloadSourceId") + .references("download_source.id") + .onDelete("CASCADE"); + table.text("uris").notNullable().defaultTo("[]"); + }); + await knex.raw( + `INSERT INTO "temporary_repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"` + ); + await knex.schema.dropTable("repack"); + await knex.schema.renameTable("temporary_repack", "repack"); + }, + + down: async (knex: Knex) => { + await knex.schema.renameTable("repack", "temporary_repack"); + await knex.schema.createTable("repack", (table) => { + table.increments("id").primary(); + table.text("title").notNullable().unique(); + table.text("magnet").notNullable().unique(); + table.integer("page"); + table.text("repacker").notNullable(); + table.text("fileSize").notNullable(); + table.datetime("uploadDate").notNullable(); + table.datetime("createdAt").notNullable().defaultTo(knex.fn.now()); + table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now()); + table + .integer("downloadSourceId") + .references("download_source.id") + .onDelete("CASCADE"); + }); + await knex.raw( + `INSERT INTO "repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"` + ); + await knex.schema.dropTable("temporary_repack"); + }, +}; diff --git a/src/main/migrations/index.ts b/src/main/migrations/index.ts deleted file mode 100644 index 3859255a..00000000 --- a/src/main/migrations/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * as hydra from "./001_Hydra_2_0_3"; -export * as downloadRefactor from "./002_RepackUris"; diff --git a/src/main/migrations/migration.stub b/src/main/migrations/migration.stub new file mode 100644 index 00000000..9cb0cbab --- /dev/null +++ b/src/main/migrations/migration.stub @@ -0,0 +1,11 @@ +import type { HydraMigration } from "@main/knex-client"; +import type { Knex } from "knex"; + +export const MigrationName: HydraMigration = { + name: "MigrationName", + up: async (knex: Knex) => { + await knex.schema.createTable("table_name", (table) => {}); + }, + + down: async (knex: Knex) => {}, +}; From 158b878883beac038cd6b4b96feadf9ff6b7fd23 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 31 Aug 2024 14:17:03 -0300 Subject: [PATCH 12/13] feat: catch hltb error --- src/main/services/how-long-to-beat.ts | 36 ++++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/main/services/how-long-to-beat.ts b/src/main/services/how-long-to-beat.ts index 67e96942..c7164d09 100644 --- a/src/main/services/how-long-to-beat.ts +++ b/src/main/services/how-long-to-beat.ts @@ -2,6 +2,7 @@ import axios from "axios"; import { requestWebPage } from "@main/helpers"; import { HowLongToBeatCategory } from "@types"; import { formatName } from "@shared"; +import { logger } from "./logger"; export interface HowLongToBeatResult { game_id: number; @@ -13,22 +14,27 @@ export interface HowLongToBeatSearchResponse { } export const searchHowLongToBeat = async (gameName: string) => { - const response = await axios.post( - "https://howlongtobeat.com/api/search", - { - searchType: "games", - searchTerms: formatName(gameName).split(" "), - searchPage: 1, - size: 100, - }, - { - headers: { - "User-Agent": - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", - Referer: "https://howlongtobeat.com/", + const response = await axios + .post( + "https://howlongtobeat.com/api/search", + { + searchType: "games", + searchTerms: formatName(gameName).split(" "), + searchPage: 1, + size: 100, }, - } - ); + { + headers: { + "User-Agent": + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", + Referer: "https://howlongtobeat.com/", + }, + } + ) + .catch((error) => { + logger.error("Error searching HowLongToBeat:", error?.response?.status); + return { data: { data: [] } }; + }); return response.data as HowLongToBeatSearchResponse; }; From 88737cf80d2efb58f8fd6143213bab7cf9517dec Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:54:55 -0300 Subject: [PATCH 13/13] feat: refactor knex promises --- src/main/index.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 5293cda8..93481f61 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -53,6 +53,18 @@ if (process.defaultApp) { app.setAsDefaultProtocolClient(PROTOCOL); } +const runMigrations = async () => { + await knexClient.migrate.list(migrationConfig).then((result) => { + logger.log( + "Migrations to run:", + result[1].map((migration) => migration.name) + ); + }); + + await knexClient.migrate.latest(migrationConfig); + await knexClient.destroy(); +}; + // 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. @@ -64,15 +76,7 @@ app.whenReady().then(async () => { return net.fetch(url.pathToFileURL(decodeURI(filePath)).toString()); }); - await knexClient.migrate.list(migrationConfig).then((result) => { - logger.log( - "Migrations to run:", - result[1].map((migration) => migration.name) - ); - }); - - await knexClient.migrate - .latest(migrationConfig) + await runMigrations() .then(() => { logger.log("Migrations executed successfully"); }) @@ -80,8 +84,6 @@ app.whenReady().then(async () => { logger.log("Migrations failed to run:", err); }); - await knexClient.destroy(); - await dataSource.initialize(); await import("./main");