Skip to content

Make toast support preventDuplicates #31501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 27, 2024
11 changes: 0 additions & 11 deletions templates/devtest/gitea-ui.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,6 @@
</div>
</div>

<div>
<h1>Toast</h1>
<div>
<button class="ui button" id="info-toast">Show Info Toast</button>
<button class="ui button" id="warning-toast">Show Warning Toast</button>
<button class="ui button" id="error-toast">Show Error Toast</button>
</div>
</div>

<div>
<h1>ComboMarkdownEditor</h1>
<div>ps: no JS code attached, so just a layout</div>
Expand All @@ -201,7 +192,5 @@
<div>
<button class="{{if true}}tw-bg-red{{end}} tw-p-5 tw-border tw-rounded hover:tw-bg-blue active:tw-bg-yellow">Button</button>
</div>

<script src="{{AssetUrlPrefix}}/js/devtest.js?v={{AssetVersion}}"></script>
</div>
{{template "base/footer" .}}
14 changes: 14 additions & 0 deletions templates/devtest/toast.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{{template "base/head" .}}

<div>
<h1>Toast</h1>
<div>
<button class="ui button" id="info-toast">Show Info Toast</button>
<button class="ui button" id="warning-toast">Show Warning Toast</button>
<button class="ui button" id="error-toast">Show Error Toast</button>
</div>
</div>

<script src="{{AssetUrlPrefix}}/js/devtest.js?v={{AssetVersion}}"></script>

{{template "base/footer" .}}
4 changes: 2 additions & 2 deletions web_src/css/modules/animations.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ code.language-math.is-loading::after {
transform: scale(1);
}
50% {
transform: scale(1.8);
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}

.pulse {
animation: pulse 2s linear;
animation: pulse 200ms linear;
}

.ui.modal,
Expand Down
15 changes: 13 additions & 2 deletions web_src/css/modules/toast.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,23 @@
background: transparent;
border: none;
display: flex;
width: 30px;
height: 30px;
min-width: 30px;
min-height: 30px;
justify-content: center;
align-items: center;
}

.toast-duplicate-number::before {
content: "(";
}
.toast-duplicate-number {
display: inline-flex;
margin: 0 2px;
}
.toast-duplicate-number::after {
content: ")";
}

.toast-close:hover {
background: var(--color-hover);
}
Expand Down
24 changes: 21 additions & 3 deletions web_src/js/modules/toast.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {htmlEscape} from 'escape-goat';
import {svg} from '../svg.js';
import {showElem} from '../utils/dom.js';
import Toastify from 'toastify-js'; // don't use "async import", because when network error occurs, the "async import" also fails and nothing is shown

const levels = {
Expand All @@ -21,12 +22,28 @@ const levels = {
};

// See https://github.com/apvarun/toastify-js#api for options
function showToast(message, level, {gravity, position, duration, useHtmlBody, ...other} = {}) {
function showToast(message, level, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other} = {}) {
const body = useHtmlBody ? String(message) : htmlEscape(message);
const key = `${level}-${body}`;

// prevent showing duplicate toasts with same level and message, and give a visual feedback for end users
if (preventDuplicates) {
const toastEl = document.querySelector(`.toastify[data-toast-unique-key="${CSS.escape(key)}"]`);
if (toastEl) {
const toastDupNumEl = toastEl.querySelector('.toast-duplicate-number');
showElem(toastDupNumEl);
toastDupNumEl.textContent = String(Number(toastDupNumEl.textContent) + 1);
toastDupNumEl.classList.remove('pulse');
requestAnimationFrame(() => toastDupNumEl.classList.add('pulse'));
return;
}
}

const {icon, background, duration: levelDuration} = levels[level ?? 'info'];
const toast = Toastify({
text: `
<div class='toast-icon'>${svg(icon)}</div>
<div class='toast-body'>${useHtmlBody ? message : htmlEscape(message)}</div>
<div class='toast-icon'>${svg(icon)}<span class="toast-duplicate-number tw-hidden">1</span></div>
<div class='toast-body'>${body}</div>
<button class='toast-close'>${svg('octicon-x')}</button>
`,
escapeMarkup: false,
Expand All @@ -39,6 +56,7 @@ function showToast(message, level, {gravity, position, duration, useHtmlBody, ..

toast.showToast();
toast.toastElement.querySelector('.toast-close').addEventListener('click', () => toast.hideToast());
toast.toastElement.setAttribute('data-toast-unique-key', key);
return toast;
}

Expand Down