Skip to content
Merged
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
82 changes: 66 additions & 16 deletions packages/core/src/api/device/DeviceUploadResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ import { DeviceModelToTypes, DeviceUploadResourceParams } from '../../types';
import { BaseMethod } from '../BaseMethod';
import { validateParams } from '../helpers/paramsValidator';
import { hexToBytes } from '../helpers/hexUtils';
import { createUiMessage, UI_REQUEST } from '../../events';
import { getDeviceType, getDeviceFirmwareVersion } from '../../utils';
import { PROTO } from '../../constants';

export default class DeviceUploadResource extends BaseMethod<ResourceUpload> {
paramsData = {
data: new Uint8Array(),
thumbnailData: new Uint8Array(),
blurData: new Uint8Array(),
};

private uploadProgress = {
totalBytes: 0,
uploadedBytes: 0,
currentFile: 'main' as 'main' | 'thumbnail' | 'blur',
};

getVersionRange() {
Expand Down Expand Up @@ -52,20 +58,28 @@ export default class DeviceUploadResource extends BaseMethod<ResourceUpload> {
{ name: 'suffix', type: 'string', required: true },
{ name: 'dataHex', type: 'string', required: true },
{ name: 'thumbnailDataHex', type: 'string', required: true },
{ name: 'blurDataHex', type: 'hexString', required: true },
{ name: 'resType', type: 'number', required: true },
{ name: 'nftMetaData', type: 'string' },
{ name: 'fileNameNoExt', type: 'string' },
]);

const { suffix, dataHex, thumbnailDataHex, resType, nftMetaData } = this
const { suffix, dataHex, thumbnailDataHex, blurDataHex, resType, nftMetaData } = this
.payload as DeviceUploadResourceParams;

// init params
this.paramsData = {
data: hexToBytes(dataHex),
thumbnailData: hexToBytes(thumbnailDataHex),
data: new Uint8Array(hexToBytes(dataHex)),
thumbnailData: new Uint8Array(hexToBytes(thumbnailDataHex)),
blurData: new Uint8Array(hexToBytes(blurDataHex)),
};

this.uploadProgress.totalBytes =
this.paramsData.data.byteLength +
this.paramsData.thumbnailData.byteLength +
this.paramsData.blurData.byteLength;
this.uploadProgress.uploadedBytes = 0;

const fileHash = bytesToHex(blake2s(this.payload.dataHex)).slice(0, 8);
const file_name_no_ext = isEmpty(this.payload.fileNameNoExt)
? `${resType === 0 ? 'wp' : 'nft'}-${fileHash}-${Math.floor(Date.now() / 1000)}`
Expand All @@ -75,48 +89,86 @@ export default class DeviceUploadResource extends BaseMethod<ResourceUpload> {
extension: suffix,
data_length: this.paramsData.data.byteLength,
zoom_data_length: this.paramsData.thumbnailData.byteLength,
blur_data_length: this.paramsData.blurData.byteLength,
res_type: resType,
nft_meta_data: nftMetaData,
file_name_no_ext,
};
}

private getDataChunk(sourceData: Uint8Array, offset: number, length: number): Uint8Array {
const endOffset = Math.min(offset + length, sourceData.byteLength);

return sourceData.subarray(offset, endOffset);
}

private updateProgress(chunkSize: number, requestType: string) {
this.uploadProgress.uploadedBytes += chunkSize;

if (requestType === 'ResourceRequest') {
this.uploadProgress.currentFile = 'main';
} else if (requestType === 'ZoomRequest') {
this.uploadProgress.currentFile = 'thumbnail';
} else {
this.uploadProgress.currentFile = 'blur';
}

const progress = Math.round(
(this.uploadProgress.uploadedBytes / this.uploadProgress.totalBytes) * 100
);

if (process.env.NODE_ENV === 'development') {
console.log(`Upload progress: ${progress}% (${this.uploadProgress.currentFile})`);
}
}

