mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-08-20 09:34:35 -05:00
Show share menu for web services
This commit is contained in:
@@ -266,7 +266,7 @@ function getEnvApi(): FApi {
|
||||
throw new Error('Unknown znca API provider');
|
||||
}
|
||||
|
||||
if (process.env.ZNCA_API_URL?.startsWith('https://')) {
|
||||
if (process.env.ZNCA_API_URL) {
|
||||
return ['nxapi', process.env.ZNCA_API_URL + '/f'];
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ export const MenuItem = electron.MenuItem;
|
||||
export const nativeImage = electron.nativeImage;
|
||||
export const Notification = electron.Notification;
|
||||
export const session = electron.session;
|
||||
export const ShareMenu = electron.ShareMenu;
|
||||
export const shell = electron.shell;
|
||||
export const Tray = electron.Tray;
|
||||
|
||||
@@ -20,5 +21,6 @@ export type IpcMainInvokeEvent = import('electron').IpcMainInvokeEvent;
|
||||
export type Menu = import('electron').Menu;
|
||||
export type MenuItem = import('electron').MenuItem;
|
||||
export type Notification = import('electron').Notification;
|
||||
export type ShareMenu = import('electron').ShareMenu;
|
||||
export type Tray = import('electron').Tray;
|
||||
export type WebContents = import('electron').WebContents;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { app, BrowserWindow, dialog, ipcMain, nativeImage, Notification } from './electron.js';
|
||||
import { app, BrowserWindow, dialog, ipcMain, Notification } from './electron.js';
|
||||
import * as path from 'path';
|
||||
import { EventEmitter } from 'events';
|
||||
import createDebug from 'debug';
|
||||
import * as persist from 'node-persist';
|
||||
import fetch from 'node-fetch';
|
||||
import dotenv from 'dotenv';
|
||||
import dotenvExpand from 'dotenv-expand';
|
||||
import MenuApp from './menu.js';
|
||||
@@ -18,6 +17,7 @@ import { getToken } from '../../common/auth/nso.js';
|
||||
import { dir } from '../../util/product.js';
|
||||
import { initStorage, paths } from '../../util/storage.js';
|
||||
import { LoopResult } from '../../util/loop.js';
|
||||
import { tryGetNativeImageFromUrl } from './util.js';
|
||||
|
||||
const debug = createDebug('app:main');
|
||||
|
||||
@@ -251,16 +251,6 @@ export class EmbeddedPresenceMonitor extends ZncDiscordPresence {
|
||||
}
|
||||
|
||||
export class ElectronNotificationManager extends NotificationManager {
|
||||
private async getNativeImageFromUrl(url: string) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const image = await response.buffer();
|
||||
return nativeImage.createFromBuffer(image);
|
||||
} catch (err) {}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async onFriendOnline(friend: CurrentUser | Friend, prev?: CurrentUser | Friend, naid?: string, ir?: boolean) {
|
||||
const currenttitle = friend.presence.game as Game;
|
||||
|
||||
@@ -268,7 +258,7 @@ export class ElectronNotificationManager extends NotificationManager {
|
||||
title: friend.name,
|
||||
body: 'Playing ' + currenttitle.name +
|
||||
(currenttitle.sysDescription ? '\n' + currenttitle.sysDescription : ''),
|
||||
icon: await this.getNativeImageFromUrl(friend.imageUri),
|
||||
icon: await tryGetNativeImageFromUrl(friend.imageUri),
|
||||
}).show();
|
||||
}
|
||||
|
||||
@@ -276,7 +266,7 @@ export class ElectronNotificationManager extends NotificationManager {
|
||||
new Notification({
|
||||
title: friend.name,
|
||||
body: 'Offline',
|
||||
icon: await this.getNativeImageFromUrl(friend.imageUri),
|
||||
icon: await tryGetNativeImageFromUrl(friend.imageUri),
|
||||
}).show();
|
||||
}
|
||||
|
||||
@@ -287,7 +277,7 @@ export class ElectronNotificationManager extends NotificationManager {
|
||||
title: friend.name,
|
||||
body: 'Playing ' + currenttitle.name +
|
||||
(currenttitle.sysDescription ? '\n' + currenttitle.sysDescription : ''),
|
||||
icon: await this.getNativeImageFromUrl(friend.imageUri),
|
||||
icon: await tryGetNativeImageFromUrl(friend.imageUri),
|
||||
}).show();
|
||||
}
|
||||
|
||||
@@ -298,7 +288,7 @@ export class ElectronNotificationManager extends NotificationManager {
|
||||
title: friend.name,
|
||||
body: 'Playing ' + currenttitle.name +
|
||||
(currenttitle.sysDescription ? '\n' + currenttitle.sysDescription : ''),
|
||||
icon: await this.getNativeImageFromUrl(friend.imageUri),
|
||||
icon: await tryGetNativeImageFromUrl(friend.imageUri),
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import * as crypto from 'crypto';
|
||||
import createDebug from 'debug';
|
||||
import * as persist from 'node-persist';
|
||||
import fetch from 'node-fetch';
|
||||
import { BrowserWindow, nativeImage, Notification, session, shell } from './electron.js';
|
||||
import { BrowserWindow, Notification, session, shell } from './electron.js';
|
||||
import { getNintendoAccountSessionToken, NintendoAccountSessionToken } from '../../api/na.js';
|
||||
import { ZNCA_CLIENT_ID } from '../../api/znc.js';
|
||||
import { ZNMA_CLIENT_ID } from '../../api/moon.js';
|
||||
import { getToken, SavedToken } from '../../common/auth/nso.js';
|
||||
import { getPctlToken, SavedMoonToken } from '../../common/auth/moon.js';
|
||||
import { Jwt } from '../../util/jwt.js';
|
||||
import { tryGetNativeImageFromUrl } from './util.js';
|
||||
|
||||
const debug = createDebug('app:main:na-auth');
|
||||
|
||||
@@ -157,18 +157,10 @@ export async function addNsoAccount(storage: persist.LocalStorage) {
|
||||
|
||||
debug('Already authenticated', data);
|
||||
|
||||
let icon = undefined;
|
||||
|
||||
try {
|
||||
const response = await fetch(data!.nsoAccount.user.imageUri);
|
||||
const image = await response.buffer();
|
||||
icon = nativeImage.createFromBuffer(image);
|
||||
} catch (err) {}
|
||||
|
||||
new Notification({
|
||||
title: 'Nintendo Switch Online',
|
||||
body: 'Already signed in as ' + data?.nsoAccount.user.name + ' (' + data?.user.nickname + ')',
|
||||
icon,
|
||||
icon: await tryGetNativeImageFromUrl(data!.nsoAccount.user.imageUri),
|
||||
}).show();
|
||||
|
||||
return getToken(storage, nsotoken, process.env.ZNC_PROXY_URL);
|
||||
@@ -184,18 +176,10 @@ export async function addNsoAccount(storage: persist.LocalStorage) {
|
||||
users.add(data.user.id);
|
||||
await storage.setItem('NintendoAccountIds', [...users]);
|
||||
|
||||
let icon = undefined;
|
||||
|
||||
try {
|
||||
const response = await fetch(data.nsoAccount.user.imageUri);
|
||||
const image = await response.buffer();
|
||||
icon = nativeImage.createFromBuffer(image);
|
||||
} catch (err) {}
|
||||
|
||||
new Notification({
|
||||
title: 'Nintendo Switch Online',
|
||||
body: 'Authenticated as ' + data.nsoAccount.user.name + ' (NSO ' + data.user.nickname + ')',
|
||||
icon,
|
||||
icon: await tryGetNativeImageFromUrl(data.nsoAccount.user.imageUri),
|
||||
}).show();
|
||||
|
||||
return {nso, data};
|
||||
|
||||
17
src/app/main/util.ts
Normal file
17
src/app/main/util.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { nativeImage } from './electron.js';
|
||||
import { Buffer } from 'buffer';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
export async function getNativeImageFromUrl(url: URL | string) {
|
||||
const response = await fetch(url.toString());
|
||||
const image = await response.arrayBuffer();
|
||||
return nativeImage.createFromBuffer(Buffer.from(image));
|
||||
}
|
||||
|
||||
export async function tryGetNativeImageFromUrl(url: URL | string) {
|
||||
try {
|
||||
return await getNativeImageFromUrl(url);
|
||||
} catch (err) {}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as path from 'path';
|
||||
import { constants } from 'fs';
|
||||
import * as fs from 'fs/promises';
|
||||
import { Buffer } from 'buffer';
|
||||
import createDebug from 'debug';
|
||||
import { BrowserWindow, IpcMainInvokeEvent, session, shell, WebContents } from './electron.js';
|
||||
import { app, BrowserWindow, IpcMainInvokeEvent, session, ShareMenu, shell, WebContents } from './electron.js';
|
||||
import fetch from 'node-fetch';
|
||||
import ZncApi from '../../api/znc.js';
|
||||
import { dev } from '../../util/product.js';
|
||||
import { WebService } from '../../api/znc-types.js';
|
||||
@@ -149,6 +153,51 @@ export class WebServiceIpc {
|
||||
const data: NativeShareRequest = JSON.parse(json);
|
||||
|
||||
debug('invokeNativeShare', webservice.name, nsoAccount.user.name, data);
|
||||
|
||||
const texts: string[] = [];
|
||||
if (data.text) texts.push(data.text);
|
||||
if (data.hashtags) texts.push(data.hashtags.map(t => '#' + t).join(' '));
|
||||
|
||||
const imagepath = await this.downloadShareImage(data);
|
||||
|
||||
const menu = new ShareMenu({
|
||||
texts,
|
||||
filePaths: [imagepath],
|
||||
});
|
||||
|
||||
menu.popup({window: BrowserWindow.fromWebContents(event.sender)!});
|
||||
}
|
||||
|
||||
private async downloadShareImage(req: NativeShareRequest) {
|
||||
const dir = app.getPath('downloads');
|
||||
const basename = path.basename(new URL(req.image_url).pathname);
|
||||
const extname = path.extname(basename);
|
||||
let filename;
|
||||
let i = 0;
|
||||
|
||||
do {
|
||||
i++;
|
||||
|
||||
filename = i === 1 ? basename : basename.substr(0, basename.length - extname.length) + ' ' + i + extname;
|
||||
} while (await this.pathExists(path.join(dir, filename)));
|
||||
|
||||
debug('Downloading image %s to %s as %s', req.image_url, dir, filename);
|
||||
|
||||
const response = await fetch(req.image_url);
|
||||
const image = await response.arrayBuffer();
|
||||
await fs.writeFile(path.join(dir, filename), Buffer.from(image));
|
||||
|
||||
return path.join(dir, filename);
|
||||
}
|
||||
|
||||
private async pathExists(path: string) {
|
||||
try {
|
||||
await fs.access(path, constants.F_OK);
|
||||
return true;
|
||||
} catch (err) {
|
||||
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async invokeNativeShareUrl(event: IpcMainInvokeEvent, json: string): Promise<void> {
|
||||
@@ -157,6 +206,13 @@ export class WebServiceIpc {
|
||||
const data: NativeShareUrlRequest = JSON.parse(json);
|
||||
|
||||
debug('invokeNativeShareUrl', webservice.name, nsoAccount.user.name, data);
|
||||
|
||||
const menu = new ShareMenu({
|
||||
texts: [data.text],
|
||||
urls: [data.url],
|
||||
});
|
||||
|
||||
menu.popup({window: BrowserWindow.fromWebContents(event.sender)!});
|
||||
}
|
||||
|
||||
async requestGameWebToken(event: IpcMainInvokeEvent): Promise<string> {
|
||||
|
||||
@@ -99,7 +99,7 @@ export async function dumpProfileImage(
|
||||
|
||||
debug('Fetching profile image');
|
||||
const image_response = await fetch(share.url);
|
||||
const image = await image_response.buffer();
|
||||
const image = await image_response.arrayBuffer();
|
||||
|
||||
debug('Writing profile image data %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify({
|
||||
@@ -109,7 +109,7 @@ export async function dumpProfileImage(
|
||||
}, null, 4) + '\n', 'utf-8');
|
||||
|
||||
debug('Writing profile image %s', image_filename);
|
||||
await fs.writeFile(image_file, image);
|
||||
await fs.writeFile(image_file, Buffer.from(image));
|
||||
|
||||
await fs.writeFile(latest_file, JSON.stringify({timestamp, etag, etag_data}, null, 4) + '\n', 'utf-8');
|
||||
}
|
||||
@@ -136,13 +136,13 @@ export async function dumpChallenges(
|
||||
|
||||
debug('Fetching challenge image for %s', challenge.key);
|
||||
const image_response = await fetch(share.url);
|
||||
const image = await image_response.buffer();
|
||||
const image = await image_response.arrayBuffer();
|
||||
|
||||
debug('Writing challenge image data %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify({share}, null, 4) + '\n', 'utf-8');
|
||||
|
||||
debug('Writing challenge image %s', filename);
|
||||
await fs.writeFile(image_file, image);
|
||||
await fs.writeFile(image_file, Buffer.from(image));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function dumpResults(
|
||||
|
||||
debug('Fetching battle results summary image');
|
||||
const image_response = await fetch(share.url);
|
||||
const image = await image_response.buffer();
|
||||
const image = await image_response.arrayBuffer();
|
||||
|
||||
debug('Writing battle results summary image data %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify({
|
||||
@@ -54,7 +54,7 @@ export async function dumpResults(
|
||||
}, null, 4) + '\n', 'utf-8');
|
||||
|
||||
debug('Writing battle results summary image %s', filename);
|
||||
await fs.writeFile(image_file, image);
|
||||
await fs.writeFile(image_file, Buffer.from(image));
|
||||
}
|
||||
|
||||
const skipped = [];
|
||||
@@ -103,7 +103,7 @@ export async function dumpResults(
|
||||
|
||||
debug('Fetching battle results image');
|
||||
const image_response = await fetch(share.url);
|
||||
const image = await image_response.buffer();
|
||||
const image = await image_response.arrayBuffer();
|
||||
|
||||
debug('Writing battle results image data %s', filename);
|
||||
await fs.writeFile(file, JSON.stringify({
|
||||
@@ -111,7 +111,7 @@ export async function dumpResults(
|
||||
}, null, 4) + '\n', 'utf-8');
|
||||
|
||||
debug('Writing battle results image %s', filename);
|
||||
await fs.writeFile(image_file, image);
|
||||
await fs.writeFile(image_file, Buffer.from(image));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user