diff --git a/public/logo.png b/public/logo.png
index d2ccc01..021a88f 100644
Binary files a/public/logo.png and b/public/logo.png differ
diff --git a/src/assets/airplane-animation.png b/src/assets/airplane-animation.png
new file mode 100644
index 0000000..7fa84fe
Binary files /dev/null and b/src/assets/airplane-animation.png differ
diff --git a/src/lib/timeline/detail/PhotoEditor.svelte b/src/lib/timeline/detail/PhotoEditor.svelte
index 24a4d6d..a6a58b5 100644
--- a/src/lib/timeline/detail/PhotoEditor.svelte
+++ b/src/lib/timeline/detail/PhotoEditor.svelte
@@ -7,18 +7,23 @@
let fileInput;
let uploading = $state(false);
+ let uploadError = $state('');
function remove(index) {
onchange(photos.filter((_, i) => i !== index));
+ uploadError = '';
}
async function addFiles(e) {
const files = Array.from(e.currentTarget.files ?? []);
if (!files.length) return;
uploading = true;
+ uploadError = '';
try {
const urls = await Promise.all(files.map(uploadPhoto));
onchange([...photos, ...urls]);
+ } catch (err) {
+ uploadError = err?.message ?? 'Upload failed. Check Firebase Storage rules.';
} finally {
uploading = false;
e.currentTarget.value = '';
@@ -68,6 +73,10 @@
{/if}
+
+ {#if uploadError}
+
{uploadError}
+ {/if}
diff --git a/src/lib/world-map/JourneyView.svelte b/src/lib/world-map/JourneyView.svelte
index 56ba5ef..3e9222e 100644
--- a/src/lib/world-map/JourneyView.svelte
+++ b/src/lib/world-map/JourneyView.svelte
@@ -5,7 +5,7 @@
import worldData from 'world-atlas/countries-50m.json';
import { get } from 'svelte/store';
import { journals } from '../stores/journalStore.js';
- import airplaneImg from '../../assets/airplane.png';
+ import airplaneImg from '../../assets/airplane-animation.png';
let { onclose, onprogress, mode = 'map', onmodechange } = $props();
diff --git a/src/lib/world-map/WorldMap.svelte b/src/lib/world-map/WorldMap.svelte
index 855954b..a38664a 100644
--- a/src/lib/world-map/WorldMap.svelte
+++ b/src/lib/world-map/WorldMap.svelte
@@ -6,6 +6,7 @@
import { getSelected, setTotalCount, getFlashing } from '../layout/selection.svelte.js';
import { getUserProfile } from '../auth/userStore.svelte.js';
import { nameToId } from '../shared/countries.js';
+ import homeIconUrl from '../../assets/home.png';
import crayonCursorUrl from '../../assets/logo-cursor.png';
let { onCountryClick = (_name) => {} } = $props();
@@ -103,6 +104,28 @@
$effect(updateAllFills);
+ function placeHomeMarker() {
+ if (!_g || !_pathFn || !_countries) return;
+ _g.selectAll('.home-marker').remove();
+ const name = getUserProfile()?.homeCountry;
+ if (!name) return;
+ const found = _countries.find(f => f.properties.name === name);
+ if (!found) return;
+ const [cx, cy] = _pathFn.centroid(found);
+ if (isNaN(cx) || isNaN(cy)) return;
+ const SIZE = 14;
+ _g.append('image')
+ .attr('class', 'home-marker')
+ .attr('href', homeIconUrl)
+ .attr('x', cx - SIZE / 2)
+ .attr('y', cy - SIZE / 2)
+ .attr('width', SIZE)
+ .attr('height', SIZE)
+ .style('pointer-events', 'none');
+ }
+
+ $effect(placeHomeMarker);
+
$effect(() => {
const flashSet = getFlashing();
const paths = _paths; // reactive read so effect re-runs when _paths is set
@@ -166,7 +189,7 @@
})
.on('mousemove', (event) => {
const [x, y] = d3.pointer(event, frameEl);
- tooltip.style('left', (x + 10) + 'px').style('top', (y - 28) + 'px');
+ tooltip.style('left', (x + 22) + 'px').style('top', (y - 28) + 'px');
})
.on('mouseleave', (event, d) => {
const s = getSelected();
@@ -207,6 +230,7 @@
}
renderMicrostates();
+ placeHomeMarker();
const zoom = d3.zoom()
.scaleExtent([1, 32])
@@ -230,8 +254,9 @@
fitProjection(projection, width, height);
const countryPaths = _g.selectAll('path');
countryPaths.attr('d', path);
- updateFill(countryPaths);
+ updateAllFills();
renderMicrostates();
+ placeHomeMarker();
}
});