processResourceRequest = async (
res:
| TypedResponseMessage<'ResourceRequest'>
| TypedResponseMessage<'ZoomRequest'>
| TypedResponseMessage<'BlurRequest'>
| TypedResponseMessage<'Success'>
): Promise<Success> => {
if (res.type === 'Success') {
return res.message;
}

const { offset, data_length } = res.message;
const { data, thumbnailData } = this.paramsData;
const { data, thumbnailData, blurData } = this.paramsData;

if (offset === undefined) {
throw new Error('offset is undefined');
}

let payload: Uint8Array;
if (res.type === 'ResourceRequest') {
payload = new Uint8Array(data.slice(offset, Math.min(offset + data_length, data.byteLength)));
} else {
payload = new Uint8Array(
thumbnailData.slice(offset, Math.min(offset + data_length, thumbnailData.byteLength))
);
let sourceData: Uint8Array;

switch (res.type) {
case 'ResourceRequest':
sourceData = data;
break;
case 'BlurRequest':
sourceData = blurData;
break;
case 'ZoomRequest':
sourceData = thumbnailData;
break;
default:
throw new Error('Invalid request type');
}

const payload = this.getDataChunk(sourceData, offset, data_length);
const digest = blake2s(payload);

this.updateProgress(payload.byteLength, res.type);

const resourceAckParams = {
data_chunk: bytesToHex(payload),
hash: bytesToHex(digest),
};

const response = await this.device.commands.typedCall(
'ResourceAck',
['ResourceRequest', 'ZoomRequest', 'Success'],
['ResourceRequest', 'ZoomRequest', 'BlurRequest', 'Success'],
resourceAckParams
);
return this.processResourceRequest(response);
Expand All @@ -129,12 +181,10 @@ export default class DeviceUploadResource extends BaseMethod<ResourceUpload> {

const res = await this.device.commands.typedCall(
'ResourceUpload',
['ResourceRequest', 'ZoomRequest', 'Success'],
['ResourceRequest', 'ZoomRequest', 'BlurRequest', 'Success'],
this.params
);

this.postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_WINDOW));

return this.processResourceRequest(res);
}
}
18 changes: 18 additions & 0 deletions packages/core/src/data/messages/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -7531,6 +7531,10 @@
"file_name_no_ext": {
"type": "string",
"id": 6
},
"blur_data_length": {
"type": "uint32",
"id": 7
}
},
"nested": {
Expand All @@ -7555,6 +7559,19 @@
}
}
},
"BlurRequest": {
"fields": {
"offset": {
"type": "uint32",
"id": 1
},
"data_length": {
"rule": "required",
"type": "uint32",
"id": 2
}
}
},
"ResourceRequest": {
"fields": {
"offset": {
Expand Down Expand Up @@ -12366,6 +12383,7 @@
"MessageType_NFTWriteData": 10015,
"MessageType_ResourceUpload": 10018,
"MessageType_ZoomRequest": 10019,
"MessageType_BlurRequest": 10032,
"MessageType_ResourceRequest": 10020,
"MessageType_ResourceAck": 10021,
"MessageType_ResourceUpdate": 10022,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/types/api/deviceUploadResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type DeviceUploadResourceParams = {
suffix: string;
dataHex: string;
thumbnailDataHex: string;
blurDataHex: string;
resType: ResourceType;
nftMetaData: string;
fileNameNoExt?: string;
Expand Down
8 changes: 8 additions & 0 deletions packages/hd-transport/src/types/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2760,6 +2760,7 @@ export type ResourceUpload = {
nft_meta_data?: string;
zoom_data_length: number;
file_name_no_ext?: string;
blur_data_length?: number;
};

// ZoomRequest
Expand All @@ -2768,6 +2769,12 @@ export type ZoomRequest = {
data_length: number;
};

// BlurRequest
export type BlurRequest = {
offset?: number;
data_length: number;
};

// ResourceRequest
export type ResourceRequest = {
offset?: number;
Expand Down Expand Up @@ -4634,6 +4641,7 @@ export type MessageType = {
SEMessageSignature: SEMessageSignature;
ResourceUpload: ResourceUpload;
ZoomRequest: ZoomRequest;
BlurRequest: BlurRequest;
ResourceRequest: ResourceRequest;
ResourceAck: ResourceAck;
ResourceUpdate: ResourceUpdate;
Expand Down
2 changes: 1 addition & 1 deletion submodules/firmware