Compare commits

..

4 Commits

Author SHA1 Message Date
e38f540c79 1.20.2
All checks were successful
CI / Lint (push) Successful in 32s
CI / Test (push) Successful in 58s
2026-04-24 12:28:08 +01:00
73ad5b4eb1 Disable closing modal during photo upload 2026-04-24 12:24:19 +01:00
b4a70233cf Show detailed photo upload status
All checks were successful
CI / Lint (push) Successful in 31s
CI / Test (push) Successful in 56s
2026-04-24 11:56:37 +01:00
cb4b9c6b40 Render portrait thumbnails as squares on mobile
All checks were successful
CI / Lint (push) Successful in 32s
CI / Test (push) Successful in 56s
A bit too small otherwise
2026-04-24 11:01:58 +01:00
14 changed files with 108 additions and 26 deletions

View File

@@ -11,6 +11,7 @@ export default class Modal extends Component {
@action
close() {
if (this.args.disableClose) return;
if (this.args.onClose) {
this.args.onClose();
}
@@ -31,10 +32,11 @@ export default class Modal extends Component {
>
<button
type="button"
class="close-modal-btn btn-text"
class="close-modal-btn btn-text {{if @disableClose 'disabled'}}"
disabled={{@disableClose}}
{{on "click" this.close}}
>
<Icon @name="x" @size={{24}} />
<Icon @name="x" @size={{24}} @color="currentColor" />
</button>
{{yield}}
</div>

View File

@@ -27,6 +27,12 @@ export default class PlaceDetails extends Component {
@tracked isPhotoUploadModalOpen = false;
@tracked isNostrConnectModalOpen = false;
@tracked newlyUploadedPhotoId = null;
@tracked isPhotoUploadActive = false;
@action
handleUploadStateChange(isActive) {
this.isPhotoUploadActive = isActive;
}
@action
openPhotoUploadModal(e) {
@@ -42,6 +48,7 @@ export default class PlaceDetails extends Component {
@action
closePhotoUploadModal(eventId) {
if (this.isPhotoUploadActive) return;
this.isPhotoUploadModalOpen = false;
if (typeof eventId === 'string') {
this.newlyUploadedPhotoId = eventId;
@@ -585,10 +592,14 @@ export default class PlaceDetails extends Component {
</div>
{{#if this.isPhotoUploadModalOpen}}
<Modal @onClose={{this.closePhotoUploadModal}}>
<Modal
@onClose={{this.closePhotoUploadModal}}
@disableClose={{this.isPhotoUploadActive}}
>
<PlacePhotoUpload
@place={{this.saveablePlace}}
@onClose={{this.closePhotoUploadModal}}
@onUploadStateChange={{this.handleUploadStateChange}}
/>
</Modal>
{{/if}}

View File

@@ -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}}
<span class="upload-status-text">{{this.statusText}}</span>
{{/if}}
</div>
{{/if}}

View File

@@ -77,6 +77,9 @@ export default class PlacePhotoUpload extends Component {
}
this.file = file;
this.uploadedPhoto = null;
if (this.args.onUploadStateChange) {
this.args.onUploadStateChange(true);
}
}
@action
@@ -91,6 +94,9 @@ export default class PlacePhotoUpload extends Component {
}
this.file = null;
this.uploadedPhoto = null;
if (this.args.onUploadStateChange) {
this.args.onUploadStateChange(false);
}
}
deletePhotoTask = task(async (photoData) => {
@@ -191,6 +197,10 @@ export default class PlacePhotoUpload extends Component {
this.file = null;
this.uploadedPhoto = null;
if (this.args.onUploadStateChange) {
this.args.onUploadStateChange(false);
}
if (this.args.onClose) {
this.args.onClose(event.id);
}

View File

@@ -116,7 +116,8 @@ export default class PlacePhotosCarousel extends Component {
{{#each this.photos as |photo|}}
{{! template-lint-disable no-inline-styles }}
<div
class="carousel-slide"
class="carousel-slide
{{if photo.isLandscape 'landscape' 'portrait'}}"
style={{photo.style}}
data-event-id={{photo.eventId}}
>

View File

@@ -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

View File

@@ -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;
@@ -1006,10 +1016,17 @@ abbr[title] {
flex: 0 0 auto;
height: 100px;
width: auto;
aspect-ratio: var(--slide-ratio, 16 / 9);
scroll-snap-align: none;
}
.carousel-slide.landscape {
aspect-ratio: var(--slide-ratio, 16 / 9);
}
.carousel-slide.portrait {
aspect-ratio: 1 / 1;
}
.carousel-placeholder {
display: block;
background-color: var(--hover-bg);
@@ -1798,6 +1815,12 @@ button.create-place {
top: 1rem;
right: 1rem;
cursor: pointer;
color: #898989;
}
.close-modal-btn.disabled {
color: #ccc;
cursor: not-allowed;
}
.place-photo-upload h2 {

View File

@@ -1,6 +1,6 @@
{
"name": "marco",
"version": "1.20.1",
"version": "1.20.2",
"private": true,
"description": "Unhosted maps app",
"repository": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -39,8 +39,8 @@
<meta name="msapplication-TileColor" content="#F6E9A6">
<meta name="msapplication-TileImage" content="/icons/icon-144.png">
<script type="module" crossorigin src="/assets/main-CGySSjv6.js"></script>
<link rel="stylesheet" crossorigin href="/assets/main-BA3LWr76.css">
<script type="module" crossorigin src="/assets/main-D7SnY8s9.js"></script>
<link rel="stylesheet" crossorigin href="/assets/main-9lfQuoS5.css">
</head>
<body>
</body>