Skip to content
Draft
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
65 changes: 65 additions & 0 deletions i18n/en-US.messages.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,19 @@ export declare const messages: {
* Missing translations: `bg`, `da`, `el`, `es-419`, `hi`, `hr`, `lt`, `ro`, `th`
*/
'REPLUGGED_ADDON_SETTINGS': TypedIntlMessageGetter<{type: any}>,
/**
* Key: `qdaVER`
*
* ### Definition
* ```text
* Open {type} Source
* ```
*
* ### Problems
*
* Missing translations: `bg`, `cs`, `da`, `de`, `el`, `en-GB`, `es-419`, `es-ES`, `fi`, `fr`, `hi`, `hr`, `hu`, `it`, `ja`, `ko`, `lt`, `nl`, `no`, `pl`, `pt-BR`, `ro`, `ru`, `sv-SE`, `th`, `tr`, `uk`, `vi`, `zh-CN`, `zh-TW`
*/
'REPLUGGED_ADDON_SOURCE_OPEN': TypedIntlMessageGetter<{type: any}>,
/**
* Key: `WqqBfH`
*
Expand Down Expand Up @@ -1308,6 +1321,19 @@ export declare const messages: {
* Missing translations: `bg`, `da`, `el`, `es-419`, `hi`, `hr`, `lt`, `no`, `ro`, `th`
*/
'REPLUGGED_SEARCH_FOR_ADDON': TypedIntlMessageGetter<{type: any}>,
/**
* Key: `g2lGYW`
*
* ### Definition
* ```text
* Search for a {type} on store
* ```
*
* ### Problems
*
* Missing translations: `bg`, `cs`, `da`, `de`, `el`, `en-GB`, `es-419`, `es-ES`, `fi`, `fr`, `hi`, `hr`, `hu`, `it`, `ja`, `ko`, `lt`, `nl`, `no`, `pl`, `pt-BR`, `ro`, `ru`, `sv-SE`, `th`, `tr`, `uk`, `vi`, `zh-CN`, `zh-TW`
*/
'REPLUGGED_SEARCH_FOR_ADDON_STORE': TypedIntlMessageGetter<{type: any}>,
/**
* Key: `wxHqIi`
*
Expand Down Expand Up @@ -1854,6 +1880,45 @@ export declare const messages: {
* Missing translations: `bg`, `da`, `el`, `es-ES`, `fr`, `hi`, `hr`, `hu`, `ko`, `lt`, `nl`, `no`, `ro`, `ru`, `sv-SE`, `vi`, `zh-CN`
*/
'REPLUGGED_STORE': TypedIntlMessageGetter<{}>,
/**
* Key: `W+c1ER`
*
* ### Definition
* ```text
* Replugged Addon Store is now built in app for ease of use.
* ```
*
* ### Problems
*
* Missing translations: `bg`, `cs`, `da`, `de`, `el`, `en-GB`, `es-419`, `es-ES`, `fi`, `fr`, `hi`, `hr`, `hu`, `it`, `ja`, `ko`, `lt`, `nl`, `no`, `pl`, `pt-BR`, `ro`, `ru`, `sv-SE`, `th`, `tr`, `uk`, `vi`, `zh-CN`, `zh-TW`
*/
'REPLUGGED_STORE_INCLUDED_IN_APP': TypedIntlMessageGetter<{}>,
/**
* Key: `JjGds7`
*
* ### Definition
* ```text
* You are on page of {current} of {total}
* ```
*
* ### Problems
*
* Missing translations: `bg`, `cs`, `da`, `de`, `el`, `en-GB`, `es-419`, `es-ES`, `fi`, `fr`, `hi`, `hr`, `hu`, `it`, `ja`, `ko`, `lt`, `nl`, `no`, `pl`, `pt-BR`, `ro`, `ru`, `sv-SE`, `th`, `tr`, `uk`, `vi`, `zh-CN`, `zh-TW`
*/
'REPLUGGED_STORE_PAGINATOR': TypedIntlMessageGetter<{current: any, total: any}>,
/**
* Key: `vz3Ak5`
*
* ### Definition
* ```text
* Where Can I get more {type}?
* ```
*
* ### Problems
*
* Missing translations: `bg`, `cs`, `da`, `de`, `el`, `en-GB`, `es-419`, `es-ES`, `fi`, `fr`, `hi`, `hr`, `hu`, `it`, `ja`, `ko`, `lt`, `nl`, `no`, `pl`, `pt-BR`, `ro`, `ru`, `sv-SE`, `th`, `tr`, `uk`, `vi`, `zh-CN`, `zh-TW`
*/
'REPLUGGED_STORE_WHERE_TO_FIND_ADDONS': TypedIntlMessageGetter<{type: any}>,
/**
* Key: `kZXj5O`
*
Expand Down
7 changes: 6 additions & 1 deletion i18n/en-US.messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export default defineMessages({
"REPLUGGED_PLUGIN_EMBED_WHATISTHIS_CONTENT": "This is a Replugged feature. It allows you to install plugins or themes straight from chat.\nSimply hit the install button on the embed.",
"REPLUGGED_ADDON_DELETE": "Delete {type}",
"REPLUGGED_ADDON_PAGE_OPEN": "Open {type} Page",
"REPLUGGED_ADDON_SOURCE_OPEN": "Open {type} Source",
"REPLUGGED_ADDON_PROFILE_OPEN": "Open {type} Profile",
"REPLUGGED_ADDON_RELOAD": "Reload {type}",
"REPLUGGED_ADDON_SETTINGS": "Open {type} Settings",
Expand All @@ -171,6 +172,7 @@ export default defineMessages({
"REPLUGGED_NO_ADDONS_INSTALLED": "No {type} installed.",
"REPLUGGED_QUICKCSS_CHANGES_APPLY": "Apply Changes",
"REPLUGGED_SEARCH_FOR_ADDON": "Search for a {type}",
"REPLUGGED_SEARCH_FOR_ADDON_STORE": "Search for a {type} on store",
"REPLUGGED_TOAST_ADDON_DISABLE_FAILED": "Failed to disable {name}",
"REPLUGGED_TOAST_ADDON_DISABLE_SUCCESS": "Disabled {name}",
"REPLUGGED_TOAST_ADDON_ENABLE_FAILED": "Failed to enable {name}",
Expand Down Expand Up @@ -234,10 +236,13 @@ export default defineMessages({
"REPLUGGED_SETTINGS_TRANSPARENT_ISSUES_LINUX": "****WARNING:**** **Hardware acceleration** may need to be turned **off**. In some cases, you may experience a black background, such as when the window is cut off at the top or bottom due to the monitor resolution, or when the development tools are open and docked.",
"REPLUGGED_SETTINGS_ERROR_PLUGIN_NAME": "Plugin: {name}",
"REPLUGGED_STORE": "Store",
"REPLUGGED_STORE_INCLUDED_IN_APP": "Replugged Addon Store is now built in app for ease of use.",
"REPLUGGED_STORE_WHERE_TO_FIND_ADDONS": "Where Can I get more {type}?",
"REPLUGGED_STORE_PAGINATOR": "You are on page of {current} of {total}",
"REPLUGGED_SETTINGS_CUSTOM_TITLE_BAR": "Custom Title Bar",
"REPLUGGED_SETTINGS_CUSTOM_TITLE_BAR_DESC": "Use Discord's custom title bar instead of the system title bar. **Requires restart**.",
"REPLUGGED_SETTINGS_DISCORD_DEVTOOLS": "Enable Discord Internal DevTools",
"REPLUGGED_SETTINGS_DISCORD_DEVTOOLS_DESC": "Replaces the help button in the title bar with Discord's internal developer tools (different from Chrome DevTools). This setting requires Discord experiments to be enabled first. **Requires restart**.",
"REPLUGGED_SETTINGS_QUICKCSS_ENABLE": "Enable Quick CSS",
"REPLUGGED_SETTINGS_QUICKCSS_ENABLE_DESC": "Apply custom styles to Discord instantly. Change colors, layout, and appearance in real time without installing themes."
});
});
90 changes: 81 additions & 9 deletions src/main/ipc/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import { writeFile as originalWriteFile } from "original-fs";
import { join, resolve, sep } from "path";
import { WEBSITE_URL } from "src/constants";
import {
type CheckResultFailure,
type CheckResultSuccess,
type InstallResultFailure,
type InstallResultSuccess,
type InstallerType,
RepluggedIpcChannels,
type ResultFailure,
} from "src/types";
import { type AnyAddonManifestOrReplugged, anyAddonOrReplugged } from "src/types/addon";
import {
type AnyAddonManifest,
type AnyAddonManifestOrReplugged,
anyAddonOrReplugged,
} from "src/types/addon";
import { CONFIG_PATH, CONFIG_PATHS } from "src/util.mjs";
import type { PackageJson } from "type-fest";
import { promisify } from "util";
Expand Down Expand Up @@ -40,7 +43,7 @@ interface ReleaseAsset {
async function github(
identifier: string,
id?: string,
): Promise<CheckResultSuccess | CheckResultFailure> {
): Promise<CheckResultSuccess | ResultFailure> {
const [owner, repo] = identifier.split("/");
if (!owner || !repo) {
return {
Expand Down Expand Up @@ -106,7 +109,7 @@ async function github(
};
}

async function store(id: string): Promise<CheckResultSuccess | CheckResultFailure> {
async function store(id: string): Promise<CheckResultSuccess | ResultFailure> {
const apiUrl = getSetting("dev.replugged.Settings", "apiUrl", WEBSITE_URL);
const STORE_BASE_URL = `${apiUrl}/api/v1/store`;
const manifestUrl = `${STORE_BASE_URL}/${id}`;
Expand Down Expand Up @@ -140,7 +143,7 @@ async function store(id: string): Promise<CheckResultSuccess | CheckResultFailur

const handlers: Record<
string,
(identifier: string, id?: string) => Promise<CheckResultSuccess | CheckResultFailure>
(identifier: string, id?: string) => Promise<CheckResultSuccess | ResultFailure>
> = {
github,
store,
Expand All @@ -150,7 +153,7 @@ export async function getAddonInfo(
type: string,
identifier: string,
id?: string,
): Promise<CheckResultSuccess | CheckResultFailure> {
): Promise<CheckResultSuccess | ResultFailure> {
if (!(type in handlers)) {
return {
success: false,
Expand All @@ -168,7 +171,7 @@ ipcMain.handle(
type: string,
identifier: string,
id?: string,
): Promise<CheckResultSuccess | CheckResultFailure> => {
): Promise<CheckResultSuccess | ResultFailure> => {
return getAddonInfo(type, identifier, id);
},
);
Expand All @@ -190,7 +193,7 @@ export async function installAddon(
url: string,
update: boolean,
version?: string,
): Promise<InstallResultSuccess | InstallResultFailure> {
): Promise<InstallResultSuccess | ResultFailure> {
const query = new URLSearchParams();
query.set("type", update ? "update" : "install");
if (version) query.set("version", version);
Expand Down Expand Up @@ -275,3 +278,72 @@ export function getRepluggedVersion(): string {
ipcMain.on(RepluggedIpcChannels.GET_REPLUGGED_VERSION, (event) => {
event.returnValue = getRepluggedVersion();
});

ipcMain.handle(
RepluggedIpcChannels.LIST_ADDONS,
async (
_,
type: "plugins" | "themes",
abortController?: AbortController,
page = 1,
query = "",
maxItems = 15,
) => {
const apiUrl = getSetting("dev.replugged.Settings", "apiUrl", WEBSITE_URL);
const STORE_BASE_URL = `${apiUrl}/api/v1/store`;
const listUrl = `${STORE_BASE_URL}/list/${type}?page=${page}&items=${maxItems}&query=${query}`;

const res = await fetch(listUrl, { method: "get", signal: abortController?.signal });

if (!res.ok) {
return {
success: false,
error: "Failed to fetch addon list",
};
}

let result;
try {
const {
error,
numPages,
page,
results: list,
} = (await res.json()) as
| {
numPages: number;
page: number;
results: AnyAddonManifest[];
error: never;
}
| { numPages: never; page: never; results: never; error: string };
if (error)
return {
success: false,
error,
};

result = {
numPages,
page,
list: list.map((manifest) => {
return {
manifest,
name: `${manifest.id}.asar`,
url: `${STORE_BASE_URL}/${manifest.id}.asar`,
};
}),
};
} catch {
return {
success: false,
error: "Failed to parse list",
};
}

return {
success: true,
...result,
};
},
);
30 changes: 24 additions & 6 deletions src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { contextBridge, ipcRenderer, webFrame } from "electron";
import { RepluggedIpcChannels } from "./types";
// eslint-disable-next-line no-duplicate-imports -- these are only used for types, the other import is for the actual code
import type {
CheckResultFailure,
CheckResultSuccess,
InstallResultFailure,
InstallResultSuccess,
InstallerType,
ListResultSuccess,
RepluggedPlugin,
RepluggedTheme,
ResultFailure,
} from "./types";

const version = ipcRenderer.sendSync(RepluggedIpcChannels.GET_REPLUGGED_VERSION);
Expand Down Expand Up @@ -39,14 +39,14 @@ const RepluggedNative = {
type: string,
identifier: string,
id: string,
): Promise<CheckResultSuccess | CheckResultFailure> =>
): Promise<CheckResultSuccess | ResultFailure> =>
ipcRenderer.invoke(RepluggedIpcChannels.GET_ADDON_INFO, type, identifier, id),
install: async (
type: InstallerType | "replugged",
path: string,
url: string,
version: string,
): Promise<InstallResultSuccess | InstallResultFailure> =>
): Promise<InstallResultSuccess | ResultFailure> =>
ipcRenderer.invoke(RepluggedIpcChannels.INSTALL_ADDON, type, path, url, true, version),
},

Expand All @@ -55,17 +55,35 @@ const RepluggedNative = {
type: string,
repo: string,
id?: string,
): Promise<CheckResultSuccess | CheckResultFailure> =>
): Promise<CheckResultSuccess | ResultFailure> =>
ipcRenderer.invoke(RepluggedIpcChannels.GET_ADDON_INFO, type, repo, id),
install: async (
type: InstallerType,
path: string,
url: string,
version: string,
): Promise<InstallResultSuccess | InstallResultFailure> =>
): Promise<InstallResultSuccess | ResultFailure> =>
ipcRenderer.invoke(RepluggedIpcChannels.INSTALL_ADDON, type, path, url, false, version),
},

store: {
getList: async (
type: "plugin" | "theme",
page?: number,
query?: string,
abortController?: AbortController,
maxItems?: number,
): Promise<ListResultSuccess | ResultFailure> =>
ipcRenderer.invoke(
RepluggedIpcChannels.LIST_ADDONS,
type,
abortController,
page,
query,
maxItems,
),
},

quickCSS: {
get: async () => ipcRenderer.invoke(RepluggedIpcChannels.GET_QUICK_CSS),
save: (css: string) => ipcRenderer.send(RepluggedIpcChannels.SAVE_QUICK_CSS, css),
Expand Down
8 changes: 8 additions & 0 deletions src/renderer/coremods/settings/icons/Clipboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// copied from discord context menu
export default (): React.ReactElement => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M3 16a1 1 0 0 1-1-1v-5a8 8 0 0 1 8-8h5a1 1 0 0 1 1 1v.5a.5.5 0 0 1-.5.5H10a6 6 0 0 0-6 6v5.5a.5.5 0 0 1-.5.5H3Z" />
<path d="M6 18a4 4 0 0 0 4 4h8a4 4 0 0 0 4-4v-4h-3a5 5 0 0 1-5-5V6h-4a4 4 0 0 0-4 4v8Z" />
<path d="M21.73 12a3 3 0 0 0-.6-.88l-4.25-4.24a3 3 0 0 0-.88-.61V9a3 3 0 0 0 3 3h2.73Z" />
</svg>
);
7 changes: 7 additions & 0 deletions src/renderer/coremods/settings/icons/Preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// copied from discord popout
export default (): React.ReactElement => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M15 2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 1 1-2 0V4.41l-4.3 4.3a1 1 0 1 1-1.4-1.42L19.58 3H16a1 1 0 0 1-1-1Z" />
<path d="M5 2a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-6a1 1 0 1 0-2 0v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h6a1 1 0 1 0 0-2H5Z" />
</svg>
);
4 changes: 4 additions & 0 deletions src/renderer/coremods/settings/icons/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import Clipboard from "./Clipboard";
import Discord from "./Discord";
import GitHub from "./GitHub";
import Preview from "./Preview";
import Link from "./Link";
import Reload from "./Reload";
import Settings from "./Settings";
import Trash from "./Trash";

export default {
Clipboard,
Discord,
GitHub,
Preview,
Link,
Reload,
Settings,
Expand Down
Loading