diff --git a/app/components/place-photo-upload-item.gjs b/app/components/place-photo-upload-item.gjs index 0194e1b..0e41c2a 100644 --- a/app/components/place-photo-upload-item.gjs +++ b/app/components/place-photo-upload-item.gjs @@ -22,6 +22,7 @@ export default class PlacePhotoUploadItem extends Component { @tracked thumbnailUrl = ''; @tracked blurhash = ''; @tracked error = ''; + @tracked statusText = ''; constructor() { super(...arguments); @@ -47,6 +48,7 @@ export default class PlacePhotoUploadItem extends Component { uploadTask = task(async (file) => { this.error = ''; + this.statusText = 'Processing'; try { // 1. Process main image and generate blurhash in worker const mainData = await this.imageProcessor.process( @@ -71,18 +73,34 @@ export default class PlacePhotoUploadItem extends Component { let mainResult, thumbResult; const isMobileDevice = isMobile(); + const mainProgress = (status) => { + if (status === 'signing') this.statusText = 'Signing photo upload'; + if (status === 'uploading') this.statusText = 'Uploading photo'; + }; + + const thumbProgress = (status) => { + if (status === 'signing') this.statusText = 'Signing thumbnail upload'; + if (status === 'uploading') this.statusText = 'Uploading thumbnail'; + }; + if (isMobileDevice) { // Mobile: sequential uploads to preserve bandwidth and memory mainResult = await this.blossom.upload(mainData.blob, { sequential: true, + onProgress: mainProgress, }); thumbResult = await this.blossom.upload(thumbData.blob, { sequential: true, + onProgress: thumbProgress, }); } else { // Desktop: concurrent uploads - const mainUploadPromise = this.blossom.upload(mainData.blob); - const thumbUploadPromise = this.blossom.upload(thumbData.blob); + const mainUploadPromise = this.blossom.upload(mainData.blob, { + onProgress: mainProgress, + }); + const thumbUploadPromise = this.blossom.upload(thumbData.blob, { + onProgress: thumbProgress, + }); [mainResult, thumbResult] = await Promise.all([ mainUploadPromise, @@ -127,6 +145,9 @@ export default class PlacePhotoUploadItem extends Component { @color="white" class="spin-animation" /> + {{#if this.statusText}} + {{this.statusText}} + {{/if}} {{/if}} diff --git a/app/services/blossom.js b/app/services/blossom.js index 4a189a0..4ac85be 100644 --- a/app/services/blossom.js +++ b/app/services/blossom.js @@ -60,10 +60,13 @@ export default class BlossomService extends Service { return `Nostr ${base64url}`; } - async _uploadToServer(file, hash, serverUrl) { + async _uploadToServer(file, hash, serverUrl, onProgress) { const uploadUrl = getBlossomUrl(serverUrl, 'upload'); + + if (onProgress) onProgress('signing'); const authHeader = await this._getAuthHeader('upload', hash, serverUrl); + if (onProgress) onProgress('uploading'); // eslint-disable-next-line warp-drive/no-external-request-patterns const response = await fetch(uploadUrl, { method: 'PUT', @@ -109,14 +112,20 @@ export default class BlossomService extends Service { if (options.sequential) { // Sequential upload logic - mainResult = await this._uploadToServer(file, payloadHash, mainServer); + mainResult = await this._uploadToServer( + file, + payloadHash, + mainServer, + options.onProgress + ); for (const serverUrl of fallbackServers) { try { const result = await this._uploadToServer( file, payloadHash, - serverUrl + serverUrl, + options.onProgress ); fallbackUrls.push(result.url); } catch (error) { @@ -125,9 +134,14 @@ export default class BlossomService extends Service { } } else { // Concurrent upload logic - const mainPromise = this._uploadToServer(file, payloadHash, mainServer); + const mainPromise = this._uploadToServer( + file, + payloadHash, + mainServer, + options.onProgress + ); const fallbackPromises = fallbackServers.map((serverUrl) => - this._uploadToServer(file, payloadHash, serverUrl) + this._uploadToServer(file, payloadHash, serverUrl, options.onProgress) ); // Main server MUST succeed diff --git a/app/styles/app.css b/app/styles/app.css index 705293e..64fa21d 100644 --- a/app/styles/app.css +++ b/app/styles/app.css @@ -285,10 +285,20 @@ body { inset: 0; background: rgb(0 0 0 / 60%); display: flex; + flex-direction: column; align-items: center; justify-content: center; } +.upload-status-text { + color: white; + margin-top: 1rem; + font-size: 0.9rem; + text-shadow: 0 1px 3px rgb(0 0 0 / 80%); + text-align: center; + padding: 0 1rem; +} + .photo-upload-item .error-overlay { background: rgb(224 108 117 / 80%); cursor: pointer;