Show detailed photo upload status
All checks were successful
CI / Lint (push) Successful in 31s
CI / Test (push) Successful in 56s

This commit is contained in:
2026-04-24 11:28:23 +01:00
parent cb4b9c6b40
commit b4a70233cf
3 changed files with 52 additions and 7 deletions

View File

@@ -22,6 +22,7 @@ export default class PlacePhotoUploadItem extends Component {
@tracked thumbnailUrl = ''; @tracked thumbnailUrl = '';
@tracked blurhash = ''; @tracked blurhash = '';
@tracked error = ''; @tracked error = '';
@tracked statusText = '';
constructor() { constructor() {
super(...arguments); super(...arguments);
@@ -47,6 +48,7 @@ export default class PlacePhotoUploadItem extends Component {
uploadTask = task(async (file) => { uploadTask = task(async (file) => {
this.error = ''; this.error = '';
this.statusText = 'Processing';
try { try {
// 1. Process main image and generate blurhash in worker // 1. Process main image and generate blurhash in worker
const mainData = await this.imageProcessor.process( const mainData = await this.imageProcessor.process(
@@ -71,18 +73,34 @@ export default class PlacePhotoUploadItem extends Component {
let mainResult, thumbResult; let mainResult, thumbResult;
const isMobileDevice = isMobile(); 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) { if (isMobileDevice) {
// Mobile: sequential uploads to preserve bandwidth and memory // Mobile: sequential uploads to preserve bandwidth and memory
mainResult = await this.blossom.upload(mainData.blob, { mainResult = await this.blossom.upload(mainData.blob, {
sequential: true, sequential: true,
onProgress: mainProgress,
}); });
thumbResult = await this.blossom.upload(thumbData.blob, { thumbResult = await this.blossom.upload(thumbData.blob, {
sequential: true, sequential: true,
onProgress: thumbProgress,
}); });
} else { } else {
// Desktop: concurrent uploads // Desktop: concurrent uploads
const mainUploadPromise = this.blossom.upload(mainData.blob); const mainUploadPromise = this.blossom.upload(mainData.blob, {
const thumbUploadPromise = this.blossom.upload(thumbData.blob); onProgress: mainProgress,
});
const thumbUploadPromise = this.blossom.upload(thumbData.blob, {
onProgress: thumbProgress,
});
[mainResult, thumbResult] = await Promise.all([ [mainResult, thumbResult] = await Promise.all([
mainUploadPromise, mainUploadPromise,
@@ -127,6 +145,9 @@ export default class PlacePhotoUploadItem extends Component {
@color="white" @color="white"
class="spin-animation" class="spin-animation"
/> />
{{#if this.statusText}}
<span class="upload-status-text">{{this.statusText}}</span>
{{/if}}
</div> </div>
{{/if}} {{/if}}

View File

@@ -60,10 +60,13 @@ export default class BlossomService extends Service {
return `Nostr ${base64url}`; return `Nostr ${base64url}`;
} }
async _uploadToServer(file, hash, serverUrl) { async _uploadToServer(file, hash, serverUrl, onProgress) {
const uploadUrl = getBlossomUrl(serverUrl, 'upload'); const uploadUrl = getBlossomUrl(serverUrl, 'upload');
if (onProgress) onProgress('signing');
const authHeader = await this._getAuthHeader('upload', hash, serverUrl); const authHeader = await this._getAuthHeader('upload', hash, serverUrl);
if (onProgress) onProgress('uploading');
// eslint-disable-next-line warp-drive/no-external-request-patterns // eslint-disable-next-line warp-drive/no-external-request-patterns
const response = await fetch(uploadUrl, { const response = await fetch(uploadUrl, {
method: 'PUT', method: 'PUT',
@@ -109,14 +112,20 @@ export default class BlossomService extends Service {
if (options.sequential) { if (options.sequential) {
// Sequential upload logic // Sequential upload logic
mainResult = await this._uploadToServer(file, payloadHash, mainServer); mainResult = await this._uploadToServer(
file,
payloadHash,
mainServer,
options.onProgress
);
for (const serverUrl of fallbackServers) { for (const serverUrl of fallbackServers) {
try { try {
const result = await this._uploadToServer( const result = await this._uploadToServer(
file, file,
payloadHash, payloadHash,
serverUrl serverUrl,
options.onProgress
); );
fallbackUrls.push(result.url); fallbackUrls.push(result.url);
} catch (error) { } catch (error) {
@@ -125,9 +134,14 @@ export default class BlossomService extends Service {
} }
} else { } else {
// Concurrent upload logic // Concurrent upload logic
const mainPromise = this._uploadToServer(file, payloadHash, mainServer); const mainPromise = this._uploadToServer(
file,
payloadHash,
mainServer,
options.onProgress
);
const fallbackPromises = fallbackServers.map((serverUrl) => const fallbackPromises = fallbackServers.map((serverUrl) =>
this._uploadToServer(file, payloadHash, serverUrl) this._uploadToServer(file, payloadHash, serverUrl, options.onProgress)
); );
// Main server MUST succeed // Main server MUST succeed

View File

@@ -285,10 +285,20 @@ body {
inset: 0; inset: 0;
background: rgb(0 0 0 / 60%); background: rgb(0 0 0 / 60%);
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
justify-content: 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 { .photo-upload-item .error-overlay {
background: rgb(224 108 117 / 80%); background: rgb(224 108 117 / 80%);
cursor: pointer; cursor: pointer;