- added api UserController.ts for 2FA
Some checks failed
CI Pipeline / japa-tests (push) Failing after 56s

- added PersonalTotpSettings.vue vor enablin/disabling 2FA
- changed User.ts: added attributes: state, twoFactorSecret and twoFactorRecoveryCodes
- added resources/js/utils/toast.ts for notifications
- modified start/routes/api.ts
- npm updates
This commit is contained in:
Kaimbacher 2024-01-19 15:33:46 +01:00
parent 18635f77b3
commit ebc62d9117
18 changed files with 1151 additions and 315 deletions

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
<path d="M14 12.3L12.3 14 8 9.7 3.7 14 2 12.3 6.3 8 2 3.7 3.7 2 8 6.3 12.3 2 14 3.7 9.7 8z"/>
</svg>

After

Width:  |  Height:  |  Size: 170 B

View file

@ -0,0 +1,103 @@
/* remember to import this scss file into your app */
.toastify.dialogs {
min-width: 200px;
background: none;
background-color: var(--color-main-background);
color: var(--color-main-text);
box-shadow: 0 0 6px 0 var(--color-box-shadow);
padding: 0 12px;
margin-top: 45px;
position: fixed;
z-index: 10100;
border-radius: var(--border-radius);
display: flex;
align-items: center;
.toast-undo-container {
display: flex;
align-items: center;
}
.toast-undo-button,
.toast-close {
position: static;
overflow: hidden;
box-sizing: border-box;
min-width: 44px;
height: 100%;
padding: 12px;
white-space: nowrap;
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
min-height: 0;
/* icon styling */
&.toast-close {
text-indent: 0;
opacity: 0.4;
border: none;
min-height: 44px;
margin-left: 10px;
font-size: 0;
/* dark theme overrides for Nextcloud 25 and later */
&::before {
background-image: url('./Close.svg');
content: ' ';
filter: var(--background-invert-if-dark);
display: inline-block;
width: 16px;
height: 16px;
}
}
&.toast-undo-button {
/* $margin: 3px; */
/* margin: $margin; */
/* height: calc(100% - 2 * #{$margin}); */
margin-left: 12px;
}
&:hover,
&:focus,
&:active {
cursor: pointer;
opacity: 1;
}
}
&.toastify-top {
right: 10px;
}
/* Toast with onClick callback */
&.toast-with-click {
cursor: pointer;
}
/* Various toasts types */
&.toast-error {
border-left: 3px solid var(--color-error);
}
&.toast-info {
border-left: 3px solid var(--color-primary);
}
&.toast-warning {
border-left: 3px solid var(--color-warning);
}
&.toast-success {
border-left: 3px solid var(--color-success);
}
&.toast-undo {
border-left: 3px solid var(--color-success);
}
}

216
resources/js/utils/toast.ts Normal file
View file

