Move sticker creator API to chat service

This commit is contained in:
Fedor Indutny 2024-05-15 15:26:37 -07:00 committed by GitHub
parent a1e090d1f1
commit 31cbb89b0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 124 additions and 587 deletions

View file

@ -3148,15 +3148,15 @@
}, },
"icu:AuthArtCreator--dialog--message": { "icu:AuthArtCreator--dialog--message": {
"messageformat": "Would you like to open Signal Sticker Pack Creator?", "messageformat": "Would you like to open Signal Sticker Pack Creator?",
"description": "A body of the dialog that is presented when user tries to open Signal Sticker Pack Creator from a link" "description": "(Deleted 2024/05/15) A body of the dialog that is presented when user tries to open Signal Sticker Pack Creator from a link"
}, },
"icu:AuthArtCreator--dialog--confirm": { "icu:AuthArtCreator--dialog--confirm": {
"messageformat": "Confirm", "messageformat": "Confirm",
"description": "A buttle title for confirming Signal Sticker Pack Creator dialog" "description": "(Deleted 2024/05/15) A buttle title for confirming Signal Sticker Pack Creator dialog"
}, },
"icu:AuthArtCreator--dialog--dismiss": { "icu:AuthArtCreator--dialog--dismiss": {
"messageformat": "Dismiss", "messageformat": "Dismiss",
"description": "A buttle title for dismissing Signal Sticker Pack Creator dialog" "description": "(Deleted 2024/05/15) A buttle title for dismissing Signal Sticker Pack Creator dialog"
}, },
"icu:ArtCreator--Authentication--error": { "icu:ArtCreator--Authentication--error": {
"messageformat": "Please set up Signal on your phone and desktop to use the Sticker Pack Creator", "messageformat": "Please set up Signal on your phone and desktop to use the Sticker Pack Creator",

View file

@ -120,8 +120,6 @@ import { parseSignalRoute } from '../ts/util/signalRoutes';
import * as dns from '../ts/util/dns'; import * as dns from '../ts/util/dns';
import { ZoomFactorService } from '../ts/services/ZoomFactorService'; import { ZoomFactorService } from '../ts/services/ZoomFactorService';
const STICKER_CREATOR_PARTITION = 'sticker-creator';
const animationSettings = systemPreferences.getAnimationSettings(); const animationSettings = systemPreferences.getAnimationSettings();
if (OS.isMacOS()) { if (OS.isMacOS()) {
@ -1000,21 +998,24 @@ ipc.handle('database-ready', async () => {
getLogger().info('sending `database-ready`'); getLogger().info('sending `database-ready`');
}); });
ipc.handle('get-art-creator-auth', () => { ipc.handle(
'art-creator:uploadStickerPack',
(_event: Electron.Event, data: unknown) => {
const { promise, resolve } = explodePromise<unknown>(); const { promise, resolve } = explodePromise<unknown>();
strictAssert(mainWindow, 'Main window did not exist'); strictAssert(mainWindow, 'Main window did not exist');
mainWindow.webContents.send('open-art-creator'); mainWindow.webContents.send('art-creator:uploadStickerPack', data);
ipc.handleOnce('open-art-creator', (_event, { username, password }) => { ipc.once('art-creator:uploadStickerPack:done', (_doneEvent, response) => {
resolve({ resolve(response);
baseUrl: config.get<string>('artCreatorUrl'),
username,
password,
});
}); });
return promise; return promise;
}
);
ipc.on('art-creator:onUploadProgress', () => {
stickerCreatorWindow?.webContents.send('art-creator:onUploadProgress');
}); });
ipc.on('show-window', () => { ipc.on('show-window', () => {
@ -1735,31 +1736,22 @@ app.on('ready', async () => {
realpath(app.getAppPath()), realpath(app.getAppPath()),
]); ]);
const webSession = session.fromPartition(STICKER_CREATOR_PARTITION); updateDefaultSession(session.defaultSession);
for (const s of [session.defaultSession, webSession]) {
updateDefaultSession(s);
if (getEnvironment() !== Environment.Test) { if (getEnvironment() !== Environment.Test) {
installFileHandler({ installFileHandler({
session: s, session: session.defaultSession,
userDataPath, userDataPath,
installPath, installPath,
isWindows: OS.isWindows(), isWindows: OS.isWindows(),
}); });
} }
}
installWebHandler({ installWebHandler({
enableHttp: Boolean(process.env.SIGNAL_ENABLE_HTTP), enableHttp: Boolean(process.env.SIGNAL_ENABLE_HTTP),
session: session.defaultSession, session: session.defaultSession,
}); });
installWebHandler({
enableHttp: true,
session: webSession,
});
logger = await logging.initialize(getMainWindow); logger = await logging.initialize(getMainWindow);
// Write buffered information into newly created logger. // Write buffered information into newly created logger.
@ -2454,7 +2446,6 @@ ipc.on('get-config', async event => {
storageUrl: config.get<string>('storageUrl'), storageUrl: config.get<string>('storageUrl'),
updatesUrl: config.get<string>('updatesUrl'), updatesUrl: config.get<string>('updatesUrl'),
resourcesUrl: config.get<string>('resourcesUrl'), resourcesUrl: config.get<string>('resourcesUrl'),
artCreatorUrl: config.get<string>('artCreatorUrl'),
cdnUrl0: config.get<string>('cdn.0'), cdnUrl0: config.get<string>('cdn.0'),
cdnUrl2: config.get<string>('cdn.2'), cdnUrl2: config.get<string>('cdn.2'),
cdnUrl3: config.get<string>('cdn.3'), cdnUrl3: config.get<string>('cdn.3'),
@ -2605,11 +2596,6 @@ function handleSignalRoute(route: ParsedSignalRoute) {
packId: route.args.packId, packId: route.args.packId,
packKey: Buffer.from(route.args.packKey, 'hex').toString('base64'), packKey: Buffer.from(route.args.packKey, 'hex').toString('base64'),
}); });
} else if (route.key === 'artAuth') {
mainWindow.webContents.send('authorize-art-creator', {
token: route.args.token,
pubKeyBase64: route.args.pubKey,
});
} else if (route.key === 'groupInvites') { } else if (route.key === 'groupInvites') {
mainWindow.webContents.send('show-group-via-link', { mainWindow.webContents.send('show-group-via-link', {
value: route.args.inviteCode, value: route.args.inviteCode,
@ -2895,7 +2881,6 @@ async function showStickerCreatorWindow() {
show: false, show: false,
webPreferences: { webPreferences: {
...defaultWebPrefs, ...defaultWebPrefs,
partition: STICKER_CREATOR_PARTITION,
nodeIntegration: false, nodeIntegration: false,
nodeIntegrationInWorker: false, nodeIntegrationInWorker: false,
sandbox: true, sandbox: true,

View file

@ -11,7 +11,6 @@
"contentProxyUrl": "http://contentproxy.signal.org:443", "contentProxyUrl": "http://contentproxy.signal.org:443",
"updatesUrl": "https://updates2.signal.org/desktop", "updatesUrl": "https://updates2.signal.org/desktop",
"resourcesUrl": "https://updates2.signal.org", "resourcesUrl": "https://updates2.signal.org",
"artCreatorUrl": "https://create.staging.signal.art",
"updatesPublicKey": "05fd7dd3de7149dc0a127909fee7de0f7620ddd0de061b37a2c303e37de802a401", "updatesPublicKey": "05fd7dd3de7149dc0a127909fee7de0f7620ddd0de061b37a2c303e37de802a401",
"sfuUrl": "https://sfu.staging.voip.signal.org/", "sfuUrl": "https://sfu.staging.voip.signal.org/",
"challengeUrl": "https://signalcaptchas.org/staging/challenge/generate.html", "challengeUrl": "https://signalcaptchas.org/staging/challenge/generate.html",

View file

@ -8,7 +8,6 @@
"2": "https://cdn2.signal.org", "2": "https://cdn2.signal.org",
"3": "https://cdn3.signal.org" "3": "https://cdn3.signal.org"
}, },
"artCreatorUrl": "https://create.signal.art",
"sfuUrl": "https://sfu.voip.signal.org/", "sfuUrl": "https://sfu.voip.signal.org/",
"challengeUrl": "https://signalcaptchas.org/challenge/generate.html", "challengeUrl": "https://signalcaptchas.org/challenge/generate.html",
"registrationChallengeUrl": "https://signalcaptchas.org/registration/generate.html", "registrationChallengeUrl": "https://signalcaptchas.org/registration/generate.html",

View file

@ -34,7 +34,6 @@
"focus-trap-react": "10.1.1", "focus-trap-react": "10.1.1",
"memoizee": "0.4.15", "memoizee": "0.4.15",
"npm-run-all": "4.1.5", "npm-run-all": "4.1.5",
"p-limit": "4.0.0",
"protobufjs": "7.2.5", "protobufjs": "7.2.5",
"protobufjs-cli": "1.1.1", "protobufjs-cli": "1.1.1",
"qrcode-generator": "1.4.4", "qrcode-generator": "1.4.4",

View file

@ -6,7 +6,6 @@
// //
export type Credentials = Readonly<{ export type Credentials = Readonly<{
baseUrl?: string;
username: string; username: string;
password: string; password: string;
}>; }>;

View file

@ -1,21 +1,17 @@
// Copyright 2022 Signal Messenger, LLC // Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import b64 from 'base64-js';
import pLimit from 'p-limit';
import type { infer as zInfer } from 'zod';
import z from 'zod';
import { type ArtType } from '../constants'; import { type ArtType } from '../constants';
import { type Credentials } from '../types.d'; import { type EncryptResult } from './crypto';
import { type EncryptResult, getRandomString } from './crypto';
const MAX_PARALLEL_UPLOADS = 10;
declare global { declare global {
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
interface Window { interface Window {
getCredentials(): Promise<Credentials>; uploadStickerPack(
manifest: Uint8Array,
stickers: ReadonlyArray<Uint8Array>,
onProgres?: () => void
): Promise<string>;
installStickerPack(packId: string, key: string): void; installStickerPack(packId: string, key: string): void;
} }
} }
@ -25,172 +21,33 @@ export type UploadOptions = Readonly<{
onProgress?: () => void; onProgress?: () => void;
}>; }>;
export type UploadResult = Readonly<{
key: string;
packId: string;
}>;
async function getSchemas() {
const UploadAttributes = z.object({
acl: z.string(),
algorithm: z.string(),
credential: z.string(),
date: z.string(),
id: z.number(),
key: z.string(),
policy: z.string(),
signature: z.string(),
securityToken: z.string(),
});
const FormResponse = z.object({
packId: z.string(),
manifest: UploadAttributes,
art: z.array(UploadAttributes),
uploadURL: z.string(),
});
return { UploadAttributes, FormResponse };
}
type Schemas = Awaited<ReturnType<typeof getSchemas>>;
const encoder = new TextEncoder();
export class APIError extends Error { export class APIError extends Error {
constructor(message: string, public readonly errorMessageI18nKey: string) { constructor(message: string, public readonly errorMessageI18nKey: string) {
super(message); super(message);
} }
} }
export type UploadResult = Readonly<{
key: string;
packId: string;
}>;
export async function upload( export async function upload(
encryptResult: EncryptResult, encryptResult: EncryptResult,
{ artType, onProgress }: UploadOptions { onProgress }: UploadOptions
): Promise<UploadResult> { ): Promise<UploadResult> {
const { const { encryptedManifest, encryptedImages, key } = encryptResult;
encryptedManifest: manifest,
encryptedImages: images,
key,
} = encryptResult;
const credentials = await window.getCredentials(); const packId = await window.uploadStickerPack(
const { baseUrl = '' } = credentials; encryptedManifest,
encryptedImages,
const auth = b64.fromByteArray( onProgress
encoder.encode([credentials.username, credentials.password].join(':'))
); );
const res = await fetch( window.installStickerPack(packId, key);
`${baseUrl}/api/form?artType=${artType}&artCount=${images.length}`,
{
headers: {
authorization: `Basic ${auth}`,
},
}
);
if (res.status === 401 || res.status === 403) {
throw new APIError(
'Credentials expired',
'StickerCreator--Toasts--expired-credenitals'
);
}
if (!res.ok) {
throw new Error(`Request failed, status: ${res.status}`);
}
const { FormResponse } = await getSchemas();
const form = FormResponse.parse(await res.json());
if (form.art.length !== images.length) {
throw new Error('Invalid form data, image count mismatch');
}
const limiter = pLimit(MAX_PARALLEL_UPLOADS);
await Promise.all([
limiter(async () => {
await uploadAttachment(form.uploadURL, form.manifest, manifest);
onProgress?.();
}),
...images.map((image, index) =>
limiter(async () => {
await uploadAttachment(form.uploadURL, form.art[index], image);
onProgress?.();
})
),
]);
window.installStickerPack(form.packId, key);
return { return {
key, key,
packId: form.packId, packId,
}; };
} }
async function uploadAttachment(
uploadURL: string,
{
key,
credential,
acl,
algorithm,
date,
policy,
signature,
securityToken,
}: zInfer<Schemas['UploadAttributes']>,
encryptedData: Uint8Array
): Promise<void> {
// Note: when using the boundary string in the POST body, it needs to be
// prefixed by an extra --, and the final boundary string at the end gets a
// -- prefix and a -- suffix.
const boundaryString = getRandomString().replace(/=/g, '');
const CRLF = '\r\n';
const getSection = (name: string, value: string) =>
[
`--${boundaryString}`,
`Content-Disposition: form-data; name="${name}"${CRLF}`,
value,
].join(CRLF);
const start = [
getSection('key', key),
getSection('x-amz-credential', credential),
getSection('acl', acl),
getSection('x-amz-algorithm', algorithm),
getSection('x-amz-date', date),
getSection('policy', policy),
getSection('x-amz-signature', signature),
getSection('x-amz-security-token', securityToken),
getSection('Content-Type', 'application/octet-stream'),
`--${boundaryString}`,
'Content-Disposition: form-data; name="file"',
`Content-Type: application/octet-stream${CRLF}${CRLF}`,
].join(CRLF);
const end = `${CRLF}--${boundaryString}--${CRLF}`;
const startBuffer = encoder.encode(start);
const endBuffer = encoder.encode(end);
const contentLength =
startBuffer.length + encryptedData.length + endBuffer.length;
const body = new Uint8Array(contentLength);
body.set(startBuffer, 0);
body.set(encryptedData, startBuffer.length);
body.set(endBuffer, startBuffer.length + encryptedData.length);
const res = await fetch(uploadURL, {
method: 'POST',
headers: {
'content-length': contentLength.toString(),
'content-type': `multipart/form-data; boundary=${boundaryString}`,
},
body,
});
if (!res.ok) {
throw new Error('Failed to upload attachment');
}
}

View file

@ -3108,13 +3108,6 @@ optionator@^0.9.1:
type-check "^0.4.0" type-check "^0.4.0"
word-wrap "^1.2.3" word-wrap "^1.2.3"
p-limit@4.0.0, p-limit@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644"
integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==
dependencies:
yocto-queue "^1.0.0"
p-limit@^3.0.2: p-limit@^3.0.2:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
@ -3122,6 +3115,13 @@ p-limit@^3.0.2:
dependencies: dependencies:
yocto-queue "^0.1.0" yocto-queue "^0.1.0"
p-limit@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644"
integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==
dependencies:
yocto-queue "^1.0.0"
p-locate@^5.0.0: p-locate@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"

View file

@ -3,7 +3,6 @@
import React from 'react'; import React from 'react';
import type { import type {
AuthorizeArtCreatorDataType,
ContactModalStateType, ContactModalStateType,
DeleteMessagesPropsType, DeleteMessagesPropsType,
EditHistoryMessagesType, EditHistoryMessagesType,
@ -96,11 +95,6 @@ export type PropsType = {
// UsernameOnboarding // UsernameOnboarding
usernameOnboardingState: UsernameOnboardingState; usernameOnboardingState: UsernameOnboardingState;
renderUsernameOnboarding: () => JSX.Element; renderUsernameOnboarding: () => JSX.Element;
// AuthArtCreatorModal
authArtCreatorData?: AuthorizeArtCreatorDataType;
isAuthorizingArtCreator?: boolean;
cancelAuthorizeArtCreator: () => unknown;
confirmAuthorizeArtCreator: () => unknown;
}; };
export function GlobalModalContainer({ export function GlobalModalContainer({
@ -166,11 +160,6 @@ export function GlobalModalContainer({
// UsernameOnboarding // UsernameOnboarding
usernameOnboardingState, usernameOnboardingState,
renderUsernameOnboarding, renderUsernameOnboarding,
// AuthArtCreatorModal
authArtCreatorData,
isAuthorizingArtCreator,
cancelAuthorizeArtCreator,
confirmAuthorizeArtCreator,
}: PropsType): JSX.Element | null { }: PropsType): JSX.Element | null {
// We want the following dialogs to show in this order: // We want the following dialogs to show in this order:
// 1. Errors // 1. Errors
@ -289,28 +278,5 @@ export function GlobalModalContainer({
); );
} }
if (authArtCreatorData) {
return (
<ConfirmationDialog
dialogName="GlobalModalContainer.authArtCreator"
cancelText={i18n('icu:AuthArtCreator--dialog--dismiss')}
cancelButtonVariant={ButtonVariant.Secondary}
i18n={i18n}
isSpinning={isAuthorizingArtCreator}
onClose={cancelAuthorizeArtCreator}
actions={[
{
text: i18n('icu:AuthArtCreator--dialog--confirm'),
style: 'affirmative',
action: confirmAuthorizeArtCreator,
autoClose: false,
},
]}
>
{i18n('icu:AuthArtCreator--dialog--message')}
</ConfirmationDialog>
);
}
return null; return null;
} }

View file

@ -18,7 +18,6 @@ import type { RecipientsByConversation } from './stories';
import type { SafetyNumberChangeSource } from '../../components/SafetyNumberChangeDialog'; import type { SafetyNumberChangeSource } from '../../components/SafetyNumberChangeDialog';
import type { EditState as ProfileEditorEditState } from '../../components/ProfileEditor'; import type { EditState as ProfileEditorEditState } from '../../components/ProfileEditor';
import type { StateType as RootStateType } from '../reducer'; import type { StateType as RootStateType } from '../reducer';
import * as Errors from '../../types/errors';
import * as SingleServePromise from '../../services/singleServePromise'; import * as SingleServePromise from '../../services/singleServePromise';
import * as Stickers from '../../types/Stickers'; import * as Stickers from '../../types/Stickers';
import { UsernameOnboardingState } from '../../types/globalModals'; import { UsernameOnboardingState } from '../../types/globalModals';
@ -28,18 +27,13 @@ import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { longRunningTaskWrapper } from '../../util/longRunningTaskWrapper'; import { longRunningTaskWrapper } from '../../util/longRunningTaskWrapper';
import { useBoundActions } from '../../hooks/useBoundActions'; import { useBoundActions } from '../../hooks/useBoundActions';
import { isGroupV1 } from '../../util/whatTypeOfConversation'; import { isGroupV1 } from '../../util/whatTypeOfConversation';
import { authorizeArtCreator } from '../../textsecure/authorizeArtCreator';
import type { AuthorizeArtCreatorOptionsType } from '../../textsecure/authorizeArtCreator';
import { getGroupMigrationMembers } from '../../groups'; import { getGroupMigrationMembers } from '../../groups';
import { ToastType } from '../../types/Toast';
import { import {
MESSAGE_CHANGED, MESSAGE_CHANGED,
MESSAGE_DELETED, MESSAGE_DELETED,
MESSAGE_EXPIRED, MESSAGE_EXPIRED,
actions as conversationsActions, actions as conversationsActions,
} from './conversations'; } from './conversations';
import { SHOW_TOAST } from './toast';
import type { ShowToastActionType } from './toast';
import { isDownloaded } from '../../types/Attachment'; import { isDownloaded } from '../../types/Attachment';
import type { ButtonVariant } from '../../components/Button'; import type { ButtonVariant } from '../../components/Button';
import type { MessageRequestState } from '../../components/conversation/MessageRequestActionsConfirmation'; import type { MessageRequestState } from '../../components/conversation/MessageRequestActionsConfirmation';
@ -73,8 +67,6 @@ export type SafetyNumberChangedBlockingDataType = ReadonlyDeep<{
promiseUuid: SingleServePromise.SingleServePromiseIdString; promiseUuid: SingleServePromise.SingleServePromiseIdString;
source?: SafetyNumberChangeSource; source?: SafetyNumberChangeSource;
}>; }>;
export type AuthorizeArtCreatorDataType =
ReadonlyDeep<AuthorizeArtCreatorOptionsType>;
type MigrateToGV2PropsType = ReadonlyDeep<{ type MigrateToGV2PropsType = ReadonlyDeep<{
areWeInvited: boolean; areWeInvited: boolean;
@ -87,7 +79,6 @@ type MigrateToGV2PropsType = ReadonlyDeep<{
export type GlobalModalsStateType = ReadonlyDeep<{ export type GlobalModalsStateType = ReadonlyDeep<{
addUserToAnotherGroupModalContactId?: string; addUserToAnotherGroupModalContactId?: string;
aboutContactModalContactId?: string; aboutContactModalContactId?: string;
authArtCreatorData?: AuthorizeArtCreatorDataType;
contactModalState?: ContactModalStateType; contactModalState?: ContactModalStateType;
deleteMessagesProps?: DeleteMessagesPropsType; deleteMessagesProps?: DeleteMessagesPropsType;
editHistoryMessages?: EditHistoryMessagesType; editHistoryMessages?: EditHistoryMessagesType;
@ -100,7 +91,6 @@ export type GlobalModalsStateType = ReadonlyDeep<{
forwardMessagesProps?: ForwardMessagesPropsType; forwardMessagesProps?: ForwardMessagesPropsType;
gv2MigrationProps?: MigrateToGV2PropsType; gv2MigrationProps?: MigrateToGV2PropsType;
hasConfirmationModal: boolean; hasConfirmationModal: boolean;
isAuthorizingArtCreator?: boolean;
isProfileEditorVisible: boolean; isProfileEditorVisible: boolean;
isShortcutGuideModalVisible: boolean; isShortcutGuideModalVisible: boolean;
isSignalConnectionsVisible: boolean; isSignalConnectionsVisible: boolean;
@ -157,13 +147,7 @@ const TOGGLE_MESSAGE_REQUEST_ACTIONS_CONFIRMATION =
'globalModals/TOGGLE_MESSAGE_REQUEST_ACTIONS_CONFIRMATION'; 'globalModals/TOGGLE_MESSAGE_REQUEST_ACTIONS_CONFIRMATION';
const CLOSE_SHORTCUT_GUIDE_MODAL = 'globalModals/CLOSE_SHORTCUT_GUIDE_MODAL'; const CLOSE_SHORTCUT_GUIDE_MODAL = 'globalModals/CLOSE_SHORTCUT_GUIDE_MODAL';
const SHOW_SHORTCUT_GUIDE_MODAL = 'globalModals/SHOW_SHORTCUT_GUIDE_MODAL'; const SHOW_SHORTCUT_GUIDE_MODAL = 'globalModals/SHOW_SHORTCUT_GUIDE_MODAL';
const SHOW_AUTH_ART_CREATOR = 'globalModals/SHOW_AUTH_ART_CREATOR';
const TOGGLE_CONFIRMATION_MODAL = 'globalModals/TOGGLE_CONFIRMATION_MODAL'; const TOGGLE_CONFIRMATION_MODAL = 'globalModals/TOGGLE_CONFIRMATION_MODAL';
const CANCEL_AUTH_ART_CREATOR = 'globalModals/CANCEL_AUTH_ART_CREATOR';
const CONFIRM_AUTH_ART_CREATOR_PENDING =
'globalModals/CONFIRM_AUTH_ART_CREATOR_PENDING';
const CONFIRM_AUTH_ART_CREATOR_FULFILLED =
'globalModals/CONFIRM_AUTH_ART_CREATOR_FULFILLED';
const SHOW_EDIT_HISTORY_MODAL = 'globalModals/SHOW_EDIT_HISTORY_MODAL'; const SHOW_EDIT_HISTORY_MODAL = 'globalModals/SHOW_EDIT_HISTORY_MODAL';
const CLOSE_EDIT_HISTORY_MODAL = 'globalModals/CLOSE_EDIT_HISTORY_MODAL'; const CLOSE_EDIT_HISTORY_MODAL = 'globalModals/CLOSE_EDIT_HISTORY_MODAL';
const TOGGLE_USERNAME_ONBOARDING = 'globalModals/TOGGLE_USERNAME_ONBOARDING'; const TOGGLE_USERNAME_ONBOARDING = 'globalModals/TOGGLE_USERNAME_ONBOARDING';
@ -332,23 +316,6 @@ type ShowShortcutGuideModalActionType = ReadonlyDeep<{
type: typeof SHOW_SHORTCUT_GUIDE_MODAL; type: typeof SHOW_SHORTCUT_GUIDE_MODAL;
}>; }>;
export type ShowAuthArtCreatorActionType = ReadonlyDeep<{
type: typeof SHOW_AUTH_ART_CREATOR;
payload: AuthorizeArtCreatorDataType;
}>;
type CancelAuthArtCreatorActionType = ReadonlyDeep<{
type: typeof CANCEL_AUTH_ART_CREATOR;
}>;
type ConfirmAuthArtCreatorPendingActionType = ReadonlyDeep<{
type: typeof CONFIRM_AUTH_ART_CREATOR_PENDING;
}>;
type ConfirmAuthArtCreatorFulfilledActionType = ReadonlyDeep<{
type: typeof CONFIRM_AUTH_ART_CREATOR_FULFILLED;
}>;
type ShowEditHistoryModalActionType = ReadonlyDeep<{ type ShowEditHistoryModalActionType = ReadonlyDeep<{
type: typeof SHOW_EDIT_HISTORY_MODAL; type: typeof SHOW_EDIT_HISTORY_MODAL;
payload: { payload: {
@ -361,14 +328,11 @@ type CloseEditHistoryModalActionType = ReadonlyDeep<{
}>; }>;
export type GlobalModalsActionType = ReadonlyDeep< export type GlobalModalsActionType = ReadonlyDeep<
| CancelAuthArtCreatorActionType
| CloseEditHistoryModalActionType | CloseEditHistoryModalActionType
| CloseErrorModalActionType | CloseErrorModalActionType
| CloseGV2MigrationDialogActionType | CloseGV2MigrationDialogActionType
| CloseShortcutGuideModalActionType | CloseShortcutGuideModalActionType
| CloseStickerPackPreviewActionType | CloseStickerPackPreviewActionType
| ConfirmAuthArtCreatorFulfilledActionType
| ConfirmAuthArtCreatorPendingActionType
| HideContactModalActionType | HideContactModalActionType
| HideSendAnywayDialogActiontype | HideSendAnywayDialogActiontype
| HideStoriesSettingsActionType | HideStoriesSettingsActionType
@ -377,7 +341,6 @@ export type GlobalModalsActionType = ReadonlyDeep<
| MessageChangedActionType | MessageChangedActionType
| MessageDeletedActionType | MessageDeletedActionType
| MessageExpiredActionType | MessageExpiredActionType
| ShowAuthArtCreatorActionType
| ShowContactModalActionType | ShowContactModalActionType
| ShowEditHistoryModalActionType | ShowEditHistoryModalActionType
| ShowErrorModalActionType | ShowErrorModalActionType
@ -406,19 +369,16 @@ export type GlobalModalsActionType = ReadonlyDeep<
// Action Creators // Action Creators
export const actions = { export const actions = {
cancelAuthorizeArtCreator,
closeEditHistoryModal, closeEditHistoryModal,
closeErrorModal, closeErrorModal,
closeGV2MigrationDialog, closeGV2MigrationDialog,
closeShortcutGuideModal, closeShortcutGuideModal,
closeStickerPackPreview, closeStickerPackPreview,
confirmAuthorizeArtCreator,
hideBlockingSafetyNumberChangeDialog, hideBlockingSafetyNumberChangeDialog,
hideContactModal, hideContactModal,
hideStoriesSettings, hideStoriesSettings,
hideUserNotFoundModal, hideUserNotFoundModal,
hideWhatsNewModal, hideWhatsNewModal,
showAuthorizeArtCreator,
showBlockingSafetyNumberChangeDialog, showBlockingSafetyNumberChangeDialog,
showContactModal, showContactModal,
showEditHistoryModal, showEditHistoryModal,
@ -790,25 +750,6 @@ function showShortcutGuideModal(): ShowShortcutGuideModalActionType {
}; };
} }
function cancelAuthorizeArtCreator(): ThunkAction<
void,
RootStateType,
unknown,
CancelAuthArtCreatorActionType
> {
return async (dispatch, getState) => {
const data = getState().globalModals.authArtCreatorData;
if (!data) {
return;
}
dispatch({
type: CANCEL_AUTH_ART_CREATOR,
});
};
}
function copyOverMessageAttributesIntoEditHistory( function copyOverMessageAttributesIntoEditHistory(
messageAttributes: ReadonlyDeep<MessageAttributesType> messageAttributes: ReadonlyDeep<MessageAttributesType>
): EditHistoryMessagesType | undefined { ): EditHistoryMessagesType | undefined {
@ -860,54 +801,6 @@ function closeEditHistoryModal(): CloseEditHistoryModalActionType {
}; };
} }
export function showAuthorizeArtCreator(
data: AuthorizeArtCreatorDataType
): ShowAuthArtCreatorActionType {
return {
type: SHOW_AUTH_ART_CREATOR,
payload: data,
};
}
export function confirmAuthorizeArtCreator(): ThunkAction<
void,
RootStateType,
unknown,
| ConfirmAuthArtCreatorPendingActionType
| ConfirmAuthArtCreatorFulfilledActionType
| CancelAuthArtCreatorActionType
| ShowToastActionType
> {
return async (dispatch, getState) => {
const data = getState().globalModals.authArtCreatorData;
if (!data) {
dispatch({ type: CANCEL_AUTH_ART_CREATOR });
return;
}
dispatch({
type: CONFIRM_AUTH_ART_CREATOR_PENDING,
});
try {
await authorizeArtCreator(data);
} catch (err) {
log.error('authorizeArtCreator failed', Errors.toLogFormat(err));
dispatch({
type: SHOW_TOAST,
payload: {
toastType: ToastType.Error,
},
});
}
dispatch({
type: CONFIRM_AUTH_ART_CREATOR_FULFILLED,
});
};
}
function copyOverMessageAttributesIntoForwardMessages( function copyOverMessageAttributesIntoForwardMessages(
messagesProps: ReadonlyArray<ForwardMessagePropsType>, messagesProps: ReadonlyArray<ForwardMessagePropsType>,
attributes: ReadonlyDeep<MessageAttributesType> attributes: ReadonlyDeep<MessageAttributesType>
@ -1168,36 +1061,6 @@ export function reducer(
}; };
} }
if (action.type === CANCEL_AUTH_ART_CREATOR) {
return {
...state,
authArtCreatorData: undefined,
};
}
if (action.type === SHOW_AUTH_ART_CREATOR) {
return {
...state,
isAuthorizingArtCreator: false,
authArtCreatorData: action.payload,
};
}
if (action.type === CONFIRM_AUTH_ART_CREATOR_PENDING) {
return {
...state,
isAuthorizingArtCreator: true,
};
}
if (action.type === CONFIRM_AUTH_ART_CREATOR_FULFILLED) {
return {
...state,
isAuthorizingArtCreator: false,
authArtCreatorData: undefined,
};
}
if (action.type === SHOW_EDIT_HISTORY_MODAL) { if (action.type === SHOW_EDIT_HISTORY_MODAL) {
return { return {
...state, ...state,

View file

@ -90,7 +90,6 @@ export const SmartGlobalModalContainer = memo(
const { const {
aboutContactModalContactId, aboutContactModalContactId,
addUserToAnotherGroupModalContactId, addUserToAnotherGroupModalContactId,
authArtCreatorData,
contactModalState, contactModalState,
deleteMessagesProps, deleteMessagesProps,
editHistoryMessages, editHistoryMessages,
@ -99,7 +98,6 @@ export const SmartGlobalModalContainer = memo(
forwardMessagesProps, forwardMessagesProps,
messageRequestActionsConfirmationProps, messageRequestActionsConfirmationProps,
notePreviewModalProps, notePreviewModalProps,
isAuthorizingArtCreator,
isProfileEditorVisible, isProfileEditorVisible,
isShortcutGuideModalVisible, isShortcutGuideModalVisible,
isSignalConnectionsVisible, isSignalConnectionsVisible,
@ -113,9 +111,7 @@ export const SmartGlobalModalContainer = memo(
} = useSelector(getGlobalModalsState); } = useSelector(getGlobalModalsState);
const { const {
cancelAuthorizeArtCreator,
closeErrorModal, closeErrorModal,
confirmAuthorizeArtCreator,
hideUserNotFoundModal, hideUserNotFoundModal,
hideWhatsNewModal, hideWhatsNewModal,
toggleSignalConnectionsModal, toggleSignalConnectionsModal,
@ -218,10 +214,6 @@ export const SmartGlobalModalContainer = memo(
toggleSignalConnectionsModal={toggleSignalConnectionsModal} toggleSignalConnectionsModal={toggleSignalConnectionsModal}
userNotFoundModalState={userNotFoundModalState} userNotFoundModalState={userNotFoundModalState}
usernameOnboardingState={usernameOnboardingState} usernameOnboardingState={usernameOnboardingState}
isAuthorizingArtCreator={isAuthorizingArtCreator}
authArtCreatorData={authArtCreatorData}
cancelAuthorizeArtCreator={cancelAuthorizeArtCreator}
confirmAuthorizeArtCreator={confirmAuthorizeArtCreator}
/> />
); );
} }

View file

@ -142,16 +142,6 @@ describe('signalRoutes', () => {
check(`sgnl://signal.link/call#key=${foo}`, result); check(`sgnl://signal.link/call#key=${foo}`, result);
}); });
it('artAuth', () => {
const result: ParsedSignalRoute = {
key: 'artAuth',
args: { token: foo, pubKey: foo },
};
const check = createCheck({ hasWebUrl: false });
check(`sgnl://art-auth/?token=${foo}&pub_key=${foo}`, result);
check(`sgnl://art-auth?token=${foo}&pub_key=${foo}`, result);
});
it('artAddStickers', () => { it('artAddStickers', () => {
const result: ParsedSignalRoute = { const result: ParsedSignalRoute = {
key: 'artAddStickers', key: 'artAddStickers',

View file

@ -52,7 +52,6 @@ export const AUTHENTICATED_CHANNEL_NAME = 'authenticated';
export type SocketManagerOptions = Readonly<{ export type SocketManagerOptions = Readonly<{
url: string; url: string;
artCreatorUrl: string;
certificateAuthority: string; certificateAuthority: string;
version: string; version: string;
proxyUrl?: string; proxyUrl?: string;

View file

@ -14,7 +14,6 @@ import PQueue from 'p-queue';
import { v4 as getGuid } from 'uuid'; import { v4 as getGuid } from 'uuid';
import { z } from 'zod'; import { z } from 'zod';
import type { Readable } from 'stream'; import type { Readable } from 'stream';
import type { connection as WebSocket } from 'websocket';
import { Net } from '@signalapp/libsignal-client'; import { Net } from '@signalapp/libsignal-client';
import { assertDev, strictAssert } from '../util/assert'; import { assertDev, strictAssert } from '../util/assert';
@ -554,7 +553,6 @@ const URL_CALLS = {
getOnboardingStoryManifest: getOnboardingStoryManifest:
'dynamic/desktop/stories/onboarding/manifest.json', 'dynamic/desktop/stories/onboarding/manifest.json',
getStickerPackUpload: 'v1/sticker/pack/form', getStickerPackUpload: 'v1/sticker/pack/form',
getArtAuth: 'v1/art/auth',
getBackupCredentials: 'v1/archives/auth', getBackupCredentials: 'v1/archives/auth',
getBackupCDNCredentials: 'v1/archives/auth/read', getBackupCDNCredentials: 'v1/archives/auth/read',
getBackupUploadForm: 'v1/archives/upload/form', getBackupUploadForm: 'v1/archives/upload/form',
@ -647,7 +645,6 @@ type InitializeOptionsType = {
storageUrl: string; storageUrl: string;
updatesUrl: string; updatesUrl: string;
resourcesUrl: string; resourcesUrl: string;
artCreatorUrl: string;
cdnUrlObject: { cdnUrlObject: {
readonly '0': string; readonly '0': string;
readonly [propName: string]: string; readonly [propName: string]: string;
@ -975,13 +972,6 @@ export type ReportMessageOptionsType = Readonly<{
token?: string; token?: string;
}>; }>;
const artAuthZod = z.object({
username: z.string(),
password: z.string(),
});
export type ArtAuthType = z.infer<typeof artAuthZod>;
const attachmentV3Response = z.object({ const attachmentV3Response = z.object({
cdn: z.literal(2).or(z.literal(3)), cdn: z.literal(2).or(z.literal(3)),
key: z.string(), key: z.string(),
@ -1149,6 +1139,23 @@ export type GetBackupInfoResponseType = z.infer<
typeof getBackupInfoResponseSchema typeof getBackupInfoResponseSchema
>; >;
const StickerPackUploadAttributesSchema = z.object({
acl: z.string(),
algorithm: z.string(),
credential: z.string(),
date: z.string(),
id: z.number(),
key: z.string(),
policy: z.string(),
signature: z.string(),
});
const StickerPackUploadFormSchema = z.object({
packId: z.string(),
manifest: StickerPackUploadAttributesSchema,
stickers: z.array(StickerPackUploadAttributesSchema),
});
export type WebAPIType = { export type WebAPIType = {
startRegistration(): unknown; startRegistration(): unknown;
finishRegistration(baton: unknown): void; finishRegistration(baton: unknown): void;
@ -1166,7 +1173,6 @@ export type WebAPIType = {
version: string, version: string,
imageFiles: Array<string> imageFiles: Array<string>
) => Promise<Array<Uint8Array>>; ) => Promise<Array<Uint8Array>>;
getArtAuth: () => Promise<ArtAuthType>;
getAttachmentFromBackupTier: (args: { getAttachmentFromBackupTier: (args: {
mediaId: string; mediaId: string;
backupDir: string; backupDir: string;
@ -1237,7 +1243,6 @@ export type WebAPIType = {
getProvisioningResource: ( getProvisioningResource: (
handler: IRequestHandler handler: IRequestHandler
) => Promise<IWebSocketResource>; ) => Promise<IWebSocketResource>;
getArtProvisioningSocket: (token: string) => Promise<WebSocket>;
getSenderCertificate: ( getSenderCertificate: (
withUuid?: boolean withUuid?: boolean
) => Promise<GetSenderCertificateResultType>; ) => Promise<GetSenderCertificateResultType>;
@ -1280,7 +1285,7 @@ export type WebAPIType = {
) => Promise<UploadAvatarHeadersType | undefined>; ) => Promise<UploadAvatarHeadersType | undefined>;
putStickers: ( putStickers: (
encryptedManifest: Uint8Array, encryptedManifest: Uint8Array,
encryptedStickers: Array<Uint8Array>, encryptedStickers: ReadonlyArray<Uint8Array>,
onProgress?: () => void onProgress?: () => void
) => Promise<string>; ) => Promise<string>;
reserveUsername: ( reserveUsername: (
@ -1471,7 +1476,6 @@ export function initialize({
storageUrl, storageUrl,
updatesUrl, updatesUrl,
resourcesUrl, resourcesUrl,
artCreatorUrl,
directoryConfig, directoryConfig,
cdnUrlObject, cdnUrlObject,
certificateAuthority, certificateAuthority,
@ -1493,9 +1497,6 @@ export function initialize({
if (!isString(resourcesUrl)) { if (!isString(resourcesUrl)) {
throw new Error('WebAPI.initialize: Invalid updatesUrl (general)'); throw new Error('WebAPI.initialize: Invalid updatesUrl (general)');
} }
if (!isString(artCreatorUrl)) {
throw new Error('WebAPI.initialize: Invalid artCreatorUrl');
}
if (!isObject(cdnUrlObject)) { if (!isObject(cdnUrlObject)) {
throw new Error('WebAPI.initialize: Invalid cdnUrlObject'); throw new Error('WebAPI.initialize: Invalid cdnUrlObject');
} }
@ -1558,7 +1559,6 @@ export function initialize({
const socketManager = new SocketManager(libsignalNet, { const socketManager = new SocketManager(libsignalNet, {
url, url,
artCreatorUrl,
certificateAuthority, certificateAuthority,
version, version,
proxyUrl, proxyUrl,
@ -1667,8 +1667,6 @@ export function initialize({
fetchLinkPreviewMetadata, fetchLinkPreviewMetadata,
finishRegistration, finishRegistration,
getAccountForUsername, getAccountForUsername,
getArtAuth,
getArtProvisioningSocket,
getAttachment, getAttachment,
getAttachmentFromBackupTier, getAttachmentFromBackupTier,
getAvatar, getAvatar,
@ -3309,20 +3307,19 @@ export function initialize({
async function putStickers( async function putStickers(
encryptedManifest: Uint8Array, encryptedManifest: Uint8Array,
encryptedStickers: Array<Uint8Array>, encryptedStickers: ReadonlyArray<Uint8Array>,
onProgress?: () => void onProgress?: () => void
) { ) {
// Get manifest and sticker upload parameters // Get manifest and sticker upload parameters
const { packId, manifest, stickers } = (await _ajax({ const formJson = await _ajax({
call: 'getStickerPackUpload', call: 'getStickerPackUpload',
responseType: 'json', responseType: 'json',
httpType: 'GET', httpType: 'GET',
urlParameters: `/${encryptedStickers.length}`, urlParameters: `/${encryptedStickers.length}`,
})) as { });
packId: string;
manifest: ServerV2AttachmentType; const { packId, manifest, stickers } =
stickers: ReadonlyArray<ServerV2AttachmentType>; StickerPackUploadFormSchema.parse(formJson);
};
// Upload manifest // Upload manifest
const manifestParams = makePutParams(manifest, encryptedManifest); const manifestParams = makePutParams(manifest, encryptedManifest);
@ -4008,15 +4005,6 @@ export function initialize({
return socketManager.getProvisioningResource(handler); return socketManager.getProvisioningResource(handler);
} }
function getArtProvisioningSocket(token: string): Promise<WebSocket> {
return socketManager.connectExternalSocket({
url: `${artCreatorUrl}/api/socket?token=${token}`,
extraHeaders: {
origin: artCreatorUrl,
},
});
}
async function cdsLookup({ async function cdsLookup({
e164s, e164s,
acisAndAccessKeys = [], acisAndAccessKeys = [],
@ -4030,19 +4018,5 @@ export function initialize({
useLibsignal, useLibsignal,
}); });
} }
//
// Art
//
async function getArtAuth(): Promise<ArtAuthType> {
const response = await _ajax({
call: 'getArtAuth',
httpType: 'GET',
responseType: 'json',
});
return artAuthZod.parse(response);
}
} }
} }

View file

@ -1,59 +0,0 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { promisify } from 'util';
import { SignalService as Proto } from '../protobuf';
import { calculateAgreement, generateKeyPair } from '../Curve';
import { encryptAttachment, deriveSecrets } from '../Crypto';
import * as Bytes from '../Bytes';
const PROVISIONING_INFO = 'Art Service Provisioning Message';
export type AuthorizeArtCreatorOptionsType = Readonly<{
token: string;
pubKeyBase64: string;
}>;
export async function authorizeArtCreator({
token,
pubKeyBase64: theirPubKeyBase64,
}: AuthorizeArtCreatorOptionsType): Promise<void> {
const { server } = window.textsecure;
if (!server) {
throw new Error('Server not ready');
}
const auth = await server.getArtAuth();
const ourKeys = generateKeyPair();
const theirPubKey = Bytes.fromBase64(theirPubKeyBase64);
const secret = calculateAgreement(theirPubKey, ourKeys.privKey);
const [aesKey, macKey] = deriveSecrets(
secret,
new Uint8Array(64),
Bytes.fromString(PROVISIONING_INFO)
);
const keys = Bytes.concatenate([aesKey, macKey]);
const { ciphertext } = encryptAttachment({
plaintext: Proto.ArtProvisioningMessage.encode({
...auth,
}).finish(),
keys,
});
const envelope = Proto.ArtProvisioningEnvelope.encode({
publicKey: ourKeys.pubKey,
ciphertext,
}).finish();
const socket = await server.getArtProvisioningSocket(token);
try {
await promisify(socket.sendBytes).call(socket, Buffer.from(envelope));
} finally {
socket.close(1000, 'goodbye');
}
}

View file

@ -30,7 +30,6 @@ export type DirectoryConfigType = z.infer<typeof directoryConfigSchema>;
export const rendererConfigSchema = z.object({ export const rendererConfigSchema = z.object({
appInstance: configOptionalStringSchema, appInstance: configOptionalStringSchema,
appStartInitialSpellcheckSetting: z.boolean(), appStartInitialSpellcheckSetting: z.boolean(),
artCreatorUrl: configRequiredStringSchema,
buildCreation: z.number(), buildCreation: z.number(),
buildExpiration: z.number(), buildExpiration: z.number(),
cdnUrl0: configRequiredStringSchema, cdnUrl0: configRequiredStringSchema,

View file

@ -16,7 +16,6 @@ import * as Errors from '../types/errors';
import * as Stickers from '../types/Stickers'; import * as Stickers from '../types/Stickers';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import type { AuthorizeArtCreatorDataType } from '../state/ducks/globalModals';
import { calling } from '../services/calling'; import { calling } from '../services/calling';
import { resolveUsernameByLinkBase64 } from '../services/username'; import { resolveUsernameByLinkBase64 } from '../services/username';
import { writeProfile } from '../services/writeProfile'; import { writeProfile } from '../services/writeProfile';
@ -96,7 +95,6 @@ export type IPCEventsValuesType = {
}; };
export type IPCEventsCallbacksType = { export type IPCEventsCallbacksType = {
openArtCreator(): Promise<void>;
getAvailableIODevices(): Promise<{ getAvailableIODevices(): Promise<{
availableCameras: Array< availableCameras: Array<
Pick<MediaDeviceInfo, 'deviceId' | 'groupId' | 'kind' | 'label'> Pick<MediaDeviceInfo, 'deviceId' | 'groupId' | 'kind' | 'label'>
@ -106,7 +104,6 @@ export type IPCEventsCallbacksType = {
}>; }>;
addCustomColor: (customColor: CustomColorType) => void; addCustomColor: (customColor: CustomColorType) => void;
addDarkOverlay: () => void; addDarkOverlay: () => void;
authorizeArtCreator: (data: AuthorizeArtCreatorDataType) => void;
deleteAllData: () => Promise<void>; deleteAllData: () => Promise<void>;
deleteAllMyStories: () => Promise<void>; deleteAllMyStories: () => Promise<void>;
editCustomColor: (colorId: string, customColor: CustomColorType) => void; editCustomColor: (colorId: string, customColor: CustomColorType) => void;
@ -141,6 +138,10 @@ export type IPCEventsCallbacksType = {
customColor?: { id: string; value: CustomColorType } customColor?: { id: string; value: CustomColorType }
) => void; ) => void;
getDefaultConversationColor: () => DefaultConversationColorType; getDefaultConversationColor: () => DefaultConversationColorType;
uploadStickerPack: (
manifest: Uint8Array,
stickers: ReadonlyArray<Uint8Array>
) => Promise<string>;
}; };
type ValuesWithGetters = Omit< type ValuesWithGetters = Omit<
@ -239,15 +240,6 @@ export function createIPCEvents(
}; };
return { return {
openArtCreator: async () => {
const auth = await window.textsecure.server?.getArtAuth();
if (!auth) {
return;
}
window.openArtCreator(auth);
},
getDeviceName: () => window.textsecure.storage.user.getDeviceName(), getDeviceName: () => window.textsecure.storage.user.getDeviceName(),
getPhoneNumber: () => { getPhoneNumber: () => {
try { try {
@ -530,14 +522,6 @@ export function createIPCEvents(
}); });
document.body.prepend(newOverlay); document.body.prepend(newOverlay);
}, },
authorizeArtCreator: (data: AuthorizeArtCreatorDataType) => {
// We can get these events even if the user has never linked this instance.
if (!Registration.everDone()) {
log.warn('authorizeArtCreator: Not registered, returning early');
return;
}
window.reduxActions.globalModals.showAuthorizeArtCreator(data);
},
removeDarkOverlay: () => { removeDarkOverlay: () => {
const elems = document.querySelectorAll('.dark-overlay'); const elems = document.querySelectorAll('.dark-overlay');
@ -717,6 +701,16 @@ export function createIPCEvents(
} }
}, },
uploadStickerPack: (
manifest: Uint8Array,
stickers: ReadonlyArray<Uint8Array>
): Promise<string> => {
strictAssert(window.textsecure.server, 'WebAPI must be available');
return window.textsecure.server.putStickers(manifest, stickers, () =>
ipcRenderer.send('art-creator:onUploadProgress')
);
},
...overrideEvents, ...overrideEvents,
}; };
} }

View file

@ -439,38 +439,6 @@ export const artAddStickersRoute = _route('artAddStickers', {
}, },
}); });
/**
* Art Service Authentication
* @example
* ```ts
* artAuthRoute.toAppUrl({
* token: "123",
* pubKey: "abc",
* })
* // URL { "sgnl://art-auth?token=123&pub_key=abc" }
*/
export const artAuthRoute = _route('artAuth', {
patterns: [_pattern('sgnl:', 'art-auth', '{/}?', { search: ':params' })],
schema: z.object({
token: paramSchema, // opaque
pubKey: paramSchema, // base64url
}),
parse(result) {
const params = new URLSearchParams(result.search.groups.params);
return {
token: params.get('token'),
pubKey: params.get('pub_key'),
};
},
toAppUrl(args) {
const params = new URLSearchParams({
token: args.token,
pub_key: args.pubKey,
});
return new URL(`sgnl://art-auth?${params.toString()}`);
},
});
/** /**
* Show a conversation * Show a conversation
* @example * @example
@ -594,7 +562,6 @@ const _allSignalRoutes = [
captchaRoute, captchaRoute,
linkCallRoute, linkCallRoute,
artAddStickersRoute, artAddStickersRoute,
artAuthRoute,
showConversationRoute, showConversationRoute,
startCallLobbyRoute, startCallLobbyRoute,
showWindowRoute, showWindowRoute,

View file

@ -341,24 +341,6 @@ ipc.on('show-group-via-link', (_event, info) => {
drop(window.Events.showGroupViaLink?.(info.value)); drop(window.Events.showGroupViaLink?.(info.value));
}); });
ipc.on('open-art-creator', () => {
drop(window.Events.openArtCreator());
});
window.openArtCreator = ({
username,
password,
}: {
username: string;
password: string;
}) => {
return ipc.invoke('open-art-creator', { username, password });
};
ipc.on('authorize-art-creator', (_event, info) => {
window.Events.authorizeArtCreator?.(info);
});
ipc.on('start-call-lobby', (_event, { conversationId }) => { ipc.on('start-call-lobby', (_event, { conversationId }) => {
window.IPC.showWindow(); window.IPC.showWindow();
window.reduxActions?.calling?.startCallingLobby({ window.reduxActions?.calling?.startCallingLobby({
@ -458,3 +440,18 @@ ipc.on('show-release-notes', () => {
showReleaseNotes(); showReleaseNotes();
} }
}); });
ipc.on(
'art-creator:uploadStickerPack',
async (
event,
{
manifest,
stickers,
}: { manifest: Uint8Array; stickers: ReadonlyArray<Uint8Array> }
) => {
const packId = await window.Events?.uploadStickerPack(manifest, stickers);
event.sender.send('art-creator:uploadStickerPack:done', packId);
}
);

View file

@ -27,7 +27,6 @@ window.WebAPI = window.textsecure.WebAPI.initialize({
storageUrl: config.storageUrl, storageUrl: config.storageUrl,
updatesUrl: config.updatesUrl, updatesUrl: config.updatesUrl,
resourcesUrl: config.resourcesUrl, resourcesUrl: config.resourcesUrl,
artCreatorUrl: config.artCreatorUrl,
directoryConfig: config.directoryConfig, directoryConfig: config.directoryConfig,
cdnUrlObject: { cdnUrlObject: {
0: config.cdnUrl0, 0: config.cdnUrl0,

View file

@ -3,8 +3,26 @@
import { contextBridge, ipcRenderer } from 'electron'; import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('getCredentials', async () => let onProgress: (() => void) | undefined;
ipcRenderer.invoke('get-art-creator-auth')
ipcRenderer.on('art-creator:onUploadProgress', () => {
onProgress?.();
});
contextBridge.exposeInMainWorld(
'uploadStickerPack',
async (
manifest: Uint8Array,
stickers: Readonly<Uint8Array>,
newOnProgress: (() => void) | undefined
): Promise<string> => {
onProgress = newOnProgress;
return ipcRenderer.invoke('art-creator:uploadStickerPack', {
manifest,
stickers,
});
}
); );
contextBridge.exposeInMainWorld( contextBridge.exposeInMainWorld(