aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rw-r--r--src/routes/api/image/remove.zig14
-rw-r--r--src/routes/api/image/root.zig1
-rw-r--r--src/storage/image-manager/image.zig11
-rw-r--r--src/storage/image-manager/root.zig34
-rw-r--r--static/api/images.js5
-rw-r--r--static/pages/image-viewer/index.css19
-rw-r--r--static/pages/image-viewer/index.js14
-rw-r--r--static/widgets/image/index.css45
-rw-r--r--static/widgets/image/index.js24
10 files changed, 157 insertions, 19 deletions
diff --git a/README.md b/README.md
index 9f496bd..db6b884 100644
--- a/README.md
+++ b/README.md
@@ -5,12 +5,13 @@
### Frontend
- [ ] Shuffle-Mode
- [ ] Something like *X* years/months/days ago this happened.
-- [ ] Settings
+- [x] Settings
- [x] Profile picture
- [x] Profile name
- - [ ] Birthday such we can flag pictures as birthday pictures.
-- [ ] Upload progress bar
+ - [x] Birthday such we can flag pictures as birthday pictures.
+- [x] Upload progress bar
### Backend
-- [ ] Not all files are delivered.
+- [x] Not all files are delivered.
+- [ ] **Removing file makes date ordering wrong**.
- [ ] Cache shuffle data in backend and drop them when next day starts.
diff --git a/src/routes/api/image/remove.zig b/src/routes/api/image/remove.zig
index e69de29..d99cbb4 100644
--- a/src/routes/api/image/remove.zig
+++ b/src/routes/api/image/remove.zig
@@ -0,0 +1,14 @@
+const std = @import("std");
+
+const memora = @import("memora");
+const Context = memora.Context;
+
+pub const access = .users;
+
+const Body = struct {
+ id: []const u8,
+};
+
+pub fn post(ctx: *Context, body: Body) anyerror!void {
+ return ctx.storage.images.delete(ctx.storage, body.id);
+}
diff --git a/src/routes/api/image/root.zig b/src/routes/api/image/root.zig
index dafa090..931bb06 100644
--- a/src/routes/api/image/root.zig
+++ b/src/routes/api/image/root.zig
@@ -3,3 +3,4 @@ const HandlerInfo = @import("../../handler-info.zig");
pub const upload: HandlerInfo = .from_type(@import("upload.zig"));
pub const list: HandlerInfo = .from_type(@import("list.zig"));
pub const load: HandlerInfo = .from_type(@import("load.zig"));
+pub const remove: HandlerInfo = .from_type(@import("remove.zig"));
diff --git a/src/storage/image-manager/image.zig b/src/storage/image-manager/image.zig
index b11b7b1..bdac5c7 100644
--- a/src/storage/image-manager/image.zig
+++ b/src/storage/image-manager/image.zig
@@ -56,6 +56,17 @@ pub fn new(
return self;
}
+pub fn delete(self: *Self, storage: *Storage) !void {
+ var dir = try storage.dir.openDir("image", .{});
+ defer dir.close();
+
+ var file_name: [id_size + 4]u8 = undefined;
+ @memcpy(file_name[0..id_size], self.id);
+ @memcpy(file_name[id_size..], ".jpg");
+
+ try dir.deleteFile(&file_name);
+}
+
pub fn init(storage: *Storage, id: []const u8) !Self {
return .{
.id = try storage.allocator.dupe(u8, id),
diff --git a/src/storage/image-manager/root.zig b/src/storage/image-manager/root.zig
index 4188b88..c99ac00 100644
--- a/src/storage/image-manager/root.zig
+++ b/src/storage/image-manager/root.zig
@@ -103,3 +103,37 @@ pub fn first_by_timestamp(self: *Self) memora.locked.Shared(?*Timestamp) {
.rw_lock = &self.rw_lock,
};
}
+
+pub fn delete(self: *Self, storage: *Storage, id: []const u8) !void {
+ self.rw_lock.lock();
+ defer self.rw_lock.unlock();
+
+ const index = index: {
+ for (self.items.items, 0..) |image, index| {
+ if (std.mem.eql(u8, image.id, id)) {
+ break :index index;
+ }
+ }
+ break :index null;
+ };
+
+ if (index) |idx| {
+ try self.items.items[idx].delete(storage);
+ const old_index = self.items.items.len - 1;
+ _ = self.items.swapRemove(idx);
+
+ var current: ?*Timestamp = if (self.timestamp_order.first) |c| @fieldParentPtr("node", c) else null;
+
+ while (current) |c| {
+ if (c.index == idx) {
+ self.timestamp_order.remove(&c.node);
+ storage.allocator.destroy(c);
+ } else if (c.index == old_index) {
+ c.index = idx;
+ break;
+ }
+
+ current = c.next();
+ }
+ }
+}
diff --git a/static/api/images.js b/static/api/images.js
index aa783e9..3b8c394 100644
--- a/static/api/images.js
+++ b/static/api/images.js
@@ -76,3 +76,8 @@ export function list() {
return r.images;
});
}
+
+
+export function remove(id) {
+ return rest.post('/api/image/remove', { id: id });
+}
diff --git a/static/pages/image-viewer/index.css b/static/pages/image-viewer/index.css
index 4b1eb9b..ec98173 100644
--- a/static/pages/image-viewer/index.css
+++ b/static/pages/image-viewer/index.css
@@ -6,16 +6,23 @@
display: grid;
gap: 10px;
padding: 10px;
+ padding-bottom: 50%;
}
#container sfw-image {
margin: auto;
- max-width: 700px;
- width: 100%;
- border-radius: var(--border-radius);
- box-shadow: #223223aa 1px 1px 4px;
}
-#container img.hidden {
- filter: brightness(0) invert();
+#subtitle {
+ text-align: center;
+ color: #7e8fa8;
+ font-size: 0.8em;
+ font-weight: lighter;
+ padding-top: 10px;
+}
+
+#subtitle i {
+ font-family: 'Pacifico';
+ font-style: normal;
+ color: var(--fg-disabled);
}
diff --git a/static/pages/image-viewer/index.js b/static/pages/image-viewer/index.js
index 0cf8598..e418e6f 100644
--- a/static/pages/image-viewer/index.js
+++ b/static/pages/image-viewer/index.js
@@ -7,6 +7,7 @@ const css = await sfw.css(import.meta.url, './index.css')
export default class ImageViewer extends sfw.element.Container {
#container
+ #subtitle
constructor() {
super({ css });
@@ -15,15 +16,24 @@ export default class ImageViewer extends sfw.element.Container {
this.onolder = () => {}
this.body.append(
- this.#container = Div.new({ id: 'container' })
+ this.#container = Div.new({
+ id: 'container',
+ children: [
+ this.#subtitle = Div.new({
+ id: 'subtitle',
+ innerHTML: 'Powered by <i>Memora<i>'
+ })
+ ],
+ })
);
}
add(id) {
- this.#container.append(Image.new({ id }))
+ this.#container.insertBefore(Image.new({ id }), this.#subtitle);
}
clear() {
this.#container.innerHTML = '';
+ this.#container.append(this.#subtitle);
}
}
diff --git a/static/widgets/image/index.css b/static/widgets/image/index.css
index f4e2dce..730a324 100644
--- a/static/widgets/image/index.css
+++ b/static/widgets/image/index.css
@@ -1,15 +1,18 @@
#container {
position: relative;
- background: var(--card-background);
height: 100%;
+ min-height: 100px;
+ border-radius: var(--border-radius);
+ overflow: none;
+ box-shadow: #223223aa 1px 1px 4px;
}
#container img {
+ border-radius: var(--border-radius);
max-width: 700px;
width: 100%;
- border-radius: var(--border-radius);
- box-shadow: #223223aa 1px 1px 4px;
visibility: hidden;
+ margin-bottom: -5px;
}
#container.loaded img {
@@ -17,9 +20,6 @@
}
@keyframes loader {
-}
-
-@keyframes loader {
0% {
width: 60px;
height: 60px;
@@ -57,3 +57,36 @@
.loaded #loading {
display: none;
}
+
+#menu {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ height: 100%;
+ width: 0px;
+ background: #efefef99;
+ backdrop-filter: blur(5px);
+ border-radius: var(--border-radius);
+ transition: width 0.2s ease, padding 0.2s ease;
+ overflow: hidden;
+ align-content: center;
+ overflow: hidden;
+ padding: 0px;
+}
+
+#menu.open {
+ width: 100%;
+ padding: 10px;
+}
+
+#delete {
+ background: #ee5151;
+ padding: 10px;
+ color: #fff;
+ font-weight: bold;
+ margin: auto;
+ width: min-content;
+ border-radius: var(--border-radius);
+ cursor: pointer;
+ user-select: none;
+}
diff --git a/static/widgets/image/index.js b/static/widgets/image/index.js
index ad77c4e..7111ec7 100644
--- a/static/widgets/image/index.js
+++ b/static/widgets/image/index.js
@@ -1,11 +1,15 @@
import * as sfw from 'sfw';
const { Div, Img } = sfw.element.native;
+import * as api from '../../api/index.js';
+
const css = await sfw.css(import.meta.url, './index.css')
export default class Image extends sfw.element.Container {
#container
#image
+ #menu
+ #id
constructor() {
super ({ css })
@@ -17,13 +21,31 @@ export default class Image extends sfw.element.Container {
this.#image = Img.new({
onload: () => this.#container.classList.add('loaded'),
}),
- Div.new({ id: 'loading' })
+ Div.new({ id: 'loading' }),
+ this.#menu = Div.new({
+ id: 'menu',
+ children: [
+ Div.new({
+ id: 'delete',
+ innerText: 'Delete',
+ onclick: () => {
+ api.images.remove(this.#id);
+ this.parentNode.removeChild(this);
+ }
+ }),
+ ]
+ })
],
+ oncontextmenu: (e) => {
+ this.#menu.classList.toggle('open');
+ e.preventDefault();
+ }
})
);
}
set id(id) {
+ this.#id = id;
this.#container.classList.remove('loaded');
this.#image.src = `/api/image/load/${id}`;
}