viewimage page layout

This commit is contained in:
Chaebean Yang 2025-06-08 22:28:12 +09:00
parent 750a4da34a
commit 4594982834
3 changed files with 148 additions and 171 deletions

View File

@ -1,8 +1,5 @@
// Import the functions you need from the SDKs you need import { initializeApp, getApps, getApp } from 'firebase/app';
import { initializeApp } from "firebase/app"; import { getDatabase } from 'firebase/database';
import { ref, child, get, set, getDatabase, onValue, push } from 'firebase/database';
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration // Your web app's Firebase configuration
const firebaseConfig = { const firebaseConfig = {
@ -15,6 +12,8 @@ const firebaseConfig = {
appId: import.meta.env.VITE_FIREBASE_APP_ID appId: import.meta.env.VITE_FIREBASE_APP_ID
}; };
// Initialize Firebase // Initialize Firebase (with duplicate app prevention)
const app = initializeApp(firebaseConfig); const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();
// Initialize Realtime Database and get a reference to the service
export const db = getDatabase(app); export const db = getDatabase(app);

View File

@ -1,124 +0,0 @@
<script>
import '../../app.css';
import Nav from '$lib/components/Nav.svelte';
import Button from '$lib/components/Button.svelte';
import { page } from '$app/state';
import { onMount } from 'svelte';
import { ref, get } from 'firebase/database';
import { db } from '../../firebase';
let memoryId = '';
/**
* @type {{ location: any; startDate: any; endDate: any; images: any; } | null}
*/
let memory = null;
let tripId = '';
$: {
tripId = page.params.tripId;
memoryId = page.params.memoryId;
}
onMount(async () => {
if (tripId && memoryId) {
try {
const snapshot = await get(ref(db, `trips/${tripId}/memories/${memoryId}`));
if (snapshot.exists()) {
memory = snapshot.val();
} else {
console.error('No memory found');
}
} catch (error) {
console.error('Error fetching memory:', error);
}
}
});
let gradientColors = ['#e74c3c', '#f1c40f', '#2ecc71', '#3498db', '#9b59b6'];
$: gradientStyle = `
conic-gradient(
${gradientColors.map((c, i) => `${c} ${i * 72}deg ${(i + 1) * 72}deg`).join(',')}
)
`;
</script>
<main>
<Nav activeTab="MyMemory" darkMode={true}/>
<div class="content">
{#if memory}
<div class="header">
<h1>{memory.location}</h1>
<p>{memory.startDate} - {memory.endDate}</p>
</div>
<div class="wheel-container">
<div class="gradient-wheel" style="background-image: {gradientStyle};"></div>
</div>
<div class="image-list">
{#each memory.images as img}
<!-- svelte-ignore a11y_img_redundant_alt -->
<img src={typeof img === 'string' ? img : URL.createObjectURL(img)} alt="Memory image" />
{/each}
</div>
{:else}
<p class="empty">Loading memory...</p>
{/if}
</div>
</main>
<style>
main {
height: 100vh;
background-color: var(--black);
font-family: 'Inter', sans-serif;
display: flex;
flex-direction: column;
}
.content {
flex: 1;
padding: 2rem 1rem;
overflow-y: auto;
}
.header {
text-align: center;
color: var(--white);
margin-bottom: 2rem;
}
.gradient-wheel {
width: 300px;
height: 300px;
margin: 2rem auto;
border-radius: 50%;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
}
.wheel-container {
display: flex;
justify-content: center;
}
.image-list {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
margin-top: 2rem;
}
.image-list img {
width: 80%;
max-width: 500px;
border-radius: 12px;
}
.empty {
color: var(--gray-400);
text-align: center;
margin-top: 4rem;
}
</style>

View File

@ -8,13 +8,10 @@
import { db } from '../../../../firebase'; import { db } from '../../../../firebase';
let memoryId = ''; let memoryId = '';
/** let tripOptions = [];
* @type {{ location: any; startDate: any; endDate: any; images: any; } | null}
*/
let memory = null; let memory = null;
let tripId = ''; let tripId = '';
// Subscribe to page store to get URL parameters
$: { $: {
tripId = page.params.tripId; tripId = page.params.tripId;
memoryId = page.params.memoryId; memoryId = page.params.memoryId;
@ -23,24 +20,52 @@
onMount(async () => { onMount(async () => {
if (tripId && memoryId) { if (tripId && memoryId) {
try { try {
const snapshot = await get(ref(db, `trips/${tripId}/memories/${memoryId}`)); const memorySnap = await get(ref(db, `trips/${tripId}/memories/${memoryId}`));
if (snapshot.exists()) { const tripSnap = await get(ref(db, `trips/${tripId}`));
memory = snapshot.val();
if (memorySnap.exists() && tripSnap.exists()) {
memory = memorySnap.val();
const currentTrip = tripSnap.val();
const { lat, lng } = currentTrip.destination.location;
const allTripsSnap = await get(ref(db, 'trips'));
if (allTripsSnap.exists()) {
const allTrips = Object.entries(allTripsSnap.val());
tripOptions = allTrips
.filter(([id, trip]) => {
const loc = trip.destination?.location;
const hasMemories = trip.memories && Object.keys(trip.memories).length > 0;
const sameLocation =
loc && Math.abs(loc.lat - lat) < 0.0001 && Math.abs(loc.lng - lng) < 0.0001;
return sameLocation && hasMemories;
})
.map(([id, trip]) => {
const memories = Object.values(trip.memories);
const latest = memories.sort((a, b) => new Date(b.startDate) - new Date(a.startDate))[0];
return {
id,
label: `${latest.startDate} - ${latest.endDate}`
};
});
const currentIndex = tripOptions.findIndex(t => t.id === tripId);
if (currentIndex > 0) {
const [current] = tripOptions.splice(currentIndex, 1);
tripOptions.unshift(current);
}
}
} else { } else {
console.error('No memory found'); console.error('Trip or memory not found');
} }
} catch (error) { } catch (error) {
console.error('Error fetching memory:', error); console.error('Error fetching data:', error);
} }
} }
}); });
let gradientColors = ['#e74c3c', '#f1c40f', '#2ecc71', '#3498db', '#9b59b6']; let gradientColors = ['#e74c3c', '#f1c40f', '#2ecc71', '#3498db', '#9b59b6'];
$: gradientStyle = ` $: gradientStyle = `conic-gradient(${gradientColors.map((c, i) => `${c} ${i * 72}deg ${(i + 1) * 72}deg`).join(',')})`;
conic-gradient(
${gradientColors.map((c, i) => `${c} ${i * 72}deg ${(i + 1) * 72}deg`).join(',')}
)
`;
</script> </script>
<main> <main>
@ -50,23 +75,36 @@
{#if memory} {#if memory}
<div class="header"> <div class="header">
<h1>{memory.location}</h1> <h1>{memory.location}</h1>
<p>{memory.startDate} - {memory.endDate}</p> </div>
<div class="trip-switcher">
<div class="trip-switcher-inner">
{#each tripOptions as trip}
<button on:click={() => goto(`/memory/${trip.id}/${memoryId}`)}>
{trip.label}
</button>
{/each}
</div>
</div> </div>
<div class="wheel-container"> <div class="wheel-container">
<div class="gradient-wheel" style="background-image: {gradientStyle};"></div> <div class="gradient-wheel" style="background-image: {gradientStyle};"></div>
</div> </div>
<div class="image-list"> <div class="image-preview">
{#if memory.images && memory.images.length > 0}
<h2>Images</h2>
<div class="image-grid">
{#each memory.images as img} {#each memory.images as img}
<!-- svelte-ignore a11y_img_redundant_alt --> <img src={typeof img === 'string' ? img : URL.createObjectURL(img)} alt="memory image" />
<img src={typeof img === 'string' ? img : URL.createObjectURL(img)} alt="Memory image" />
{/each} {/each}
</div> </div>
{:else} {:else}
<p class="empty">Loading memory...</p> <p class="no-image">No images uploaded.</p>
{/if} {/if}
</div> </div>
{/if} <!-- ✅ 이 줄 빠지면 Svelte 에러 발생 -->
</div>
</main> </main>
<style> <style>
@ -80,14 +118,70 @@
.content { .content {
flex: 1; flex: 1;
padding: 2rem 1rem; padding: 0 1rem 2rem 1rem;
overflow-y: auto; position: relative;
display: flex;
flex-direction: column;
} }
.header { .header {
text-align: center; padding-top: 2rem;
padding-left: 1rem;
background-color: var(--black);
color: var(--white); color: var(--white);
margin-bottom: 2rem; }
.header h1 {
font-size: 2rem;
font-weight: 600;
margin: 0;
}
.trip-switcher {
width: 100%;
text-align: center;
overflow: hidden;
padding-top: 1.5rem;
}
.trip-switcher-inner {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
gap: 1rem;
padding: 0.5rem;
}
.trip-switcher-inner::-webkit-scrollbar {
width: 10px;
height: 4px;
}
.trip-switcher-inner::-webkit-scrollbar-thumb {
background: var(--white);
border-radius: 2px;
}
.trip-switcher-inner::-webkit-scrollbar-track {
background: transparent;
}
.trip-switcher button {
flex: 0 0 auto;
background-color: white;
border: 1px solid var(--gray-200);
padding: 0.5rem 1rem;
border-radius: 8px;
font-size: 0.95rem;
cursor: pointer;
white-space: nowrap;
transition: color 0.3s ease;
}
.trip-switcher button:hover {
background-color: var(--gray-100);
} }
.gradient-wheel { .gradient-wheel {
@ -103,23 +197,31 @@
justify-content: center; justify-content: center;
} }
.image-list { .image-preview {
display: flex; padding: 1rem 2rem;
flex-direction: column; color: white;
align-items: center; }
.image-preview h2 {
font-size: 1.25rem;
margin-bottom: 0.75rem;
}
.image-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 1rem; gap: 1rem;
margin-top: 2rem;
} }
.image-list img { .image-grid img {
width: 80%; width: 100%;
max-width: 500px; border-radius: 8px;
border-radius: 12px; object-fit: cover;
aspect-ratio: 1 / 1;
} }
.empty { .no-image {
font-size: 0.95rem;
color: var(--gray-400); color: var(--gray-400);
text-align: center;
margin-top: 4rem;
} }
</style> </style>