NewMemoryPopup with problems

This commit is contained in:
Chaebean Yang 2025-06-06 18:58:10 +09:00
parent 7b0740fe0a
commit bd6b405478
4 changed files with 205 additions and 181 deletions

52
package-lock.json generated
View File

@ -10,7 +10,7 @@
"dependencies": {
"@googlemaps/js-api-loader": "^1.16.8",
"d3": "^7.9.0",
"firebase": "^11.8.1",
"firebase": "^11.9.0",
"topojson-client": "^3.1.0",
"topojson-server": "^3.0.1"
},
@ -469,9 +469,9 @@
}
},
"node_modules/@firebase/ai": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.3.0.tgz",
"integrity": "sha512-qBxJTtl9hpgZr050kVFTRADX6I0Ss6mEQyp/JEkBgKwwxixKnaRNqEDGFba4OKNL7K8E4Y7LlA/ZW6L8aCKH4A==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.0.tgz",
"integrity": "sha512-wvF33gtU6TXb6Co8TEC1pcl4dnVstYmRE/vs9XjUGE7he7Sgf5TqSu+EoXk/fuzhw5tKr1LC5eG9KdYFM+eosw==",
"license": "Apache-2.0",
"dependencies": {
"@firebase/app-check-interop-types": "0.3.3",
@ -527,9 +527,9 @@
"license": "Apache-2.0"
},
"node_modules/@firebase/app": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.0.tgz",
"integrity": "sha512-Vj3MST245nq+V5UmmfEkB3isIgPouyUr8yGJlFeL9Trg/umG5ogAvrjAYvQ8gV7daKDoQSRnJKWI2JFpQqRsuQ==",
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.1.tgz",
"integrity": "sha512-0O33PKrXLoIWkoOO5ByFaLjZehBctSYWnb+xJkIdx2SKP/K9l1UPFXPwASyrOIqyY3ws+7orF/1j7wI5EKzPYQ==",
"license": "Apache-2.0",
"dependencies": {
"@firebase/component": "0.6.17",
@ -593,12 +593,12 @@
"license": "Apache-2.0"
},
"node_modules/@firebase/app-compat": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.0.tgz",
"integrity": "sha512-LjLUrzbUgTa/sCtPoLKT2C7KShvLVHS3crnU1Du02YxnGVLE0CUBGY/NxgfR/Zg84mEbj1q08/dgesojxjn0dA==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.1.tgz",
"integrity": "sha512-9VGjnY23Gc1XryoF/ABWtZVJYnaPOnjHM7dsqq9YALgKRtxI1FryvELUVkDaEIUf4In2bfkb9ZENF1S9M273Dw==",
"license": "Apache-2.0",
"dependencies": {
"@firebase/app": "0.13.0",
"@firebase/app": "0.13.1",
"@firebase/component": "0.6.17",
"@firebase/logger": "0.4.4",
"@firebase/util": "1.12.0",
@ -748,9 +748,9 @@
}
},
"node_modules/@firebase/firestore": {
"version": "4.7.16",
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.16.tgz",
"integrity": "sha512-5OpvlwYVUTLEnqewOlXmtIpH8t2ISlZHDW0NDbKROM2D0ATMqFkMHdvl+/wz9zOAcb8GMQYlhCihOnVAliUbpQ==",
"version": "4.7.17",
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.17.tgz",
"integrity": "sha512-YhXWA7HlSnekExhZ5u4i0e+kpPxsh/qMrzeNDgsAva71JXK8OOuOx+yLyYBFhmu3Hr5JJDO2fsZA/wrWoQYHDg==",
"license": "Apache-2.0",
"dependencies": {
"@firebase/component": "0.6.17",
@ -769,13 +769,13 @@
}
},
"node_modules/@firebase/firestore-compat": {
"version": "0.3.51",
"resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.51.tgz",
"integrity": "sha512-E5iubPhS6aAM7oSsHMx/FGBwfA2nbEHaK/hCs+MD3l3N7rHKnq4SYCGmVu/AraSJaMndZR1I37N9A/BH7aCq5A==",
"version": "0.3.52",
"resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.52.tgz",
"integrity": "sha512-nzt3Sag+EBdm1Jkw/FnnKBPk0LpUUxOlMHMADPBXYhhXrLszxn1+vb64nJsbgRIHfsCn+rg8gyGrb+8frzXrjg==",
"license": "Apache-2.0",
"dependencies": {
"@firebase/component": "0.6.17",
"@firebase/firestore": "4.7.16",
"@firebase/firestore": "4.7.17",
"@firebase/firestore-types": "3.0.3",
"@firebase/util": "1.12.0",
"tslib": "^2.1.0"
@ -2733,26 +2733,26 @@
}
},
"node_modules/firebase": {
"version": "11.8.1",
"resolved": "https://registry.npmjs.org/firebase/-/firebase-11.8.1.tgz",
"integrity": "sha512-oetXhPCvJZM4DVL/n/06442emMU+KzM0JLZjszpwlU6mqdFZqBwumBxn6hQkLukJyU5wsjihZHUY8HEAE2micg==",
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/firebase/-/firebase-11.9.0.tgz",
"integrity": "sha512-7uIGhxKtTNfDcoMKWn0G8G0Z1Zj5VeW8uzImAcUmI31PaYQdVWi2rVVig7thWB3vPianESPrLEKim2Fw7U8fiA==",
"license": "Apache-2.0",
"dependencies": {
"@firebase/ai": "1.3.0",
"@firebase/ai": "1.4.0",
"@firebase/analytics": "0.10.16",
"@firebase/analytics-compat": "0.2.22",
"@firebase/app": "0.13.0",
"@firebase/app": "0.13.1",
"@firebase/app-check": "0.10.0",
"@firebase/app-check-compat": "0.3.25",
"@firebase/app-compat": "0.4.0",
"@firebase/app-compat": "0.4.1",
"@firebase/app-types": "0.9.3",
"@firebase/auth": "1.10.6",
"@firebase/auth-compat": "0.5.26",
"@firebase/data-connect": "0.3.9",
"@firebase/database": "1.0.19",
"@firebase/database-compat": "2.0.10",
"@firebase/firestore": "4.7.16",
"@firebase/firestore-compat": "0.3.51",
"@firebase/firestore": "4.7.17",
"@firebase/firestore-compat": "0.3.52",
"@firebase/functions": "0.12.8",
"@firebase/functions-compat": "0.3.25",
"@firebase/installations": "0.6.17",

View File

@ -28,7 +28,7 @@
"dependencies": {
"@googlemaps/js-api-loader": "^1.16.8",
"d3": "^7.9.0",
"firebase": "^11.8.1",
"firebase": "^11.9.0",
"topojson-client": "^3.1.0",
"topojson-server": "^3.0.1"
}

View File

@ -1,32 +1,24 @@
<script lang="ts">
import '../../app.css';
import Button from './Button.svelte';
import Button from '$lib/components/Button.svelte';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { Loader } from '@googlemaps/js-api-loader';
import { goto } from '$app/navigation';
import { collection, addDoc, getFirestore, Timestamp } from 'firebase/firestore';
import { app } from '$lib/firebase';
export let showPopup = false;
<<<<<<< HEAD
export let onAddMemory = () => {};
=======
export let locations: any[] = [];
export let onAddMemory = (p0?: { location: string; images: any[]; startDate: string; endDate: string; }) => {};
>>>>>>> 9def1973a7b39a01052b5d81f6a5327e2524e7e1
export let onCancel = () => {};
let startDate = "";
let endDate = "";
let startDate = '';
let endDate = '';
let isGoogleLoaded = false;
let dragActive = false;
let selectedLocation = '';
let customLocation = '';
<<<<<<< HEAD
let customLocationInput: HTMLInputElement;
let images = [];
=======
let images: any[] = [];
let dragActive = false;
>>>>>>> 9def1973a7b39a01052b5d81f6a5327e2524e7e1
let images: File[] = [];
let showLocationError = false;
let showImageError = false;
@ -34,126 +26,112 @@
const GOOGLE_PLACES_API_KEY = import.meta.env.VITE_GOOGLE_PLACES_API_KEY;
onMount(async () => {
if (!GOOGLE_PLACES_API_KEY) {
console.error('Google Maps API key is missing');
return;
}
if (!GOOGLE_PLACES_API_KEY) return;
const loader = new Loader({
apiKey: GOOGLE_PLACES_API_KEY,
version: "weekly",
libraries: ["places"],
language: 'en'
});
const loader = new Loader({
apiKey: GOOGLE_PLACES_API_KEY,
version: 'weekly',
libraries: ['places'],
language: 'en'
});
try {
await loader.importLibrary("places");
isGoogleLoaded = true;
} catch (error) {
console.error('Error loading Places Autocomplete:', error);
}
try {
await loader.importLibrary('places');
isGoogleLoaded = true;
} catch (error) {
console.error('Error loading Places Autocomplete:', error);
}
});
$: if (isGoogleLoaded && isCustomLocation() && customLocationInput) {
const autocompleteCustom = new google.maps.places.Autocomplete(customLocationInput, {
types: ['(regions)']
});
const autocompleteCustom = new google.maps.places.Autocomplete(customLocationInput, {
types: ['(regions)']
});
autocompleteCustom.setFields(['name', 'formatted_address']);
autocompleteCustom.addListener('place_changed', () => {
const place = autocompleteCustom.getPlace();
if (place.name) {
customLocation = place.name;
showLocationError = false;
}
});
}
function handleFiles(files: any) {
for (const file of files) {
if (file.type.startsWith('image/')) {
images = [...images, file];
}
autocompleteCustom.setFields(['name']);
autocompleteCustom.addListener('place_changed', () => {
const place = autocompleteCustom.getPlace();
if (place.name) {
customLocation = place.name;
showLocationError = false;
}
});
}
function handleDrop(event: any) {
event.preventDefault();
dragActive = false;
handleFiles(event.dataTransfer.files);
}
function handleDragOver(event: any) {
event.preventDefault();
dragActive = true;
}
function handleDragLeave(event: any) {
event.preventDefault();
dragActive = false;
}
function handleInputChange(event: any) {
if (event.target.files) {
handleFiles(event.target.files);
function handleFiles(files: FileList) {
for (const file of files) {
if (file.type.startsWith('image/')) {
images = [...images, file];
}
}
}
function handleCancelClick() {
onCancel();
reset();
function handleDrop(event: DragEvent) {
event.preventDefault();
dragActive = false;
handleFiles(event.dataTransfer!.files);
}
function reset() {
showPopup = false;
selectedLocation = '';
customLocation = '';
images = [];
startDate = '';
endDate = '';
showLocationError = false;
showImageError = false;
function handleDragOver(event: DragEvent) {
event.preventDefault();
dragActive = true;
}
function handleDragLeave(event: DragEvent) {
event.preventDefault();
dragActive = false;
}
function handleInputChange(event: Event) {
const target = event.target as HTMLInputElement;
if (target.files) {
handleFiles(target.files);
}
}
function isCustomLocation() {
return selectedLocation === 'custom';
return selectedLocation === 'custom';
}
function removeImage(imageToRemove: File) {
images = images.filter(img => img !== imageToRemove);
images = images.filter(img => img !== imageToRemove);
}
function handleAddMemory() {
showLocationError = selectedLocation === '' || (selectedLocation === 'custom' && customLocation.trim() === '');
showImageError = images.length === 0;
async function handleAddMemory() {
showLocationError = selectedLocation === '' || (isCustomLocation() && customLocation.trim() === '');
showImageError = images.length === 0;
if (showLocationError || showImageError) return;
if (showLocationError || showImageError) return;
const finalLocation = selectedLocation === 'custom' ? customLocation : selectedLocation;
const finalLocation = isCustomLocation() ? customLocation : selectedLocation;
const memory = {
location: finalLocation,
images,
startDate,
endDate
};
const db = getFirestore(app);
const docRef = await addDoc(collection(db, 'memories'), {
location: finalLocation,
startDate,
endDate,
images: images.map(file => URL.createObjectURL(file)), // 임시 처리. 실제 서비스에서는 Storage URL 써야 함
createdAt: Timestamp.now()
});
// link data later...
const params = new URLSearchParams({
location: finalLocation,
startDate,
endDate
});
reset();
goto(`/viewimage?id=${docRef.id}`);
}
goto(`/viewimage?${params.toString()}`);
function reset() {
showPopup = false;
selectedLocation = '';
customLocation = '';
images = [];
startDate = '';
endDate = '';
showLocationError = false;
showImageError = false;
}
reset();
}
// needs to link with plan data set
const locations = ['Paris', 'Tokyo', 'New York'];
</script>
</script>
{#if showPopup}
<div class="overlay">

View File

@ -1,43 +1,89 @@
<script>
import { page } from '$app/stores';
import { onMount } from 'svelte';
import '../../app.css';
import Button from '$lib/components/Button.svelte';
import Nav from '$lib/components/Nav.svelte';
import { currentMemory } from '$lib/stores/memory';
import { get } from 'svelte/store';
let location = '';
let startDate = '';
let endDate = '';
let memory = get(currentMemory);
$: {
const q = $page.url.searchParams;
location = q.get('location') ?? '';
startDate = q.get('startDate') ?? '';
endDate = q.get('endDate') ?? '';
}
// 메인 이미지
let mainImageUrl = memory?.images?.[0]
? typeof memory.images[0] === 'string'
? memory.images[0]
: URL.createObjectURL(memory.images[0])
: '';
// 컬럼별 대표 색 (예시용)
let gradientColors = ['#e74c3c', '#f1c40f', '#2ecc71', '#3498db', '#9b59b6'];
$: gradientStyle = `conic-gradient(${[...gradientColors, gradientColors[0]].join(', ')})`;
</script>
<div class="memory-view">
<h2>{location}</h2>
<p>{startDate} - {endDate}</p>
<main>
<Nav activeTab="Memory" />
<div class="gradient-wheel" style="background-image: {gradientStyle};"></div>
</div>
<div class="content">
<div class="header">
<h1>Memory View</h1>
</div>
{#if memory}
<div class="memory-container">
<h2>{memory.location}</h2>
<p>{memory.startDate} - {memory.endDate}</p>
<div class="visual-section">
<!-- 🎨 gradient wheel -->
<div
class="gradient-wheel"
style="background-image: {gradientStyle}; width: 300px; height: 300px"
></div>
<!-- 📸 main image -->
<img
class="main-image"
src={mainImageUrl}
alt="Main Memory Image"
/>
</div>
</div>
{:else}
<p>Memory not loaded.</p>
{/if}
</div>
</main>
<style>
.memory-view {
.content {
padding: 2rem;
font-family: sans-serif;
}
.header {
margin-bottom: 2rem;
}
.memory-container {
text-align: center;
}
.visual-section {
margin-top: 2rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
}
.gradient-wheel {
width: 300px;
height: 300px;
margin: 2rem auto;
border-radius: 50%;
background: var(--gradient);
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
}
.main-image {
width: 300px;
height: auto;
border-radius: 12px;
object-fit: cover;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.2);
}
</style>