Skip to content

Commit

Permalink
refactor: with results
Browse files Browse the repository at this point in the history
  • Loading branch information
pd4d10 committed Feb 15, 2024
1 parent c130366 commit d2385a3
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 175 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"react-use": "^17.5.0",
"ts-results": "^3.3.0",
"typescript": "^5.3.3",
"universal-analytics": "^0.5.3"
},
Expand Down
28 changes: 0 additions & 28 deletions src/main/utils.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,8 @@
import fs from "fs";
import { machineId } from "node-machine-id";
import os from "os";
// import { setUpdateNotification } from 'electron-update-notification' // TODO:
import ua from "universal-analytics";

export async function readdirSafe(p: string) {
try {
return await fs.promises.readdir(p);
} catch (err) {
console.error(err);
return [];
}
}

export async function readFileSafe(p: string) {
try {
return await fs.promises.readFile(p, { encoding: "utf8" });
} catch (err) {
console.error(err);
return "";
}
}

export async function readFileAsBufferSafe(p: string) {
try {
return await fs.promises.readFile(p);
} catch (err) {
console.error(err);
return;
}
}

export async function setReporter() {
try {
const id = await machineId();
Expand Down
119 changes: 64 additions & 55 deletions src/platforms/linux.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,80 @@
import { readdirSafe, readFileSafe } from "../main/utils";
import type { AppInfo } from "../reducers/app";
import type { AppReader } from "./utils";
import fs from "fs";
import ini from "ini";
import path from "path";
import { Result } from "ts-results";

const desktopFilesDir = "/usr/share/applications";

async function readAppInfo(desktopFile: string): Promise<AppInfo | undefined> {
const content = await readFileSafe(desktopFile);
const readAppInfo = (desktopFile: string) =>
Result.wrapAsync(async () => {
const content = await fs.promises.readFile(desktopFile, {
encoding: "utf-8",
});
const entry = ini.parse(content)["Desktop Entry"] as
| {
Name?: string;
Icon?: string;
Exec?: string;
}
| undefined;

const entry = ini.parse(content)["Desktop Entry"] as {
Name?: string;
Icon?: string;
Exec?: string;
};
if (!entry || !entry.Exec) return;
if (!entry?.Exec) throw new Error("Exec not found");

let exePath = "";
if (entry.Exec.startsWith('"')) {
exePath = entry.Exec.replace(/^"(.*)".*/, "$1");
} else {
// Remove arg
exePath = entry.Exec.split(/\s+/)[0] ?? "";
}

if (!exePath.startsWith("/")) return;

if (!fs.existsSync(path.join(exePath, "../resources/electron.asar"))) return;
let exePath = "";
if (entry.Exec.startsWith('"')) {
exePath = entry.Exec.replace(/^"(.*)".*/, "$1");
} else {
// Remove arg
exePath = entry.Exec.split(/\s+/)[0] ?? "";
}

let icon = "";
if (entry.Icon) {
try {
const iconBuffer = await fs.promises.readFile(
`/usr/share/icons/hicolor/1024x1024/apps/${entry.Icon}.png`,
);
icon = "data:image/png;base64," + iconBuffer.toString("base64");
} catch (err) {
console.error(err);
if (!exePath.startsWith("/")) {
throw new Error("Exec path invalid");
}
if (!fs.existsSync(path.join(exePath, "../resources/electron.asar"))) {
throw new Error("resources/electron.asar not exists");
}
}

return {
id: exePath,
icon: icon, // TODO: Read icon
name: entry.Name || path.basename(exePath),
exePath: exePath,
};
}
let icon = "";
if (entry.Icon) {
try {
const iconBuffer = await fs.promises.readFile(
`/usr/share/icons/hicolor/1024x1024/apps/${entry.Icon}.png`,
);
icon = "data:image/png;base64," + iconBuffer.toString("base64");
} catch (err) {
console.error(err);
}
}

export const adapter: AppReader = {
async readAll() {
const files = await readdirSafe(desktopFilesDir);
const apps = await Promise.all(
files.map((file) => {
if (!file.endsWith(".desktop")) return;
return readAppInfo(path.join(desktopFilesDir, file));
}),
);
return apps;
},
async readByPath(p: string) {
return {
id: p,
name: path.basename(p),
icon: "",
exePath: p,
id: exePath,
icon: icon, // TODO: Read icon
name: entry.Name || path.basename(exePath),
exePath: exePath,
};
},
});

export const adapter: AppReader = {
readAll: () =>
Result.wrapAsync(async () => {
const files = await fs.promises.readdir(desktopFilesDir);
const apps = await Promise.all(
files
.filter((f) => f.endsWith(".desktop"))
.map((file) => readAppInfo(path.join(desktopFilesDir, file))),
);
return apps.flatMap((app) => (app.ok ? [app.unwrap()] : []));
}),
readByPath: (p: string) =>
Result.wrapAsync(async () => {
// TODO:
return {
id: p,
name: path.basename(p),
icon: "",
exePath: p,
};
}),
};
52 changes: 28 additions & 24 deletions src/platforms/macos.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { readdirSafe, readFileAsBufferSafe } from "../main/utils";
import type { AppReader } from "./utils";
import fs from "fs";
import path from "path";
import plist from "simple-plist";
import { Result } from "ts-results";

interface MacosAppInfo {
CFBundleIdentifier: string;
Expand All @@ -26,7 +26,7 @@ async function readPlistFile(path: string) {
}

async function readIcnsAsImageUri(file: string) {
let buf = await readFileAsBufferSafe(file);
let buf = await fs.promises.readFile(file);
if (!buf) return "";

const totalSize = buf.readInt32BE(4) - 8;
Expand Down Expand Up @@ -55,28 +55,32 @@ async function readIcnsAsImageUri(file: string) {
}

export const adapter: AppReader = {
async readAll() {
const dir = "/Applications";
const appPaths = await readdirSafe(dir);
return Promise.all(appPaths.map((p) => this.readByPath(path.join(dir, p))));
},
async readByPath(p: string) {
const isElectronBased = fs.existsSync(
path.join(p, "Contents/Frameworks/Electron Framework.framework"),
);
if (!isElectronBased) return;
readAll: () =>
Result.wrapAsync(async () => {
const dir = "/Applications";
const appPaths = await fs.promises.readdir(dir);
const apps = await Promise.all(
appPaths.map((p) => adapter.readByPath(path.join(dir, p))),
);
return apps.flatMap((app) => (app.ok ? [app.unwrap()] : []));
}),
readByPath: (p: string) =>
Result.wrapAsync(async () => {
const isElectronBased = fs.existsSync(
path.join(p, "Contents/Frameworks/Electron Framework.framework"),
);
if (!isElectronBased) throw new Error("Not an electron app");

const info = await readPlistFile(path.join(p, "Contents/Info.plist"));
const info = await readPlistFile(path.join(p, "Contents/Info.plist"));
const icon = await readIcnsAsImageUri(
path.join(p, "Contents/Resources", info.CFBundleIconFile),
);

const icon = await readIcnsAsImageUri(
path.join(p, "Contents/Resources", info.CFBundleIconFile),
);

return {
id: info.CFBundleIdentifier,
name: info.CFBundleName,
icon,
exePath: path.resolve(p, "Contents/MacOS", info.CFBundleExecutable),
};
},
return {
id: info.CFBundleIdentifier,
name: info.CFBundleName,
icon,
exePath: path.resolve(p, "Contents/MacOS", info.CFBundleExecutable),
};
}),
};
5 changes: 3 additions & 2 deletions src/platforms/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { AppInfo } from "../reducers/app";
import { Result } from "ts-results";

export interface AppReader {
readAll(): Promise<(AppInfo | undefined)[]>;
readByPath(p: string): Promise<AppInfo | undefined>;
readAll(): Promise<Result<AppInfo[], Error>>;
readByPath(p: string): Promise<Result<AppInfo, Error>>;
}

0 comments on commit d2385a3

Please sign in to comment.