Files
alphabreed/hugo/content/bookmarks/_index.html
2026-01-14 23:24:24 +01:00

176 lines
6.0 KiB
HTML

---
title: "Bookmarks"
---
<main x-data="{
store: null,
categories: [],
openCategoryId: '',
slideInOpen: false,
showFavletCode: false,
editCategory: null,
editBookmark: null,
host: '',
async updateBookmarks() {
const result = await this.store.getBookmarks();
const grouped = Object.groupBy(result, (bookmark) => bookmark.expand.category.id);
this.categories = Object.values(grouped).map((group) => {
const category = group[0].expand.category;
return {
id: category.id,
name: category.name,
bookmarks: group.map((bookmark) => ({
category: category.id,
id: bookmark.id,
name: bookmark.name,
url: bookmark.url
}))
};
});
this.categories.sort((a, b) => a.name.localeCompare(b.name));
},
openCategory(id, element) {
if (this.openCategoryId == id) {
this.openCategoryId = '';
return;
}
this.openCategoryId = id;
element.scrollIntoView(true);
},
async saveCategory() {
if (!this.editCategory || !this.editCategory.name)
return;
await this.store.updateBookmarkCategory(
this.editCategory.id, this.editCategory.name
);
this.slideInOpen = false;
await this.updateBookmarks();
},
async deleteCategory() {
if (!this.editCategory)
return;
await this.store.deleteBookmarkCategory(this.editCategory.id);
this.slideInOpen = false;
await this.updateBookmarks();
},
async saveBookmark() {
let categoryId = this.editBookmark.category || this.categories[0].id;
if (!this.editBookmark || !this.editBookmark.name || !this.editBookmark.url || (!categoryId && !this.editBookmark.newcategory))
return;
if (this.editBookmark.newcategory)
categoryId = await this.store.createBookmarkCategory(this.editBookmark.newcategory);
if (this.editBookmark.id) {
await this.store.updateBookmark(
this.editBookmark.id,
categoryId,
this.editBookmark.name,
this.editBookmark.url
);
}
else {
await this.store.createBookmark(
categoryId,
this.editBookmark.name,
this.editBookmark.url
);
}
this.slideInOpen = false;
this.openCategoryId = categoryId;
await this.updateBookmarks();
},
async deleteBookmark() {
if (!this.editBookmark)
return;
await this.store.deleteBookmark(this.editBookmark.id);
this.slideInOpen = false;
await this.updateBookmarks();
},
async init() {
this.store = Alpine.store('alphabreed');
this.store.backUrl = '/bookmarks/';
this.host = window.location.protocol + '//' + window.location.host;
const tokenOK = await this.store.checkToken();
if (tokenOK)
this.updateBookmarks();
}
}">
<p><button @click="editBookmark = {}; editCategory = null; showFavletCode = false; slideInOpen = true;">+ Bookmark</button></p>
<span id="favlet" @click="showFavletCode = true; editCategory = null; editBookmark = null; slideInOpen = true;">Generate Favlet code</span>
<div class="categories">
<template x-for="category in categories" :key="category.id">
<div class="category" :class="{'open': category.id == openCategoryId}">
<h1>
<span class="title" x-text="category.name" @click="openCategory(category.id, $el)"></span>
<span class="badge" x-text="category.bookmarks.length"></span>
<span class="edit" @click="editCategory = Object.assign({}, category); editBookmark = null; showFavletCode = false; slideInOpen = true;"></span>
</h1>
<ul class="bookmarks">
<template x-for="bookmark in category.bookmarks" :key="bookmark.id">
<li class="bookmark">
<div class="editicon" @click="editBookmark = Object.assign({}, bookmark); editCategory = null; showFavletCode = false; slideInOpen = true;">
<img :src="'https://www.google.com/s2/favicons?domain=' + encodeURIComponent(bookmark.url)">
</div>
<a class="link" :href="bookmark.url" target="_blank">
<span x-text="bookmark.name"></span>
</a>
</li>
</template>
</ul>
</div>
</template>
</div>
<div id="slidein" :class="{'open': slideInOpen}">
<span class="closer" @click="slideInOpen = false;"></span>
<div class="inner">
<template x-if="showFavletCode">
<div>
<p>Favlet code:</p>
<p class="favletcode" x-text='"javascript:(function(w,e){w.open(\""+host+"/bookmarks/add/?name=\"+e(document.title)+\"&url=\"+e(w.location.href),\"_blank\",\"height=500,width=500,location=0,menubar=0,scrollbars=0,status=0,titlebar=0,toolbar=0\")})(window,encodeURIComponent)"'></p>
</div>
</template>
<template x-if="editCategory">
<div>
<div class="field textfield">
<label for="categoryname">Category name</label>
<input id="categoryname" type="text" x-model="editCategory.name">
</div>
<div class="field buttons">
<button @click="saveCategory()">Save</button>
</div>
<div class="field buttons">
<button @click="deleteCategory()">Delete</button>
</div>
</div>
</template>
<template x-if="editBookmark">
<div>
<div class="field textfield">
<label for="bookmarkname">Bookmark name</label>
<input id="bookmarkname" type="text" x-model="editBookmark.name">
</div>
<div class="field textfield">
<label for="bookmarkurl">URL</label>
<input id="bookmarkurl" type="text" x-model="editBookmark.url">
</div>
<div class="field selectfield">
<label for="bookmarkcategory">Category</label>
<select id="bookmarkcategory" x-model="editBookmark.category">
<template x-for="category in categories" :key="category.id">
<option :value="category.id" x-text="category.name" :selected="category.id == editBookmark.category">
</template>
</select>
</div>
<div class="field textfield">
<label for="bookmarknewcategory">New category</label>
<input id="bookmarknewcategory" type="text" x-model="editBookmark.newcategory">
</div>
<div class="field buttons">
<button @click="saveBookmark()">Save</button>
</div>
<div class="field buttons">
<button @click="deleteBookmark()">Delete</button>
</div>
</div>
</template>
</div>
</div>
</main>