From 44445734ba4a0f05564f808c2fd34a7f23c6fafb Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Wed, 20 May 2026 10:16:50 +0200 Subject: Let's do a rewrite! Created project structure and changed `build.zig` and added licence. --- static/pages/image-viewer/index.css | 9 --- static/pages/image-viewer/index.js | 34 -------- static/pages/index.js | 5 -- static/pages/login/index.css | 153 ------------------------------------ static/pages/login/index.js | 118 --------------------------- static/pages/main/index.css | 70 ----------------- static/pages/main/index.js | 98 ----------------------- static/pages/settings/index.css | 104 ------------------------ static/pages/settings/index.js | 112 -------------------------- static/pages/shuffle/index.css | 39 --------- static/pages/shuffle/index.js | 110 -------------------------- 11 files changed, 852 deletions(-) delete mode 100644 static/pages/image-viewer/index.css delete mode 100644 static/pages/image-viewer/index.js delete mode 100644 static/pages/index.js delete mode 100644 static/pages/login/index.css delete mode 100644 static/pages/login/index.js delete mode 100644 static/pages/main/index.css delete mode 100644 static/pages/main/index.js delete mode 100644 static/pages/settings/index.css delete mode 100644 static/pages/settings/index.js delete mode 100644 static/pages/shuffle/index.css delete mode 100644 static/pages/shuffle/index.js (limited to 'static/pages') diff --git a/static/pages/image-viewer/index.css b/static/pages/image-viewer/index.css deleted file mode 100644 index 4567050..0000000 --- a/static/pages/image-viewer/index.css +++ /dev/null @@ -1,9 +0,0 @@ - -#container { - width: 100%; - height: 100vh; - overflow-y: auto; - display: grid; - gap: 10px; - padding: 10px; -} diff --git a/static/pages/image-viewer/index.js b/static/pages/image-viewer/index.js deleted file mode 100644 index 34b5f69..0000000 --- a/static/pages/image-viewer/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import * as sfw from 'sfw'; -const { Img, Div } = sfw.element.native; - -import Month from '../../month.js'; - -import stylesheet from './index.css'; - -const css = await sfw.css.sheet(stylesheet); - -export default class ImageViewer extends sfw.element.Container { - #images - #image_elements - - constructor() { - super({ css }); - - this.#images = []; - - this.body.append( - Div.new({ - id: 'container', - onscroll: () => console.log('scroll'), - }) - ); - } - - set images(images) { - this.#images = images; - } - - jump_to(month) { - // TODO - } -} diff --git a/static/pages/index.js b/static/pages/index.js deleted file mode 100644 index 56b0848..0000000 --- a/static/pages/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export ImageViewer from './image-viewer/index.js'; -export LoginView from './login/index.js'; -export MainView from './main/index.js'; -export SettingsView from './settings/index.js'; -export ShuffleView from './shuffle/index.js'; diff --git a/static/pages/login/index.css b/static/pages/login/index.css deleted file mode 100644 index d9d0f41..0000000 --- a/static/pages/login/index.css +++ /dev/null @@ -1,153 +0,0 @@ -#container { - background: var(--page-background); - overflow: hidden; - width: 100%; - height: 100%; - z-index: 1000; - position: absolute; - top: 0; - bottom: 0; - transition: top 0.2s ease; -} - -#container.hidden { - top: -100%; -} - -#box { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - max-width: 300px; - width: 100%; - padding: 0px; - box-shadow: #223223aa 1px 1px 4px; - border-radius: var(--border-radius); - z-index: 5000; - display: grid; - grid-template-columns: auto 30px; - background: #ffffff99; - backdrop-filter: blur(10px); -} - -#box input { - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; - background: #0000; -} - -#box #submit { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - background: var(--page-background); - color: var(--fg); - padding: 5px; - background: #0000; -} - -#username-label { - position: absolute; - top: calc(50% - 40px); - left: 50%; - transform: translate(-50%, -50%); - z-index: 10000; - user-select: none; - color: var(--fg-primary); - font-size: 0.8em; - cursor: pointer; -} -#comment { - position: absolute; - top: calc(50% + 40px); - left: 50%; - transform: translate(-50%, -50%); - z-index: 10000; - user-select: none; - color: var(--primary); - font-size: 0.8em; - cursor: pointer; -} - -#title { - font-family: 'Pacifico'; - position: absolute; - width: 100%; - height: 100%; - text-align: center; - top: 0px; - padding-top: 50px; - font-size: 2.5em; - z-index: 1000; - text-shadow: -1px -1px 0 var(--page-background), - 1px 1px 0 var(--page-background), - 1px -1px 0 var(--page-background), - -1px 1px 0 var(--page-background); - overflow: hidden; -} - -@keyframes left-bubble { - from { - width: 0px; - height: 0px; - } - to { - width: 100vw; - height: 100vw; - } -} - -#title:before { - content: ''; - display: block; - position: absolute; - top: -30px; - left: 0px; - background: var(--primary); - transform: translate(-50%, -50%); - border-radius: 100%; - z-index: -1; - animation: left-bubble 1s normal forwards ease; - width: 100vw; - height: 100vw; -} - -@keyframes right-bubble { - from { - width: 0px; - height: 0px; - } - to { - width: 200vw; - height: 200vw; - } -} - -#title:after { - content: ''; - display: block; - position: absolute; - bottom: 55%; - right: 0px; - width: 200vw; - height: 200vw; - transform: translate(50%, 50%); - background: var(--fg); - border-radius: 100%; - z-index: -2; - animation: right-bubble 1s normal forwards ease; -} -#subtitle { - position: absolute; - bottom: 50px; - left: 50%; - transform: translate(-50%, 0); - white-space: nowrap; - font-family: 'Pacifico'; - color: #69717d; - z-index: 1000; - text-shadow: -1px -1px 0 var(--page-background), - 1px 1px 0 var(--page-background), - 1px -1px 0 var(--page-background), - -1px 1px 0 var(--page-background); -} diff --git a/static/pages/login/index.js b/static/pages/login/index.js deleted file mode 100644 index 0390cc0..0000000 --- a/static/pages/login/index.js +++ /dev/null @@ -1,118 +0,0 @@ -import icons from 'icons'; -import * as sfw from 'sfw'; -const { Div, Label, H1: Title, Input, Button, Form } = sfw.element.native; - -import stylesheet from './index.css'; - -const css = await sfw.css.sheet(stylesheet); - -export default class LoginView extends sfw.element.Container { - #container - #user - #username - #password - #comment - - constructor() { - super({ css }); - - this.onlogin = () => {}; - this.onpassword = () => {}; - - this.body.append( - this.#container = Div.new({ - id: 'container', - children: [ - Div.new({ id: 'title', innerText: 'Memora' }), - this.#user = Div.new({ - id: 'username-label', - onclick: () => this.#reset(), - }), - Form.new({ - id: 'box', - onsubmit: (e) => e.preventDefault(), - children: [ - this.#username = Input.new({ - id: 'username', - placeholder: 'Username', - autocomplete: 'username', - }), - this.#password = Input.new({ - id: 'password', - placeholder: 'Password', - autocomplete: 'current-password', - type: 'password', - style: { display: 'none' }, - onkeydown: (e) => { - if (e.key === 'Enter') { - return this.#login(); - } - }, - }), - Button.new({ - id: 'submit', - children: [ icons.next ], - onclick: () => this.#next(), - }), - ] - }), - this.#comment = Div.new({ - id: 'comment', - onclick: () => this.#reset(), - }), - Div.new({ - id: 'subtitle', - innerText: 'Where nostalgia is home.', - }), - ] - }) - ); - } - - #next() { - if (this.#username.style.display != 'none') { - this.#go_to_password(); - } else { - this.#login(); - } - } - - #go_to_password() { - if (this.#username.value === '') return; - - this.#user.innerText = this.#username.value; - this.#username.style.display = 'none'; - this.#password.style.display = ''; - this.#password.focus(); - } - - async #login() { - await this.onlogin(this.#username.value, this.#password.value); - this.#reset(); - } - - #reset() { - this.#user.innerText = ''; - this.#username.value = ''; - this.#password.value = ''; - this.#username.style.display = ''; - this.#password.style.display = 'none'; - } - - set comment(value) { - this.#comment.innerText = value; - } - - show() { - this.#container.classList.remove('hidden'); - this.focus(); - } - - hide() { - this.#container.classList.add('hidden'); - } - - focus() { - this.#username.focus(); - } -} diff --git a/static/pages/main/index.css b/static/pages/main/index.css deleted file mode 100644 index 21d5022..0000000 --- a/static/pages/main/index.css +++ /dev/null @@ -1,70 +0,0 @@ - -:host { - display: grid; - height: 100%; - width: 100%; - background: var(--page-background); -} - -#bar { - position: fixed; - bottom: 5px; - left: 5px; - right: 5px; - background: #fff9; - box-shadow: #223223aa 1px 1px 4px; - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr 1fr ; - height: 50px; - backdrop-filter: blur(10px); - border-radius: var(--border-radius); - transition: bottom 0.2s ease; - max-width: 600px; - margin: 0 auto; -} - -#bar.hidden { - bottom: -200px; -} - -.menu-button { - color: var(--fg-disabled); - width: 18px; - height: 18px; - border-radius: 100%; - margin: auto; - cursor: pointer; - user-select: none; - align-content: center; - text-align: center; -} - -.menu-button.add { - width: 60px; - height: 60px; - margin-top: -20px; - background: var(--primary); - color: var(--card-background); - font-weight: bold; - font-size: 2em; -} - -.menu-button.add .icon { - width: 30px; - height: 30px; - display: grid; - margin: auto; -} - -.menu-button.add .icon .bi-house-fill { - width: 20px; - height: 20px; - margin: auto; -} - -.menu-button .icon { - width: 100%; - height: 100%; - display: grid; - margin: auto; -} diff --git a/static/pages/main/index.js b/static/pages/main/index.js deleted file mode 100644 index b0a13f6..0000000 --- a/static/pages/main/index.js +++ /dev/null @@ -1,98 +0,0 @@ -import icons from 'icons'; -import * as sfw from 'sfw'; -const { Div, Input } = sfw.element.native; - -import stylesheet from './index.css'; - -const css = await sfw.css.sheet(stylesheet); - -export default class MainView extends sfw.element.Container { - #active_kind - #active_button - #active_view - #bar - - static Kind = Object.freeze({ - 'upload': 0, - 'home': 1, - }); - - constructor() { - super({ css }); - - this.onsearch = () => {} - this.onmonth = () => {} - this.onupload = () => {} - this.onshuffle = () => {} - this.onhome = () => {} - - this.body.append( - this.#active_view = Div.new({ id: 'main' }), - - this.#bar = Div.new({ - id: 'bar', - children: [ - Div.new({ - className: 'menu-button', - children: [ icons.search ], - onclick: (e) => { - e.stopPropagation(); - this.onsearch(); - } - }), - - Div.new({ - className: 'menu-button', - children: [ icons.calendar ], - onclick: () => this.onmonth(), - }), - - this.#active_button = Div.new({ - className: 'menu-button add', - children: [ icons.add ], - onclick: () => this.onupload() - }), - - Div.new({ - className: 'menu-button', - children: [ icons.shuffle ], - onclick: () => this.onshuffle(), - }), - - Div.new({ - className: 'menu-button', - children: [ icons.settings ], - onclick: () => this.onsettings(), - }), - ], - }), - ); - } - - hide() { - this.#bar.classList.add('hidden'); - } - - show() { - this.#bar.classList.remove('hidden'); - } - - set active_view(element) { - this.#active_view.innerHTML = ''; - this.#active_view.append(element); - } - - set active_kind(kind) { - if (kind == MainView.Kind.home) { - this.#active_button.innerHTML = ''; - this.#active_button.append(icons.home); - this.#active_button.onclick = () => this.onhome(); - } else if (kind == MainView.Kind.upload) { - this.#active_button.innerHTML = ''; - this.#active_button.append(icons.add); - this.#active_button.onclick = () => this.onupload(); - } else { - console.error(`invalid kind ${kind}`); - } - } -} diff --git a/static/pages/settings/index.css b/static/pages/settings/index.css deleted file mode 100644 index dcd952f..0000000 --- a/static/pages/settings/index.css +++ /dev/null @@ -1,104 +0,0 @@ - -#container { - width: 100%; - height: 100%; - background: var(--page-background); - padding: 40px; - display: flex; - flex-flow: column; - gap: 20px; - max-width: 700px; - margin: auto; -} - -#profile-image { - width: 200px; - height: 200px; - position: relative; - margin: auto; - margin-top: 50px; - margin-bottom: 30px; -} - -#image-container { - background: #fff; - position: relative; - border-radius: 100%; - width: 100%; - height: 100%; - top: 0px; - left: 0px; - overflow: hidden; -} - -#image-container img { - visibility: hidden; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - min-width: 100%; - min-height: 100%; - width: 100%; - height: 100%; - object-fit: cover; - user-select: none; -} - -#image-container img[src] { - visibility: visible; -} - -#profile-image #edit-container { - overflow: unset; -} - -#profile-image #edit { - position: absolute; - top: 15px; - right: 15px; - background: var(--card-background); - width: 30px; - height: 30px; - padding: 6px; - border-radius: 100%; - box-shadow: var(--shadow); - cursor: pointer; -} - -#info-box { - width: 100%; - text-align: center; - color: var(--fg-disabled); - font-weight: lighter; - user-select: none; -} - -#info-box #name { - font-family: 'Pacifico'; -} - -#change-password { - background: var(--primary); - padding: 10px; - border-radius: var(--border-radius); - color: var(--fg-primary); - box-shadow: var(--shadow); - font-weight: bold; - text-align: center; - cursor: pointer; - user-select: none; -} - -#logout { - padding: 10px; - width: 100%; - text-align: center; - background: #ee5151; - box-shadow: var(--shadow); - color: #fff; - font-weight: bold; - border-radius: var(--border-radius); - cursor: pointer; - user-select: none; -} diff --git a/static/pages/settings/index.js b/static/pages/settings/index.js deleted file mode 100644 index 5cc40be..0000000 --- a/static/pages/settings/index.js +++ /dev/null @@ -1,112 +0,0 @@ -import icons from 'icons'; -import * as api from 'api'; -import * as sfw from 'sfw'; -const { Div, Img } = sfw.element.native; - -import Editable from '../../widgets/editable/index.js'; -import PasswordDialog from '../../widgets/password-dialog/index.js'; - -import stylesheet from './index.css'; - -const css = await sfw.css.sheet(stylesheet); - -export default class SettingsView extends sfw.element.Container { - #profile_image - #name - #birthday - - #profile - #change_password - - constructor() { - super({ css }); - - this.onlogout = () => {}; - - this.body.append( - Div.new({ - id: 'container', - children: [ - Div.new({ - id: 'profile-image', - children: [ - Div.new({ - id: 'image-container', - children: [ - this.#profile_image = Img.new(), - ] - }), - Div.new({ - id: 'edit', - children: [ icons.edit ], - onclick: async () => { - const uploader = await api.images.upload_to_profile(this.#profile.name); - uploader.ondone = async () => { - const blob = await fetch( - `/api/profile/image/load/${this.#profile.name}`, - {cache: 'reload', mode: 'no-cors'} - ).then(r => r.blob()); - - this.#profile_image.src = URL.createObjectURL(blob); - }; - uploader.send() - } - }), - ] - }), - this.#name = Editable.new({ - title: 'Name', - value: '', - onupdate: () => this.#update(), - }), - this.#birthday = Editable.new({ - title: 'Birthday', - type: 'date', - value: '', - onupdate: () => this.#update(), - }), - Div.new({ - id: 'change-password', - innerText: 'Change Password', - onclick: (e) => { - e.stopPropagation(); - this.body.append(PasswordDialog.new()); - } - }), - Div.new({ - id: 'logout', - innerText: 'Logout', - onclick: () => this.onlogout(), - }), - Div.new({ - id: 'info-box', - children: [ - Div.new({ id: 'name', innerText: 'Memora' }), - Div.new({ id: 'version', innerText: '0.0.1-unstable' }), - ] - }), - ] - }) - ) - } - - #update() { - api.profile.set(this.#name.value, this.#birthday.value); - } - - set profile(profile) { - this.#profile = profile; - - if (this.#profile) { - this.#profile_image.src = `/api/profile/image/load/${profile.name}`; - this.#profile_image.onerror = () => this.#profile_image.removeAttribute('src'); - - this.#name.value = profile.full_name; - this.#birthday.value = profile.birthday; - } - } - - get profile() { - return this.#profile; - } -} diff --git a/static/pages/shuffle/index.css b/static/pages/shuffle/index.css deleted file mode 100644 index 30d30ac..0000000 --- a/static/pages/shuffle/index.css +++ /dev/null @@ -1,39 +0,0 @@ -:host { - display: block; - width: 100vw; - height: 100vh; - top: 0px; - left: 0px; - overflow: hidden; - position: relative; -} - -sfw-image { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, calc(-50% - 50px)); - width: 100%; - max-width: calc(100vw - 20px); - max-height: calc(100vh - 200px); - user-select: none; - transition: transform 0.05s ease; -} - -#front { - z-index: 1000; -} - -#back { - z-index: 500; - filter: blur(50px); -} - -#date { - margin: 10px; - border-radius: var(--border-radius); - padding: 10px; - background: #efefef99; - backdrop-filter: blur(10px); - box-shadow: #223223aa 1px 1px 4px; -} diff --git a/static/pages/shuffle/index.js b/static/pages/shuffle/index.js deleted file mode 100644 index 620195c..0000000 --- a/static/pages/shuffle/index.js +++ /dev/null @@ -1,110 +0,0 @@ -import * as sfw from 'sfw'; -const { Div, Canvas } = sfw.element.native; - -import { Image } from 'widgets'; - -import stylesheet from './index.css'; - -const css = await sfw.css.sheet(stylesheet); - -export default class ShuffleView extends sfw.element.Container { - #front_image - #back_image - #front_image_index - #back_image_index - #images - #date - #drag_start_position - #drag_strength - - constructor() { - super({ css }); - - this.#front_image_index = null; - this.#back_image_index = null; - - this.body.append( - this.#date = Div.new({ id: 'date' }), - this.#front_image = Image.new({ - id: 'front', - disabled: true, - ontouchstart: (e) => { - if (e.touches.length != 1) return; - - this.#drag_start_position = [e.touches[0].pageX, e.touches[0].pageY]; - }, - ontouchmove: (e) => { - if (e.touches.length != 1) return; - - const position = [e.touches[0].pageX, e.touches[0].pageY]; - - const delta = [ - position[0] - this.#drag_start_position[0], - position[1] - this.#drag_start_position[1] - ]; - - const strength = Math.min(1, Math.max((delta[0]**2 + delta[1]**2) / (40 ** 2), 0) / 100); - this.#drag_strength = strength; - - const angle_x = position[0] - window.screen.width / 2; - const angle_y = position[1] - window.screen.height * 1.2; - let angle = (Math.atan((angle_y) / (angle_x)) * 180 / Math.PI + 90); - - if (angle_x < 0) { - angle -= 180; - } - - this.#front_image.style.transform = ` - translate(-50%, calc(-50% - 50px)) - translate(${delta[0]}px, ${delta[1]}px) - rotate(${angle}deg) - `; - this.#back_image.style.filter = `blur(${(1 - strength) * 50}px)`; - }, - ontouchend: (e) => { - if (this.#drag_strength > 0.3) { - this.next(); - } - - this.#front_image.style.transform = ''; - this.#back_image.style.filter = ''; - }, - }), - this.#back_image = Image.new({ id: 'back', disabled: true }), - ) - - this.onclick = () => this.next(); - } - - set images(images) { - this.#images = images; - } - - open() { - this.next(); - this.next(); - } - - next() { - this.#front_image_index = this.#back_image_index; - this.#back_image_index = Math.floor(Math.random() * this.#images.length); - - if (this.#front_image_index !== null) { - this.#front_image.metadata = this.#images[this.#front_image_index]; - this.#front_image.load(); - - const date = this.#images[this.#front_image_index].date - - this.#date.innerText = date.toLocaleString('default', { - month: 'long', - year: 'numeric', - day: 'numeric', - hour: 'numeric', - minute: 'numeric' - }); ; - } - - this.#back_image.metadata = this.#images[this.#back_image_index]; - this.#back_image.load(); - } -} -- cgit v1.2.3-70-g09d2