From 4ca90751e65fe4323a3a02da6763e49f1abdd36d Mon Sep 17 00:00:00 2001 From: ThaUnknown <6506529+ThaUnknown@users.noreply.github.com> Date: Sat, 15 Jul 2023 21:50:28 +0200 Subject: [PATCH] feat: nicer notifications --- package.json | 1 + pnpm-lock.yaml | 11 ++++ src/renderer/App.svelte | 4 +- src/renderer/components/Sidebar.svelte | 10 ++-- src/renderer/components/Toasts.svelte | 56 ------------------- src/renderer/css.css | 14 +++++ src/renderer/modules/anilist.js | 8 +-- src/renderer/modules/anime.js | 53 +++++++++--------- src/renderer/modules/providers/tosho.js | 2 + src/renderer/modules/rss.js | 14 ++--- src/renderer/modules/scroll.js | 2 +- src/renderer/views/Player/Player.svelte | 39 +++++-------- src/renderer/views/RSSView.svelte | 28 +++++----- src/renderer/views/Settings.svelte | 20 +++---- src/renderer/views/ViewAnime/ViewAnime.svelte | 10 ++-- .../views/WatchTogether/WatchTogether.svelte | 10 ++-- 16 files changed, 114 insertions(+), 168 deletions(-) delete mode 100644 src/renderer/components/Toasts.svelte diff --git a/package.json b/package.json index 4f19204..bf44270 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "svelte-keybinds": "1.0.5", "svelte-loader": "^3.1.9", "svelte-miniplayer": "1.0.3", + "svelte-sonner": "^0.1.1", "webpack": "^5.85.0", "webpack-cli": "^5.1.3", "webpack-dev-server": "^4.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 867c3ff..ca31010 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,6 +112,9 @@ devDependencies: svelte-miniplayer: specifier: 1.0.3 version: 1.0.3 + svelte-sonner: + specifier: ^0.1.1 + version: 0.1.1(svelte@4.0.4) webpack: specifier: ^5.85.0 version: 5.86.0(webpack-cli@5.1.4) @@ -5485,6 +5488,14 @@ packages: resolution: {integrity: sha512-++IvuENs/3x0SbHZ/jWNqfq+hmU/ZT73V/ETwn6TqCbHF7asXsZFxVcgyIL1lMaQMKxe227HrYghYRVNJXeFVw==} dev: true + /svelte-sonner@0.1.1(svelte@4.0.4): + resolution: {integrity: sha512-jQ/coEZvJxImxxsT5FPnHeXitGx5wQRhAkAz6C8DY8yM1qFPvFowG6mDtC0KPPDEKlHCmocbWigk6zrG27OO/Q==} + peerDependencies: + svelte: ^4.0.0 + dependencies: + svelte: 4.0.4 + dev: true + /svelte@4.0.4: resolution: {integrity: sha512-DDJavyX1mpNFLZ7jU9FwBKouemh6CJHZXwePBa5GXSaW5GuHZ361L2/1uznBqOCxu2UsUoWu8wRsB2iB8QG5sQ==} engines: {node: '>=16'} diff --git a/src/renderer/App.svelte b/src/renderer/App.svelte index 07b9ec0..c03482e 100644 --- a/src/renderer/App.svelte +++ b/src/renderer/App.svelte @@ -22,8 +22,8 @@ import ViewTrailer from './views/ViewAnime/ViewTrailer.svelte' import RSSView from './views/RSSView.svelte' import Menubar from './components/Menubar.svelte' - import Toasts from './components/Toasts.svelte' import IspBlock from './views/IspBlock.svelte' + import { Toaster, toast } from 'svelte-sonner' setContext('view', view) @@ -31,7 +31,7 @@
- +
diff --git a/src/renderer/components/Sidebar.svelte b/src/renderer/components/Sidebar.svelte index de92833..dec66e2 100644 --- a/src/renderer/components/Sidebar.svelte +++ b/src/renderer/components/Sidebar.svelte @@ -3,7 +3,7 @@ import { alID } from '@/modules/anilist.js' import { media } from '../views/Player/MediaHandler.svelte' import { platformMap } from '../views/Settings.svelte' - import { addToast } from './Toasts.svelte' + import { toast } from 'svelte-sonner' import { click } from '@/modules/click.js' const view = getContext('view') export let page @@ -17,11 +17,9 @@ } else { window.IPC.emit('open', 'https://anilist.co/api/v2/oauth/authorize?client_id=4254&response_type=token') // Change redirect_url to miru://auth if (platformMap[window.version.platform] === 'Linux') { - addToast({ - text: "If your linux distribution doesn't support custom protocol handlers, you can simply paste the full URL into the app.", - title: 'Support Notification', - type: 'secondary', - duration: '300000' + toast('Support Notification', { + description: "If your linux distribution doesn't support custom protocol handlers, you can simply paste the full URL into the app.", + duration: 300000 }) } } diff --git a/src/renderer/components/Toasts.svelte b/src/renderer/components/Toasts.svelte deleted file mode 100644 index 39ca3b2..0000000 --- a/src/renderer/components/Toasts.svelte +++ /dev/null @@ -1,56 +0,0 @@ - - -
- {#each Object.entries($toasts) as [index, toast] (index)} -
- -

{toast.title}

- {@html toast.text} -
- {/each} -
- - diff --git a/src/renderer/css.css b/src/renderer/css.css index 23e9eb7..89a9329 100644 --- a/src/renderer/css.css +++ b/src/renderer/css.css @@ -28,6 +28,20 @@ --dm-button-secondary-bg-color-hover: #ddd !important; } +[data-sonner-toaster][data-theme='dark'] { + --normal-bg: var(--dark-color) !important; + --normal-border: none !important; + --normal-text: var(--dm-base-text-color) !important; + + /* --success-bg: var(--success-color) !important; */ + --success-border: none !important; + /* --success-text: var(--lm-base-text-color) !important; */ + + /* --error-bg: hsl(358, 76%, 10%); */ + --error-border: none !important; + /* --error-text: hsl(358, 100%, 81%); */ +} + .material-symbols-outlined { font-family: "Material Symbols Outlined Variable"; font-weight: normal; diff --git a/src/renderer/modules/anilist.js b/src/renderer/modules/anilist.js index f9b81b6..70c6fa7 100644 --- a/src/renderer/modules/anilist.js +++ b/src/renderer/modules/anilist.js @@ -3,7 +3,7 @@ import { writable } from 'simple-store-svelte' import Bottleneck from 'bottleneck' import { alToken } from '../views/Settings.svelte' -import { addToast } from '../components/Toasts.svelte' +import { toast } from 'svelte-sonner' import { sleep } from './util.js' const codes = { @@ -106,10 +106,8 @@ if (alToken) { function printError (error) { console.warn(error) - addToast({ - text: /* html */`Failed making request to anilist!
Try again in a minute.
${error.status || 429} - ${error.message || codes[error.status || 429]}`, - title: 'Search Failed', - type: 'danger', + toast.error('Search Failed', { + description: `Failed making request to anilist!\nTry again in a minute.\n${error.status || 429} - ${error.message || codes[error.status || 429]}`, duration: 3000 }) } diff --git a/src/renderer/modules/anime.js b/src/renderer/modules/anime.js index eca7bdd..6544efb 100644 --- a/src/renderer/modules/anime.js +++ b/src/renderer/modules/anime.js @@ -3,7 +3,7 @@ import { DOMPARSER, PromiseBatch, binarySearch } from './util.js' import { alRequest, alSearch } from './anilist.js' import anitomyscript from 'anitomyscript' import { media } from '../views/Player/MediaHandler.svelte' -import { addToast } from '../components/Toasts.svelte' +import { toast } from 'svelte-sonner' import { view } from '@/App.svelte' import { playAnime } from '../views/RSSView.svelte' @@ -15,7 +15,17 @@ window.addEventListener('paste', ({ clipboardData }) => { // WAIT image lookup o const item = clipboardData.items[0] if (!item) return const { type } = item - if (type.startsWith('image')) return traceAnime(item.getAsFile()) + if (type.startsWith('image')) { + const promise = traceAnime(item.getAsFile()) + toast.promise(promise, { + description: 'You can also paste an URL to an image.', + loading: 'Looking up anime for image...', + success: 'Found anime for image!', + error: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.' + + }) + return + } if (!type.startsWith('text')) return item.getAsString(text => { if (torrentRx.exec(text)) { @@ -28,26 +38,19 @@ window.addEventListener('paste', ({ clipboardData }) => { // WAIT image lookup o } else if (imageRx.exec(text)) { src = text } - if (src) traceAnime(src) + if (src) { + const promise = traceAnime(src) + toast.promise(promise, { + description: 'You can also paste an URL to an image.', + loading: 'Looking up anime for image...', + success: 'Found anime for image!', + error: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.' + }) + } } }) }) export async function traceAnime (image) { // WAIT lookup logic - if (image instanceof Blob) { - const reader = new FileReader() - reader.onload = e => { - addToast({ - title: 'Looking up anime for image', - text: /* html */`You can also paste an URL to an image!
` - }) - } - reader.readAsDataURL(image) - } else { - addToast({ - title: 'Looking up anime for image', - text: /* html */`` - }) - } let options let url = `https://api.trace.moe/search?cutBorders&url=${image}` if (image instanceof Blob) { @@ -60,14 +63,12 @@ export async function traceAnime (image) { // WAIT lookup logic } const res = await fetch(url, options) const { result } = await res.json() - if (result && result[0].similarity >= 0.85) { + if (result?.[0].similarity >= 0.85) { const res = await alRequest({ method: 'SearchIDSingle', id: result[0].anilist }) view.set(res.data.Media) } else { - addToast({ - text: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.', - title: 'Search Failed', - type: 'danger' + throw new Error('Search Failed', { + message: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.' }) } } @@ -315,10 +316,8 @@ export async function resolveSeason (opts) { const obj = { media, episode: episode - offset, offset, increment, rootMedia, failed: true } if (!force) { console.warn('Error in parsing!', obj) - addToast({ - text: /* html */`Failed resolving anime episode!
${media.title.userPreferred} - ${episode - offset}`, - title: 'Parsing Error', - type: 'secondary' + toast('Parsing Error', { + description: `Failed resolving anime episode!\n${media.title.userPreferred} - ${episode - offset}` }) } return obj diff --git a/src/renderer/modules/providers/tosho.js b/src/renderer/modules/providers/tosho.js index afa7290..57a0e87 100644 --- a/src/renderer/modules/providers/tosho.js +++ b/src/renderer/modules/providers/tosho.js @@ -45,6 +45,8 @@ export default async function tosho ({ media, episode }) { found.seeders = complete } + if (!mapped?.length) throw new Error('no entries found') + return mapped } diff --git a/src/renderer/modules/rss.js b/src/renderer/modules/rss.js index 380716c..2548c0d 100644 --- a/src/renderer/modules/rss.js +++ b/src/renderer/modules/rss.js @@ -1,6 +1,6 @@ import { DOMPARSER } from '@/modules/util.js' import { set } from '@/views/Settings.svelte' -import { addToast } from '@/components/Toasts.svelte' +import { toast } from 'svelte-sonner' import { add } from '@/modules/torrent.js' import { resolveFileMedia, getEpisodeMetadataForMedia } from './anime.js' import { hasNextPage } from '@/modules/sections.js' @@ -49,19 +49,15 @@ export async function getRSSContent (url) { try { const res = await fetch(url) if (!res.ok) { - addToast({ - text: 'Failed fetching RSS!
' + res.statusText, - title: 'Search Failed', - type: 'danger' + toast.error('Search Failed', { + description: 'Failed fetching RSS!\n' + res.statusText }) console.error('Failed to fetch rss', res.statusText) } return DOMPARSER(await res.text(), 'text/xml') } catch (e) { - addToast({ - text: 'Failed fetching RSS!
' + e.message, - title: 'Search Failed', - type: 'danger' + toast.error('Search Failed', { + description: 'Failed fetching RSS!\n' + e.message }) console.error('Failed to fetch rss', e) } diff --git a/src/renderer/modules/scroll.js b/src/renderer/modules/scroll.js index 8e125fc..1612986 100644 --- a/src/renderer/modules/scroll.js +++ b/src/renderer/modules/scroll.js @@ -1,6 +1,6 @@ import { set } from '@/views/Settings.svelte' -export default function scroll (t, { speed = 120, smooth = 10 } = {}) { +export default function (t, { speed = 120, smooth = 10 } = {}) { if (!set.smoothScroll) return let moving = false let pos = 0 diff --git a/src/renderer/views/Player/Player.svelte b/src/renderer/views/Player/Player.svelte index 9d37d6d..80deab5 100644 --- a/src/renderer/views/Player/Player.svelte +++ b/src/renderer/views/Player/Player.svelte @@ -6,7 +6,7 @@ import { alEntry } from '@/modules/anilist.js' import Subtitles from '@/modules/subtitles.js' import { toTS, videoRx, fastPrettyBytes } from '@/modules/util.js' - import { addToast } from '../../components/Toasts.svelte' + import { toast } from 'svelte-sonner' import { getChaptersAniSkip } from '@/modules/anime.js' import Seekbar from 'perfect-seekbar' import { click } from '@/modules/click.js' @@ -71,10 +71,8 @@ function checkAudio () { if ('audioTracks' in HTMLVideoElement.prototype) { if (!video.audioTracks.length) { - addToast({ - text: "This torrent's audio codec is not supported, try a different release by disabling Autoplay Torrents in RSS settings.", - title: 'Audio Codec Unsupported', - type: 'danger' + toast.error('Audio Codec Unsupported', { + description: "This torrent's audio codec is not supported, try a different release by disabling Autoplay Torrents in RSS settings." }) } else if (video.audioTracks.length > 1) { const preferredTrack = [...video.audioTracks].find(({ language }) => language === set.audioLanguage) @@ -335,10 +333,8 @@ }) ]) canvas.remove() - addToast({ - text: 'Saved screenshot to clipboard.', - title: 'Screenshot', - type: 'success' + toast.success('Screenshot', { + description: 'Saved screenshot to clipboard.' }) } } @@ -830,31 +826,25 @@ break case target.error.MEDIA_ERR_NETWORK: console.warn('A network error caused the video download to fail part-way.', target.error) - addToast({ - text: 'A network error caused the video download to fail part-way. Click here to reload the video.', - title: 'Video Network Error', - type: 'danger', + toast.error('Video Network Error', { + description: 'A network error caused the video download to fail part-way. Click here to reload the video.', duration: 1000000, - click: () => target.load() + onClick: () => target.load() }) break case target.error.MEDIA_ERR_DECODE: console.warn('The video playback was aborted due to a corruption problem or because the video used features your browser did not support.', target.error) - addToast({ - text: 'The video playback was aborted due to a corruption problem. Click here to reload the video.', - title: 'Video Decode Error', - type: 'danger', + toast.error('Video Decode Error', { + description: 'The video playback was aborted due to a corruption problem. Click here to reload the video.', duration: 1000000, - click: () => target.load() + onClick: () => target.load() }) break case target.error.MEDIA_ERR_SRC_NOT_SUPPORTED: if (target.error.message !== 'MEDIA_ELEMENT_ERROR: Empty src attribute') { console.warn('The video could not be loaded, either because the server or network failed or because the format is not supported.', target.error) - addToast({ - text: 'The video could not be loaded, either because the server or network failed or because the format is not supported. Try a different release by disabling Autoplay Torrents in RSS settings.', - title: 'Video Codec Unsupported', - type: 'danger', + toast.error('Video Codec Unsupported', { + description: 'The video could not be loaded, either because the server or network failed or because the format is not supported. Try a different release by disabling Autoplay Torrents in RSS settings.', duration: 300000 }) } @@ -881,8 +871,7 @@ on:keypress={resetImmerse} on:mouseleave={immersePlayer}> {#if showKeybinds && !miniplayer} -
- +
(showKeybinds = false)} tabindex='-1' role='button'>
{item?.id || ''}
diff --git a/src/renderer/views/RSSView.svelte b/src/renderer/views/RSSView.svelte index 6de1d34..f196ea8 100644 --- a/src/renderer/views/RSSView.svelte +++ b/src/renderer/views/RSSView.svelte @@ -1,7 +1,7 @@