From bfe025d321fe957a7365afc78ce2eff72406fde5 Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 8 Sep 2022 22:02:55 +0600 Subject: [PATCH] ui revamp (3.5) --- package.json | 2 +- src/front/cobalt.css | 89 +++++++++++++++++++-------- src/front/cobalt.js | 56 ++++++++++++++--- src/front/emoji/clipboard.svg | 10 +++ src/front/emoji/dragon_face_wukko.svg | 11 ++++ src/localization/changelog.json | 4 +- src/localization/languages/en.json | 4 +- src/localization/languages/ru.json | 6 +- src/modules/emoji.js | 5 +- src/modules/pageRender/elements.js | 14 ++++- src/modules/pageRender/page.js | 52 +++++++++++----- 11 files changed, 191 insertions(+), 62 deletions(-) create mode 100644 src/front/emoji/clipboard.svg create mode 100644 src/front/emoji/dragon_face_wukko.svg diff --git a/package.json b/package.json index 05598d4..2bd6a39 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cobalt", "description": "save what you love", - "version": "3.4.3", + "version": "3.5", "author": "wukko", "exports": "./src/cobalt.js", "type": "module", diff --git a/src/front/cobalt.css b/src/front/cobalt.css index e879e33..58459c1 100644 --- a/src/front/cobalt.css +++ b/src/front/cobalt.css @@ -154,7 +154,6 @@ input[type="checkbox"] { width: 60%; height: auto; display: inline-flex; - padding: 3rem; } #logo-area { padding-right: 3rem; @@ -164,10 +163,22 @@ input[type="checkbox"] { white-space: nowrap; } #download-area { + display: flex; + flex-direction: column; + width: 100%; +} +#cobalt-main-box #top { display: inline-flex; height: 2rem; - width: 100%; margin-top: -0.6rem; + flex-direction: row; +} +#cobalt-main-box #bottom { + padding-top: 1.5rem; +} +#cobalt-main-box #bottom button { + width: auto!important; + padding: 0.6rem 1.2rem!important; } .box { background: var(--background); @@ -182,8 +193,17 @@ input[type="checkbox"] { border: 0; float: right; border-bottom: 0.1rem solid var(--accent-unhover); - transition: border-bottom 0.2s; outline: none; + font-size: 0.8rem; +} +#url-clear { + background: none; + padding: 0 1.1rem; + font-size: 1rem; + transform: none; + line-height: 0rem; + height: 1.6rem; + margin-top: .4rem; } #url-input-area:focus { outline: none; @@ -194,10 +214,10 @@ input[type="checkbox"] { color: var(--accent); background: none; border: none; - font-size: 1.4rem; + font-size: 1.6rem; cursor: pointer; padding: 0; - letter-spacing: -0.1rem; + letter-spacing: -0.36rem; } #download-button:disabled { color: var(--accent-unhover); @@ -210,25 +230,19 @@ input[type="checkbox"] { transform: translate(-50%, -50%); font-size: 0.9rem; text-align: center; - width: 90%; + width: auto; } -#footer-buttons { - display: inline-flex; +#footer-buttons, .footer-pair { + display: flex; + flex-direction: row; align-items: center; } .footer-button { - cursor: pointer; + width: auto!important; color: var(--accent-unhover-2); - border: 0.15rem solid var(--accent-unhover-2); - padding: 0.4rem 0.8rem 0.5rem; + padding: 0.6rem 1.2rem!important; margin: 0.4rem; - display: flex; align-content: center; - align-items: center; -} -.footer-button:hover { - color: var(--accent); - border: var(--border-15); } .text-backdrop { background: var(--accent); @@ -290,7 +304,8 @@ input[type="checkbox"] { padding-bottom: 1rem; } #popup-desc, -#desc-error { +#desc-error, +#popup-info-desc { width: 100%; text-align: left; float: left; @@ -493,8 +508,8 @@ input[type="checkbox"] { padding-top: 0!important; padding-bottom: 1rem; } -#popup-imagePicker #popup-buttons { - padding-top: 1rem; +.popup-tabs { + padding-top: 0.5rem; } /* adapt the page according to screen size */ @media screen and (min-width: 2300px) { @@ -563,14 +578,9 @@ input[type="checkbox"] { } /* mobile page */ @media screen and (max-width: 949px) { - #cobalt-main-box { + #cobalt-main-box, #footer { width: 85%; } - #close-error { - bottom: 5%; - position: absolute; - width: var(--without-padding); - } } @media screen and (max-width: 475px) { .tab { @@ -579,11 +589,36 @@ input[type="checkbox"] { .tab .emoji { margin-right: 0; } - #cobalt-main-box { + #cobalt-main-box, #footer { width: 90%; } } +@media screen and (max-width: 320px) { + .footer-button { + font-size: 0; + } + #cobalt-main-box #bottom button { + font-size: 0; + } + .footer-button .emoji, #cobalt-main-box #bottom button .emoji { + margin-right: 0; + } +} @media screen and (max-width: 949px) { + #cobalt-main-box #bottom button { + width: 100%!important; + } + #footer { + bottom: 2rem; + transform: translate(-50%, 0%); + } + #footer-buttons { + flex-direction: column; + align-items: stretch; + } + .footer-pair .footer-button { + width: 100%!important; + } .imagepicker-image-container { height: 7rem; width: 7rem; diff --git a/src/front/cobalt.js b/src/front/cobalt.js index 2066767..b4055bd 100644 --- a/src/front/cobalt.js +++ b/src/front/cobalt.js @@ -1,5 +1,7 @@ let isIOS = navigator.userAgent.toLowerCase().match("iphone os"); -let version = 6; +let isFirefox = navigator.userAgent.toLowerCase().match("firefox/"); +let version = 7; +let regex = new RegExp(/https:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/); let switchers = { "theme": ["auto", "light", "dark"], @@ -7,7 +9,8 @@ let switchers = { "quality": ["max", "hig", "mid", "low"], "audioFormat": ["best", "mp3", "ogg", "wav", "opus"] } -let checkboxes = ["disableTikTokWatermark", "fullTikTokAudio"] +let checkboxes = ["disableTikTokWatermark", "fullTikTokAudio"]; +if (!isFirefox) checkboxes.push("disableClipboardButton"); let exceptions = { // used solely for ios devices, because they're less capable than everything else. "ytFormat": "mp4", "audioFormat": "mp3" @@ -65,8 +68,17 @@ document.addEventListener("keydown", (event) => { } }) function button() { - let regex = /https:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/.test(eid("url-input-area").value); - regex ? changeDownloadButton(1, '>>') : changeDownloadButton(0, '>>'); + let regexTest = regex.test(eid("url-input-area").value); + if ((eid("url-input-area").value).length > 0) { + eid("url-clear").style.display = "block"; + } else { + eid("url-clear").style.display = "none"; + } + regexTest ? changeDownloadButton(1, '>>') : changeDownloadButton(0, '>>'); +} +function clearInput() { + eid("url-input-area").value = ''; + button(); } function copy(id, data) { let e = document.getElementById(id); @@ -106,6 +118,7 @@ function hideAllPopups() { eid("popup-backdrop").style.visibility = "hidden"; } function popup(type, action, text) { + if (action == 1) hideAllPopups(); eid("popup-backdrop").style.visibility = vis(action); switch (type) { case "about": @@ -170,16 +183,18 @@ function changeSwitcher(li, b) { } function internetError() { eid("url-input-area").disabled = false - changeDownloadButton(2, '!!') + changeDownloadButton(2, '!!'); popup("error", 1, loc.noInternet); } function checkbox(action) { if (eid(action).checked) { sSet(action, "true"); if (action == "alwaysVisibleButton") button(); + if (action == "disableClipboardButton") eid("pasteFromClipboard").style.display = "none"; } else { sSet(action, "false"); if (action == "alwaysVisibleButton") button(); + if (action == "disableClipboardButton") eid("pasteFromClipboard").style.display = "flex"; } } function updateToggle(toggl, state) { @@ -188,7 +203,7 @@ function updateToggle(toggl, state) { eid(toggl).innerHTML = loc.toggleAudio; break; case "false": - eid(toggl).innerHTML = sGet(`${toggl}ToggledOnce`) == "true" ? loc.toggleDefault : loc.pressToChange + loc.toggleDefault; + eid(toggl).innerHTML = loc.toggleDefault; break; } } @@ -203,6 +218,7 @@ function toggle(toggl) { updateToggle(toggl, sGet(toggl)) } function loadSettings() { + if (sGet("disableClipboardButton") == "true") eid("pasteFromClipboard").style.display = "none"; if (sGet("alwaysVisibleButton") == "true") { eid("alwaysVisibleButton").checked = true; eid("download-button").value = '>>' @@ -222,8 +238,17 @@ function loadSettings() { changeSwitcher(i, sGet(i)) } } +async function pasteClipboard() { + let t = await navigator.clipboard.readText(); + if (regex.test(t)) { + eid("url-input-area").value = t; + download(eid("url-input-area").value); + } + button(); +} async function download(url) { changeDownloadButton(2, '...'); + eid("url-clear").style.display = "none"; eid("url-input-area").disabled = true; let audioMode = sGet("audioMode"); let format = ``; @@ -246,7 +271,8 @@ async function download(url) { case "redirect": changeDownloadButton(2, '>>>') setTimeout(() => { - changeDownloadButton(1, '>>') + changeDownloadButton(1, '>>'); + eid("url-clear").style.display = "block"; eid("url-input-area").disabled = false }, 3000) if (sGet("downloadPopup") == "true") { @@ -271,29 +297,34 @@ async function download(url) { window.location.href = j.url } setTimeout(() => { - changeDownloadButton(1, '>>') + changeDownloadButton(1, '>>'); + eid("url-clear").style.display = "block"; eid("url-input-area").disabled = false }, 5000) } else { eid("url-input-area").disabled = false - changeDownloadButton(2, '!!') + changeDownloadButton(2, '!!'); + eid("url-clear").style.display = "block"; popup("error", 1, jp.text); } }).catch((error) => internetError()); break; default: eid("url-input-area").disabled = false - changeDownloadButton(2, '!!') + changeDownloadButton(2, '!!'); + eid("url-clear").style.display = "block"; popup("error", 1, loc.unknownStatus); break; } } else { eid("url-input-area").disabled = false + eid("url-clear").style.display = "block"; changeDownloadButton(2, '!!') popup("error", 1, loc.noURLReturned); } } else { eid("url-input-area").disabled = false + eid("url-clear").style.display = "block"; changeDownloadButton(2, '!!') popup("error", 1, j.text); } @@ -318,9 +349,14 @@ window.onload = () => { button(); } } +eid("url-input-area").addEventListener("keydown", (event) => { + if (event.key === 'Escape') eid("url-input-area").value = ''; + button(); +}) eid("url-input-area").addEventListener("keyup", (event) => { if (event.key === 'Enter') eid("download-button").click(); }) document.onkeydown = (event) => { + if (event.key == "Tab" || event.ctrlKey) eid("url-input-area").focus(); if (event.key === 'Escape') hideAllPopups(); }; \ No newline at end of file diff --git a/src/front/emoji/clipboard.svg b/src/front/emoji/clipboard.svg new file mode 100644 index 0000000..600e532 --- /dev/null +++ b/src/front/emoji/clipboard.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/front/emoji/dragon_face_wukko.svg b/src/front/emoji/dragon_face_wukko.svg new file mode 100644 index 0000000..c389f4c --- /dev/null +++ b/src/front/emoji/dragon_face_wukko.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/localization/changelog.json b/src/localization/changelog.json index 9a6fde2..f19eb87 100644 --- a/src/localization/changelog.json +++ b/src/localization/changelog.json @@ -1,5 +1,5 @@ { - "ContentTitle": "tiktok images and better localization (3.4)", - "Content": "- added ability to save images from tiktok conveniently, and without watermarks.\n- it's now way easier to contribute translations to cobalt. read more on how to do it on github. in short, you don't need to fork the repo anymore, everything is handled through crowdin :D\n- updated readme in github repo to make it easier to read and understand.\n- began to add more descriptive errors, more to come soon.\n\ninternal stuff:\n- remade entirety of tiktok module and merged it with douyin one. now both (basically identical) platforms have perfect parity of download features.\n- cleaned up the twitter module, now it's way more compact and easy to read.\n- moved changelog out of english localization.\n- other small improvements and fixes.", + "ContentTitle": "ui revamp and usability imporvements (3.5)", + "Content": "new features:\n- cobalt now lets you paste the link in your clipboard and download the file in a single press of a button. if content in your clipboard isn't a valid url, cobalt won't process or paste it. you can also hide it in settings if you want to.\nunfortunately, the clipboard button is not visible to firefox users because mozilla didn't add proper support for clipboard api.\n\n- there's now a button to quickly clean the input area, right next to download button. added it just in case you want to quickly save a bunch of videos and don't want to bother selecting text (personally my favorite new feature).\n\n- keyboard shortcuts! you love them, i love them, and now we can use them to perform quick actions in cobalt. use ctrl+v combo to paste the link without focusing the input area and esc key to close the active popup or clean the input area. and if you didn't know, you can also press enter to download content from the link.\n\nnew looks:\n- main box has been revamped. it has lost its border, thick padding, and now feels light and fresh.\n\n- download button is now prettier, and has been tuned to make >> look just like the logo.\n\n- buttons on the bottom now actually look like buttons and are way more descriptive. no more #@+?$ bullshit. it's way easier to see and understand what each of them does.\n\n- bottom buttons are also prettier and easier to use on a phone. they're bigger and stretch out to sides, making them easier to press.\n\nfixes:\n\n- it's now impossible to overlap multiple popups at once. no more mess if you decide to explore popups while waiting for request to process.\n\n- popup tabs have been slightly moved down in order to prevent popup content overlapping.\n\n- ui scalability has been improved.", "FollowTwitter": "follow cobalt's twitter account for polls, updates, and more: @justusecobalt" } \ No newline at end of file diff --git a/src/localization/languages/en.json b/src/localization/languages/en.json index 97f9529..ba42ce0 100644 --- a/src/localization/languages/en.json +++ b/src/localization/languages/en.json @@ -96,6 +96,8 @@ "ImagePickerExplanationPC": "right click an image to save it.", "ImagePickerExplanationPhone": "press and hold an image to save it.", "ErrorNoUrlReturned": "server didn't return a download link. this should never happen. reload the page and try again, but if it doesn't help, {ContactLink}.", - "ErrorUnknownStatus": "i received a response i can't process. most likely something with status is wrong. this should never happen. reload the page and try again, but if it doesn't help, {ContactLink}." + "ErrorUnknownStatus": "i received a response i can't process. most likely something with status is wrong. this should never happen. reload the page and try again, but if it doesn't help, {ContactLink}.", + "PasteFromClipboard": "paste from clipboard", + "SettingsDisableClipboard": "hide clipboard button" } } diff --git a/src/localization/languages/ru.json b/src/localization/languages/ru.json index dd49cfb..a08dc01 100644 --- a/src/localization/languages/ru.json +++ b/src/localization/languages/ru.json @@ -82,7 +82,7 @@ "SettingsAudioFormatDescription": "когда выбран \"лучший\" формат, ты получишь аудио максимально возможного качества, так как оно останется в оригинальном формате. если же выбрано что-то другое, то аудио будет немного сжато.", "Keyphrase": "сохраняй то, что любишь", "SettingsDisableChangelogOnUpdate": "не показывать изменения после обновлений", - "SettingsRemoveWatermark": "убрать ватермарку", + "SettingsRemoveWatermark": "выключить ватермарку", "ErrorPopupCloseButton": "ясно", "ModeToggle": "режим", "ModeToggleSmart": "умный", @@ -96,6 +96,8 @@ "ImagePickerExplanationPC": "нажми правой кнопкой мыши на изображение, чтобы его сохранить.", "ImagePickerExplanationPhone": "зажми и удерживай изображение, чтобы его сохранить.", "ErrorNoUrlReturned": "я не получил ссылку для скачивания от сервера. такого происходить не должно. перезагрузи страницу, а если не поможет, то {ContactLink}.", - "ErrorUnknownStatus": "сервер ответил мне чем-то непонятным. такого происходить не должно. перезагрузи страницу, а если не поможет, то {ContactLink}." + "ErrorUnknownStatus": "сервер ответил мне чем-то непонятным. такого происходить не должно. перезагрузи страницу, а если не поможет, то {ContactLink}.", + "PasteFromClipboard": "вставить из буфера обмена", + "SettingsDisableClipboard": "скрыть кнопку буфера обмена" } } diff --git a/src/modules/emoji.js b/src/modules/emoji.js index e4ddded..7b229b1 100644 --- a/src/modules/emoji.js +++ b/src/modules/emoji.js @@ -7,10 +7,11 @@ const names = { "✨": "sparkles", "🪅": "pinata", "🪄": "magic_wand", - "🐲": "dragon_face", + "🐲": "dragon_face_wukko", "💸": "money_with_wings", "⚙️": "gear", - "☹️": "frowning_face" + "☹️": "frowning_face", + "📋": "clipboard" } let sizing = { 22: 0.4, diff --git a/src/modules/pageRender/elements.js b/src/modules/pageRender/elements.js index 62a2138..0e77e26 100644 --- a/src/modules/pageRender/elements.js +++ b/src/modules/pageRender/elements.js @@ -105,8 +105,18 @@ export function settingsCategory(obj) { export function footerButtons(obj) { let items = `` for (let i = 0; i < obj.length; i++) { - let func = `${obj[i]["type"] == "toggle" ? `toggle('${obj[i]["name"]}')` : `popup('${obj[i]["name"]}', 1)`}`; - items += ``; + switch (obj[i]["type"]) { + case "toggle": + items += ``; + break; + case "action": + items += ``; + break; + case "popup": + items += ``; + i++; + break; + } } return ` ` diff --git a/src/modules/pageRender/page.js b/src/modules/pageRender/page.js index cbef565..290abdd 100644 --- a/src/modules/pageRender/page.js +++ b/src/modules/pageRender/page.js @@ -28,10 +28,11 @@ for (let i in donations["crypto"]) { extr = ' extra' } export default function(obj) { - audioFormats[0]["text"] = loc(obj.lang, 'SettingsAudioFormatBest') - let ua = obj.useragent.toLowerCase() - let isIOS = ua.match("iphone os") - let isMobile = ua.match("android") || ua.match("iphone os") + audioFormats[0]["text"] = loc(obj.lang, 'SettingsAudioFormatBest'); + let ua = obj.useragent.toLowerCase(); + let isIOS = ua.match("iphone os"); + let isMobile = ua.match("android") || ua.match("iphone os"); + let isFirefox = ua.match("firefox/"); try { return ` @@ -239,7 +240,10 @@ export default function(obj) { "text": loc(obj.lang, 'SettingsThemeLight') }] }) - }) + checkbox("alwaysVisibleButton", loc(obj.lang, 'SettingsKeepDownloadButton'), loc(obj.lang, 'AccessibilityKeepDownloadButton')) + checkbox("disableChangelog", loc(obj.lang, 'SettingsDisableChangelogOnUpdate'), loc(obj.lang, 'SettingsDisableChangelogOnUpdate')) + }) + + checkbox("alwaysVisibleButton", loc(obj.lang, 'SettingsKeepDownloadButton'), loc(obj.lang, 'AccessibilityKeepDownloadButton')) + + checkbox("disableChangelog", loc(obj.lang, 'SettingsDisableChangelogOnUpdate'), loc(obj.lang, 'SettingsDisableChangelogOnUpdate')) + + (!isFirefox ? checkbox("disableClipboardButton", loc(obj.lang, 'SettingsDisableClipboard'), loc(obj.lang, 'SettingsDisableClipboard')) : ``) }], })} ${popup({ @@ -267,7 +271,6 @@ export default function(obj) { buttons: [`${loc(obj.lang, 'ImagePickerDownloadAudio')}`], content: '
' })} - ${popup({ name: "error", standalone: true, @@ -281,29 +284,49 @@ export default function(obj) { }, body: `
` })} + ${popup({ + name: "info", + standalone: true, + buttonOnly: true, + emoji: emoji("✨", 48, 1), + classes: ["small"], + buttonText: loc(obj.lang, 'ErrorPopupCloseButton'), + header: { + closeAria: loc(obj.lang, 'AccessibilityClosePopup'), + title: "popup title" + }, + body: `` + })} -