From ea5388481a8203b2eb0ab2696fa3e1a361011d44 Mon Sep 17 00:00:00 2001 From: a0eoc <42794343+a0eoc@users.noreply.github.com> Date: Tue, 12 Sep 2023 23:40:51 +0500 Subject: [PATCH 01/14] feat(ui): don't display copy button if no js --- web/typescript/page/view-style.ts | 1 + web/views/style/view.tmpl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/web/typescript/page/view-style.ts b/web/typescript/page/view-style.ts index 5a2c95d4..a417cd25 100644 --- a/web/typescript/page/view-style.ts +++ b/web/typescript/page/view-style.ts @@ -11,6 +11,7 @@ function shareButton() { if (!shareButton) { return; } + shareButton.removeAttribute("hidden"); shareButton.addEventListener('click', () => { navigator.clipboard.writeText(urlValue).then(() => { shareButton.classList.add('copied'); diff --git a/web/views/style/view.tmpl b/web/views/style/view.tmpl index f46bc9fa..d24dec8f 100644 --- a/web/views/style/view.tmpl +++ b/web/views/style/view.tmpl @@ -50,7 +50,7 @@ From cd05202718589fe1567da67a32fbf0ffa4223cfe Mon Sep 17 00:00:00 2001 From: a0eoc <42794343+a0eoc@users.noreply.github.com> Date: Wed, 13 Sep 2023 23:14:45 +0500 Subject: [PATCH 02/14] feat(ui): don't display share slug for nojs URL isn't truncated for me for "https://userstyles.world/style/60000". We may want to tweak max-width for .share #share later. --- handlers/style/view.go | 2 +- web/typescript/page/view-style.ts | 5 +++-- web/views/style/view.tmpl | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/handlers/style/view.go b/handlers/style/view.go index 2582a99e..5b76809e 100644 --- a/handlers/style/view.go +++ b/handlers/style/view.go @@ -50,7 +50,7 @@ func GetStylePage(c *fiber.Ctx) error { "User": u, "Title": data.Name, "Style": data, - "URL": c.BaseURL() + c.Path(), + "URL": c.BaseURL() + "/style/" + id, "Slug": slug, "Canonical": "style/" + id + "/" + slug, "RenderMeta": true, diff --git a/web/typescript/page/view-style.ts b/web/typescript/page/view-style.ts index a417cd25..408bca3f 100644 --- a/web/typescript/page/view-style.ts +++ b/web/typescript/page/view-style.ts @@ -6,14 +6,15 @@ export const initViewStyle = () => doDomOperation(() => { }); function shareButton() { - const urlValue = document.getElementById('share').textContent; + const urlBar = document.getElementById('share'); const shareButton = document.getElementById('btn-share') as HTMLButtonElement; if (!shareButton) { return; } + urlBar.textContent += urlBar.getAttribute("slug"); shareButton.removeAttribute("hidden"); shareButton.addEventListener('click', () => { - navigator.clipboard.writeText(urlValue).then(() => { + navigator.clipboard.writeText(urlBar.textContent).then(() => { shareButton.classList.add('copied'); }, () => { shareButton.classList.add('copied-failed'); diff --git a/web/views/style/view.tmpl b/web/views/style/view.tmpl index d24dec8f..eb970627 100644 --- a/web/views/style/view.tmpl +++ b/web/views/style/view.tmpl @@ -46,7 +46,7 @@
- {{ .URL }} + {{ .URL }}
From eca8b9f99d948bc180517b2ebc7fdac481bb8ab3 Mon Sep 17 00:00:00 2001 From: vednoc Date: Mon, 21 Aug 2023 05:43:24 +0200 Subject: [PATCH 04/14] fix(search): remove substring functionality As it turns out, it caused more weirdness than I imagined. Therefore, let's see where things go without substring search. If that doesn't improve the status quo either, we'll go back to the drawing board. To simulate substring search, you can use a prefix with an asterisk. For example, looking up all Git forges can be done with `git*`, which will return all userstyles that contain a word starting with "git". However, using `-` still has the same behavior. To work around it, wrap it with quotes, like `"dark-"`, which will return all userstyles that contain "dark-" literally. Both of these can be used to target specific columns: name, description, notes, categories (added in a commit from the future), as well as other columns we'll add later on (if we're happy with this approach). Fix #233 --- handlers/core/search.go | 4 ---- models/search.go | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/handlers/core/search.go b/handlers/core/search.go index e7a458e4..b51fe5c8 100644 --- a/handlers/core/search.go +++ b/handlers/core/search.go @@ -19,10 +19,6 @@ func Search(c *fiber.Ctx) error { c.Locals("Canonical", "search") keyword := strings.TrimSpace(c.Query("q")) - if len(keyword) < 3 { - c.Locals("Error", "Keywords need to be in a group of three or more characters.") - return c.Render("core/search", fiber.Map{}) - } c.Locals("Keyword", keyword) page, err := models.IsValidPage(c.Query("page")) diff --git a/models/search.go b/models/search.go index 1392b8a8..57989e1f 100644 --- a/models/search.go +++ b/models/search.go @@ -5,7 +5,7 @@ import "userstyles.world/modules/database" func InitStyleSearch() error { init := ` DROP TABLE IF EXISTS fts_styles; -CREATE VIRTUAL TABLE fts_styles USING FTS5(id, name, description, notes, tokenize="trigram"); +CREATE VIRTUAL TABLE fts_styles USING FTS5(id, name, description, notes); INSERT INTO fts_styles(id, name, description, notes) SELECT id, name, description, notes FROM styles; DROP TRIGGER IF EXISTS fts_styles_insert; From f9792732dbc53fe51e7354e63645bf46a9c3b09b Mon Sep 17 00:00:00 2001 From: vednoc Date: Wed, 20 Sep 2023 20:49:41 +0200 Subject: [PATCH 05/14] feat(search): add a new "category" filter Ref #238 Fix #87, #127, #128, #230 --- handlers/core/search.go | 13 +++++++++++-- models/search.go | 17 ++++++++++++----- web/views/core/search.tmpl | 11 +++++++++-- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/handlers/core/search.go b/handlers/core/search.go index b51fe5c8..4d3c05c0 100644 --- a/handlers/core/search.go +++ b/handlers/core/search.go @@ -1,6 +1,7 @@ package core import ( + "fmt" "strings" "time" @@ -21,6 +22,14 @@ func Search(c *fiber.Ctx) error { keyword := strings.TrimSpace(c.Query("q")) c.Locals("Keyword", keyword) + category := strings.TrimSpace(c.Query("category")) + c.Locals("Category", category) + + query := keyword + if category != "" { + query += fmt.Sprintf(" category:%s", category) + } + page, err := models.IsValidPage(c.Query("page")) if err != nil || page < 1 { c.Locals("Title", "Invalid page size") @@ -32,7 +41,7 @@ func Search(c *fiber.Ctx) error { t := time.Now() - total, err := storage.TotalSearchStyles(keyword, sort) + total, err := storage.TotalSearchStyles(query, sort) if err != nil { log.Database.Println(err) c.Locals("Title", "Failed to count userstyles") @@ -46,7 +55,7 @@ func Search(c *fiber.Ctx) error { } c.Locals("Pagination", p) - s, err := storage.FindSearchStyles(keyword, p.SortStyles(), page) + s, err := storage.FindSearchStyles(query, p.SortStyles(), page) if err != nil { log.Database.Println(err) c.Locals("Title", "Failed to search for userstyles") diff --git a/models/search.go b/models/search.go index 57989e1f..8f51182a 100644 --- a/models/search.go +++ b/models/search.go @@ -5,21 +5,28 @@ import "userstyles.world/modules/database" func InitStyleSearch() error { init := ` DROP TABLE IF EXISTS fts_styles; -CREATE VIRTUAL TABLE fts_styles USING FTS5(id, name, description, notes); -INSERT INTO fts_styles(id, name, description, notes) SELECT id, name, description, notes FROM styles; +CREATE VIRTUAL TABLE fts_styles USING FTS5(id, name, description, notes, category); + +INSERT INTO fts_styles(id, name, description, notes, category) +SELECT id, name, description, notes, category +FROM styles; DROP TRIGGER IF EXISTS fts_styles_insert; CREATE TRIGGER fts_styles_insert AFTER INSERT ON styles BEGIN - INSERT INTO fts_styles(id, name, description, notes) - VALUES (new.id, new.name, new.description, new.notes); + INSERT INTO fts_styles(id, name, description, notes, category) + VALUES (new.id, new.name, new.description, new.notes, new.category); END; DROP TRIGGER IF EXISTS fts_styles_update; CREATE TRIGGER fts_styles_update AFTER UPDATE ON styles BEGIN UPDATE fts_styles - SET name = new.name, description = new.description, notes = new.notes + SET + name = new.name, + description = new.description, + notes = new.notes, + category = new.category WHERE id = old.id; END; diff --git a/web/views/core/search.tmpl b/web/views/core/search.tmpl index 262125d8..36c884ea 100644 --- a/web/views/core/search.tmpl +++ b/web/views/core/search.tmpl @@ -7,13 +7,20 @@
-
+
+ + +
+ +
From bf446855e23318bc1eb57abaca438d739ef6a5b1 Mon Sep 17 00:00:00 2001 From: vednoc Date: Wed, 20 Sep 2023 20:57:16 +0200 Subject: [PATCH 06/14] fix(db): skip soft-deleted styles in search init --- models/search.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/search.go b/models/search.go index 8f51182a..479ae495 100644 --- a/models/search.go +++ b/models/search.go @@ -9,7 +9,8 @@ CREATE VIRTUAL TABLE fts_styles USING FTS5(id, name, description, notes, categor INSERT INTO fts_styles(id, name, description, notes, category) SELECT id, name, description, notes, category -FROM styles; +FROM styles +WHERE deleted_at IS NULL; DROP TRIGGER IF EXISTS fts_styles_insert; CREATE TRIGGER fts_styles_insert AFTER INSERT ON styles From e0490f38b7298e856d899a3285bee07b0d9a6975 Mon Sep 17 00:00:00 2001 From: vednoc Date: Fri, 22 Sep 2023 19:27:32 +0200 Subject: [PATCH 07/14] fix(db): remove styles from search on removal We should enforce atomicity of these actions as any one of them failing could lead to inconsistency in the database. It hasn't been a problem yet, and it would be great to keep it that way in the future. Fix #244 --- handlers/style/ban.go | 5 +++++ handlers/style/delete.go | 5 +++++ modules/storage/style_search.go | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/handlers/style/ban.go b/handlers/style/ban.go index 2ff176f4..d95f74f2 100644 --- a/handlers/style/ban.go +++ b/handlers/style/ban.go @@ -13,6 +13,7 @@ import ( "userstyles.world/modules/database" "userstyles.world/modules/email" "userstyles.world/modules/log" + "userstyles.world/modules/storage" ) func BanGet(c *fiber.Ctx) error { @@ -118,6 +119,10 @@ func BanPost(c *fiber.Ctx) error { }) } + if err = storage.DeleteSearchStyle(i); err != nil { + log.Warn.Printf("Failed to remove %d from search: %v\n", i, err) + } + if err = models.RemoveStyleCode(strconv.Itoa(int(s.ID))); err != nil { log.Warn.Printf("kind=removecode id=%v err=%q\n", s.ID, err) } diff --git a/handlers/style/delete.go b/handlers/style/delete.go index b603c93e..1671ded0 100644 --- a/handlers/style/delete.go +++ b/handlers/style/delete.go @@ -10,6 +10,7 @@ import ( "userstyles.world/modules/cache" "userstyles.world/modules/database" "userstyles.world/modules/log" + "userstyles.world/modules/storage" ) func DeleteGet(c *fiber.Ctx) error { @@ -87,6 +88,10 @@ func DeletePost(c *fiber.Ctx) error { }) } + if err = storage.DeleteSearchStyle(i); err != nil { + log.Warn.Printf("Failed to remove %d from search: %v\n", i, err) + } + if err = models.RemoveStyleCode(strconv.Itoa(int(s.ID))); err != nil { log.Warn.Printf("kind=removecode id=%v err=%q\n", s.ID, err) } diff --git a/modules/storage/style_search.go b/modules/storage/style_search.go index ee2fbb29..8c51cad2 100644 --- a/modules/storage/style_search.go +++ b/modules/storage/style_search.go @@ -101,3 +101,8 @@ MATCH ?`) return s, nil } + +// DeleteSearchStyle removes a style from FTS table. +func DeleteSearchStyle(id int) error { + return database.Conn.Exec("DELETE FROM fts_styles WHERE id = ?", id).Error +} From c05af104c7366ba7eabd28c71a12e91eda14ea0e Mon Sep 17 00:00:00 2001 From: vednoc Date: Fri, 22 Sep 2023 20:04:22 +0200 Subject: [PATCH 08/14] chore(db): add migration for new search tables --- modules/database/init/init.go | 15 --------------- modules/database/init/migrate.go | 7 +------ 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/modules/database/init/init.go b/modules/database/init/init.go index f2b25dcd..8065b8b4 100644 --- a/modules/database/init/init.go +++ b/modules/database/init/init.go @@ -110,21 +110,6 @@ func Initialize() { } } - q := "DELETE FROM fts_styles WHERE id IN (SELECT id FROM styles WHERE deleted_at IS NOT NULL)" - if err = database.Conn.Exec(q).Error; err != nil { - log.Info.Fatal(err) - } - - q = "UPDATE styles SET user_id = 1592 WHERE id = 10653" - if err = database.Conn.Debug().Exec(q).Error; err != nil { - log.Info.Fatal(err) - } - - var h models.History - if err = database.Conn.Migrator().AutoMigrate(h); err != nil { - log.Info.Fatal(err) - } - if shouldSeed { seed() } diff --git a/modules/database/init/migrate.go b/modules/database/init/migrate.go index 43aaccb8..4a9ca220 100644 --- a/modules/database/init/migrate.go +++ b/modules/database/init/migrate.go @@ -18,12 +18,7 @@ func runMigration(db *gorm.DB) { // Wrap in a transaction to allow rollbacks. db.Transaction(func(tx *gorm.DB) error { - var l models.Log - if err := tx.Migrator().AddColumn(l, "Message"); err != nil { - log.Database.Fatalf("Failed to add column message: %s\n", err) - } - - return nil + return models.InitStyleSearch() }) log.Database.Printf("Done in %s.\n", time.Since(t).Round(time.Microsecond)) From 42c0ec73c41d75b0ebd656e23a00784e4f32529d Mon Sep 17 00:00:00 2001 From: a0eoc <42794343+a0eoc@users.noreply.github.com> Date: Sat, 23 Sep 2023 10:39:17 +0500 Subject: [PATCH 09/14] fix(search): always show sort if there are styels --- web/views/core/search.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/views/core/search.tmpl b/web/views/core/search.tmpl index 36c884ea..dc90778b 100644 --- a/web/views/core/search.tmpl +++ b/web/views/core/search.tmpl @@ -30,7 +30,7 @@ >{{ template "icons/search" }} Search
- {{ if and .Keyword .Styles }} + {{ if .Styles }} {{ template "partials/form-sort" . }} {{ end }} From 9001f47ec99095b7b29467af0279466f92e23ef8 Mon Sep 17 00:00:00 2001 From: a0eoc <42794343+a0eoc@users.noreply.github.com> Date: Sat, 23 Sep 2023 10:39:51 +0500 Subject: [PATCH 10/14] feat(ui): remove "Please try searching..." --- web/views/core/search.tmpl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/views/core/search.tmpl b/web/views/core/search.tmpl index dc90778b..d63b014a 100644 --- a/web/views/core/search.tmpl +++ b/web/views/core/search.tmpl @@ -53,10 +53,6 @@ {{ else }} {{ if .Error }} - {{ else }} -
-

Please try searching for something.

-
{{ end }}

Suggestions