@ -0,0 +1,216 @@
import Toastify from 'toastify-js';
// import { t } from './utils/l10n.js';
import './toast.css';
/**
* Enum of available Toast types
*/
export enum ToastType {
ERROR = 'toast-error',
WARNING = 'toast-warning',
INFO = 'toast-info',
SUCCESS = 'toast-success',
PERMANENT = 'toast-error',
UNDO = 'toast-undo',
}
/** @deprecated Use ToastAriaLive.OFF */
export const TOAST_ARIA_LIVE_OFF = 'off';
/** @deprecated Use ToastAriaLive.POLITE */
export const TOAST_ARIA_LIVE_POLITE = 'polite';
/** @deprecated Use ToastAriaLive.ASSERTIVE */
export const TOAST_ARIA_LIVE_ASSERTIVE = 'assertive';
export enum ToastAriaLive {
OFF = TOAST_ARIA_LIVE_OFF,
POLITE = TOAST_ARIA_LIVE_POLITE,
ASSERTIVE = TOAST_ARIA_LIVE_ASSERTIVE,
}
/** Timeout in ms of a undo toast */
export const TOAST_UNDO_TIMEOUT = 10000;
/** Default timeout in ms of toasts */
export const TOAST_DEFAULT_TIMEOUT = 4000;
/** Timeout value to show a toast permanently */
export const TOAST_PERMANENT_TIMEOUT = -1;
/**
* Type of a toast
* @see https://apvarun.github.io/toastify-js/
* @notExported
*/
type Toast = ReturnType<typeof Toastify>;
export interface ToastOptions {
/**
* Defines the timeout in milliseconds after which the toast is closed. Set to -1 to have a persistent toast.
*/
timeout?: number;
/**
* Set to true to allow HTML content inside of the toast text
* @default false
*/
isHTML?: boolean;
/**
* Set a type of {ToastType} to style the modal
*/
type?: ToastType;
/**
* Provide a function that is called after the toast is removed
*/
onRemove?: () => void;
/**
* Provide a function that is called when the toast is clicked
*/
onClick?: () => void;
/**
* Make the toast closable
*/
close?: boolean;
/**
* Specify the element to attach the toast element to (for testing)
*/
selector?: string;
/**
* Whether the messages should be announced to screen readers.
* See the following docs for an explanation when to use which:
* https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions
*
* By default, errors are announced assertive and other messages "polite".
*/
ariaLive?: ToastAriaLive;
}
/**
* Show a toast message
*
* @param data Message to be shown in the toast, any HTML is removed by default
* @param options
*/
export function showMessage(data: string | Node, options?: ToastOptions): Toast {
options = Object.assign(
{
timeout: TOAST_DEFAULT_TIMEOUT,
isHTML: false,
type: undefined,
// An undefined selector defaults to the body element
selector: undefined,
onRemove: () => {},
onClick: undefined,
close: true,
},
options,
);
if (typeof data === 'string' && !options.isHTML) {
// fime mae sure that text is extracted
const element = document.createElement('div');
element.innerHTML = data;
data = element.innerText;
}
let classes = options.type ?? '';
if (typeof options.onClick === 'function') {
classes += ' toast-with-click ';
}
const isNode = data instanceof Node;
let ariaLive: ToastAriaLive = ToastAriaLive.POLITE;
if (options.ariaLive) {
ariaLive = options.ariaLive;
} else if (options.type === ToastType.ERROR || options.type === ToastType.UNDO) {
ariaLive = ToastAriaLive.ASSERTIVE;
}
const toast = Toastify({
[!isNode ? 'text' : 'node']: data,
duration: options.timeout,
callback: options.onRemove,
onClick: options.onClick,
close: options.close,
gravity: 'top',
selector: options.selector,
position: 'right',
backgroundColor: '',
className: 'dialogs ' + classes,
escapeMarkup: !options.isHTML,
ariaLive,
});
toast.showToast();
return toast;
}
export default {
updatableNotification: null,
getDefaultNotificationFunction: null,
/**
* Shows a notification that disappears after x seconds, default is
* 7 seconds
*
* @param {string} text Message to show
* @param {Array} [options] options array
* @param {number} [options.timeout=7] timeout in seconds, if this is 0 it will show the message permanently
* @param {boolean} [options.isHTML=false] an indicator for HTML notifications (true) or text (false)
* @param {string} [options.type] notification type
* @return {JQuery} the toast element
*/
showTemporary(text, options = { timeout: 3000 }) {
options = options || {};
options.timeout = options.timeout || TOAST_DEFAULT_TIMEOUT;
const toast = showMessage(text, options);
toast.toastElement.toastify = toast;
// return $(toast.toastElement)
},
/**
* Show a toast message with error styling
*
* @param text Message to be shown in the toast, any HTML is removed by default
* @param options
*/
showError(text: string, options?: ToastOptions): Toast {
return showMessage(text, { ...options, type: ToastType.ERROR });
},
/**
* Show a toast message with warning styling
*
* @param text Message to be shown in the toast, any HTML is removed by default
* @param options
*/
showWarning(text: string, options?: ToastOptions): Toast {
return showMessage(text, { ...options, type: ToastType.WARNING });
},
/**
* Show a toast message with info styling
*
* @param text Message to be shown in the toast, any HTML is removed by default
* @param options
*/
showInfo(text: string, options?: ToastOptions): Toast {
return showMessage(text, { ...options, type: ToastType.INFO });
},
/**
* Show a toast message with success styling
*
* @param text Message to be shown in the toast, any HTML is removed by default
* @param options
*/
showSuccess(text: string, options?: ToastOptions): Toast {
return showMessage(text, { ...options, type: ToastType.SUCCESS });
},
};