From 3952937581941bda99f0bc80241e3058d5168ca7 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Tue, 8 Apr 2025 12:26:46 -0500 Subject: [PATCH] gif picker --- src/webpage/icons/gif.svg | 1 + src/webpage/index.html | 1 + src/webpage/index.ts | 8 +++ src/webpage/localuser.ts | 113 +++++++++++++++++++++++++++++++++++++- src/webpage/message.ts | 6 +- src/webpage/style.css | 85 ++++++++++++++++++++++++++-- translations/en.json | 1 + 7 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 src/webpage/icons/gif.svg diff --git a/src/webpage/icons/gif.svg b/src/webpage/icons/gif.svg new file mode 100644 index 0000000..ccfb358 --- /dev/null +++ b/src/webpage/icons/gif.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/webpage/index.html b/src/webpage/index.html index 55ef721..f15bb36 100644 --- a/src/webpage/index.html +++ b/src/webpage/index.html @@ -96,6 +96,7 @@
+
diff --git a/src/webpage/index.ts b/src/webpage/index.ts index 49ae8d2..fe28af6 100644 --- a/src/webpage/index.ts +++ b/src/webpage/index.ts @@ -275,4 +275,12 @@ import {I18n} from "./i18n.js"; thisUser.TBEmojiMenu(emojiTB.getBoundingClientRect()); }; emojiTB.onclick = (e) => e.stopImmediatePropagation(); + + const gifTB = document.getElementById("gifTB") as HTMLElement; + gifTB.onmousedown = (e) => { + e.preventDefault(); + e.stopImmediatePropagation(); + thisUser.makeGifBox(gifTB.getBoundingClientRect()); + }; + gifTB.onclick = (e) => e.stopImmediatePropagation(); })(); diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index d653a00..92e535a 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -31,6 +31,7 @@ import {Message} from "./message.js"; import {badgeArr} from "./Dbadges.js"; import {Rights} from "./rights.js"; import {Contextmenu} from "./contextmenu.js"; +import {Search} from "./search.js"; const wsCodesRetry = new Set([4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]); interface CustomHTMLDivElement extends HTMLDivElement { @@ -2238,12 +2239,122 @@ class Localuser { this.search(document.getElementById("searchOptions") as HTMLDivElement, typeMd, str, pre); }; } + async makeGifBox(rect: DOMRect) { + interface fullgif { + id: string; + title: string; + url: string; + src: string; + gif_src: string; + width: number; + height: number; + preview: string; + } + const menu = document.createElement("div"); + menu.classList.add("flexttb", "gifmenu"); + menu.style.bottom = window.innerHeight - rect.top + 15 + "px"; + menu.style.right = window.innerWidth - rect.right + "px"; + document.body.append(menu); + Contextmenu.keepOnScreen(menu); + if (Contextmenu.currentmenu !== "") { + Contextmenu.currentmenu.remove(); + } + Contextmenu.currentmenu = menu; + const trending = (await ( + await fetch( + this.info.api + "/gifs/trending?" + new URLSearchParams([["locale", I18n.lang]]), + {headers: this.headers}, + ) + ).json()) as { + categories: { + name: string; + src: string; + }[]; + gifs: [fullgif]; + }; + const gifbox = document.createElement("div"); + gifbox.classList.add("gifbox"); + const search = document.createElement("input"); + let gifs = gifbox; + const searchBox = async () => { + gifs.remove(); + if (search.value === "") { + menu.append(gifbox); + gifs = gifbox; + return; + } + gifs = document.createElement("div"); + gifs.classList.add("gifbox"); + menu.append(gifs); + const sValue = search.value; + const gifReturns = (await ( + await fetch( + this.info.api + + "/gifs/search?" + + new URLSearchParams([ + ["locale", I18n.lang], + ["q", sValue], + ["limit", "500"], + ]), + {headers: this.headers}, + ) + ).json()) as fullgif[]; + if (sValue !== search.value) { + return; + } + for (const gif of gifReturns) { + const div = document.createElement("div"); + div.classList.add("gifBox"); + const img = document.createElement("img"); + img.src = gif.gif_src; + img.alt = gif.title; + const scale = gif.width / 196; + + img.width = gif.width / scale; + img.height = gif.height / scale; + div.append(img); + gifs.append(div); + div.onclick = () => { + if (this.channelfocus) { + this.channelfocus.sendMessage(gif.url, {embeds: [], attachments: [], replyingto: null}); + menu.remove(); + } + }; + } + }; + let last = ""; + search.onkeyup = () => { + if (last === search.value) { + return; + } + last = search.value; + searchBox(); + }; + search.classList.add("searchGifBar"); + search.placeholder = I18n.searchGifs(); + for (const category of trending.categories) { + const div = document.createElement("div"); + div.classList.add("gifPreviewBox"); + const img = document.createElement("img"); + img.src = category.src; + const title = document.createElement("span"); + title.textContent = category.name; + div.append(img, title); + gifbox.append(div); + div.onclick = (e) => { + e.stopImmediatePropagation(); + search.value = category.name; + searchBox(); + }; + } + menu.append(search, gifbox); + search.focus(); + } async TBEmojiMenu(rect: DOMRect) { const typebox = document.getElementById("typebox") as CustomHTMLDivElement; const p = saveCaretPosition(typebox); if (!p) return; const original = MarkDown.getText(); - console.log(original); const emoji = await Emoji.emojiPicker( -0 + rect.right - window.innerWidth, diff --git a/src/webpage/message.ts b/src/webpage/message.ts index 733d955..1a4fb44 100644 --- a/src/webpage/message.ts +++ b/src/webpage/message.ts @@ -663,8 +663,10 @@ class Message extends SnowFlake { } else { this.content.onUpdate = () => {}; const messaged = this.content.makeHTML(); - messagedwrap.classList.add("flexttb"); - messagedwrap.appendChild(messaged); + if (!this.embeds.find((_) => _.json.url === messaged.textContent)) { + messagedwrap.classList.add("flexttb"); + messagedwrap.appendChild(messaged); + } } text.appendChild(messagedwrap); build.appendChild(text); diff --git a/src/webpage/style.css b/src/webpage/style.css index d8f3870..3f86bb5 100644 --- a/src/webpage/style.css +++ b/src/webpage/style.css @@ -355,6 +355,9 @@ textarea { .svg-emoji { mask: url(/icons/emoji.svg); } +.svg-gif { + mask: url(/icons/gif.svg); +} .svg-edit { mask: url(/icons/edit.svg); } @@ -433,11 +436,19 @@ textarea { aspect-ratio: 1/1; flex-shrink: 0; } -#emojiTB{ - width:.2in; - height:.2in; +#emojiTB { + width: 0.2in; + height: 0.2in; cursor: pointer; flex-shrink: 0; + margin-left: 6px; +} +#gifTB { + width: 0.2in; + height: 0.2in; + cursor: pointer; + flex-shrink: 0; + mask-size: 0.2in 0.2in; } .selectarrow { position: absolute; @@ -1158,7 +1169,7 @@ span.instanceStatus { flex-shrink: 1; text-wrap: auto; overflow-y: auto; - margin-right: .03in; + margin-right: 0.03in; } .outerTypeBox { max-height: 50svh; @@ -2395,7 +2406,8 @@ fieldset input[type="radio"] { background: var(--primary-bg); transition: left 0.3s; } - #sideContainDiv, #sideContainDiv.searchDiv { + #sideContainDiv, + #sideContainDiv.searchDiv { display: block; right: -100svw; width: 100svw; @@ -2424,7 +2436,8 @@ fieldset input[type="radio"] { #page:has(#maintoggle:checked) #mainarea { left: 0; } - #page:has(#memberlisttoggle:checked) #sideContainDiv, #sideContainDiv.searchDiv { + #page:has(#memberlisttoggle:checked) #sideContainDiv, + #sideContainDiv.searchDiv { right: 0; } #page:has(#maintoggle:checked) #maintoggleicon { @@ -2631,3 +2644,63 @@ fieldset input[type="radio"] { right: 0.2in; cursor: pointer; } + +.gifmenu { + position: absolute; + width: 4.5in; + height: 5in; + background: var(--secondary-bg); + border-radius: 8px; +} +.gifPreviewBox { + position: relative; + width: 2in; + margin-bottom: 10px; + border-radius: 7px; + overflow: hidden; + cursor: pointer; + + img { + width: 2in; + height: 1in; + object-fit: cover; + } + span { + top: 0px; + left: 0px; + position: absolute; + width: 100%; + height: 100%; + display: inline-flex; + align-items: center; + justify-content: center; + background: #00000099; + font-weight: bold; + } +} +.gifbox { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-around; + overflow-y: auto; + margin: 0.1in; + align-items: center; +} +.searchGifBar { + height: 0.3in; + margin: 0.15in 0.15in 0 0.15in; + flex-shrink: 0; + background: var(--black); + border: none; + border-radius: 4px; + font-size: 0.2in; + padding: 0 0.1in; +} +.gifBox { + img { + max-width: 196px; + } + cursor: pointer; + cursor: p; +} diff --git a/translations/en.json b/translations/en.json index 77a6fbb..89fac01 100644 --- a/translations/en.json +++ b/translations/en.json @@ -149,6 +149,7 @@ "name": "Accessibility", "roleColors": "Disable role colors" }, + "searchGifs": "Search Tenor", "channel": { "creating": "Creating channel", "name": "Channel",