refactor: button to allow html attributes & refactor class names (#956)
This commit is contained in:
@@ -130,7 +130,7 @@ export const ConfirmPassword: FunctionComponent<Props> = observer(
|
||||
label={
|
||||
isRegistering ? 'Creating account...' : 'Create account & sign in'
|
||||
}
|
||||
type="primary"
|
||||
variant="primary"
|
||||
onClick={handleConfirmFormSubmit}
|
||||
disabled={isRegistering}
|
||||
/>
|
||||
|
||||
@@ -122,7 +122,7 @@ export const CreateAccount: FunctionComponent<Props> = observer(
|
||||
<Button
|
||||
className="btn-w-full mt-1"
|
||||
label="Next"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
onClick={handleRegisterFormSubmit}
|
||||
/>
|
||||
</form>
|
||||
|
||||
@@ -171,7 +171,7 @@ export const SignInPane: FunctionComponent<Props> = observer(
|
||||
<Button
|
||||
className="btn-w-full mt-1 mb-3"
|
||||
label={isSigningIn ? 'Signing in...' : 'Sign in'}
|
||||
type="primary"
|
||||
variant="primary"
|
||||
onClick={handleSignInFormSubmit}
|
||||
disabled={isSigningIn}
|
||||
/>
|
||||
|
||||
@@ -196,7 +196,7 @@ export const AttachedFilesPopover: FunctionComponent<Props> = observer(
|
||||
: 'No files found in this account'}
|
||||
</div>
|
||||
<Button
|
||||
type="normal"
|
||||
variant="normal"
|
||||
onClick={handleAttachFilesClick}
|
||||
onBlur={closeOnBlur}
|
||||
>
|
||||
|
||||
@@ -1,60 +1,79 @@
|
||||
import { JSXInternal } from 'preact/src/jsx';
|
||||
import TargetedEvent = JSXInternal.TargetedEvent;
|
||||
import TargetedMouseEvent = JSXInternal.TargetedMouseEvent;
|
||||
|
||||
import { ComponentChildren, FunctionComponent, Ref } from 'preact';
|
||||
import { forwardRef } from 'preact/compat';
|
||||
|
||||
const baseClass = `rounded px-4 py-1.75 font-bold text-sm fit-content`;
|
||||
|
||||
type ButtonType = 'normal' | 'primary' | 'danger';
|
||||
type ButtonVariant = 'normal' | 'primary';
|
||||
|
||||
const buttonClasses: { [type in ButtonType]: string } = {
|
||||
normal: `${baseClass} bg-default color-text border-solid border-main border-1 focus:bg-contrast hover:bg-contrast`,
|
||||
primary: `${baseClass} no-border bg-info color-info-contrast hover:brightness-130 focus:brightness-130`,
|
||||
danger: `${baseClass} bg-default color-danger border-solid border-main border-1 focus:bg-contrast hover:bg-contrast`,
|
||||
const getClassName = (
|
||||
variant: ButtonVariant,
|
||||
danger: boolean,
|
||||
disabled: boolean
|
||||
) => {
|
||||
const borders =
|
||||
variant === 'normal' ? 'border-solid border-main border-1' : 'no-border';
|
||||
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer';
|
||||
|
||||
let colors =
|
||||
variant === 'normal'
|
||||
? 'bg-default color-text'
|
||||
: 'bg-info color-info-contrast';
|
||||
|
||||
let focusHoverStates =
|
||||
variant === 'normal'
|
||||
? 'focus:bg-contrast hover:bg-contrast'
|
||||
: 'hover:brightness-130 focus:brightness-130';
|
||||
|
||||
if (danger) {
|
||||
colors =
|
||||
variant === 'normal'
|
||||
? 'bg-default color-danger'
|
||||
: 'bg-danger color-info-contrast';
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
colors =
|
||||
variant === 'normal'
|
||||
? 'bg-default color-grey-2'
|
||||
: 'bg-grey-2 color-info-contrast';
|
||||
focusHoverStates =
|
||||
variant === 'normal'
|
||||
? 'focus:bg-default hover:bg-default'
|
||||
: 'focus:brightness-default hover:brightness-default';
|
||||
}
|
||||
|
||||
return `${baseClass} ${colors} ${borders} ${focusHoverStates} ${cursor}`;
|
||||
};
|
||||
|
||||
type ButtonProps = {
|
||||
type ButtonProps = JSXInternal.HTMLAttributes<HTMLButtonElement> & {
|
||||
children?: ComponentChildren;
|
||||
className?: string;
|
||||
type: ButtonType;
|
||||
variant?: ButtonVariant;
|
||||
dangerStyle?: boolean;
|
||||
label?: string;
|
||||
onClick: (
|
||||
event:
|
||||
| TargetedEvent<HTMLFormElement>
|
||||
| TargetedMouseEvent<HTMLButtonElement>
|
||||
) => void;
|
||||
onBlur?: (event: FocusEvent) => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const Button: FunctionComponent<ButtonProps> = forwardRef(
|
||||
(
|
||||
{
|
||||
type,
|
||||
variant = 'normal',
|
||||
label,
|
||||
className = '',
|
||||
onBlur,
|
||||
onClick,
|
||||
dangerStyle: danger = false,
|
||||
disabled = false,
|
||||
children,
|
||||
...props
|
||||
}: ButtonProps,
|
||||
ref: Ref<HTMLButtonElement>
|
||||
) => {
|
||||
const buttonClass = buttonClasses[type];
|
||||
const cursorClass = disabled ? 'cursor-default' : 'cursor-pointer';
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`${buttonClass} ${cursorClass} ${className}`}
|
||||
onBlur={onBlur}
|
||||
onClick={(e) => {
|
||||
onClick(e);
|
||||
e.preventDefault();
|
||||
}}
|
||||
type="button"
|
||||
className={`${getClassName(variant, danger, disabled)} ${className}`}
|
||||
disabled={disabled}
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
{label ?? children}
|
||||
</button>
|
||||
|
||||
@@ -111,7 +111,7 @@ export const FilePreviewModal: FunctionComponent<Props> = ({
|
||||
<div className="flex items-center">
|
||||
{objectUrl && (
|
||||
<Button
|
||||
type="primary"
|
||||
variant="primary"
|
||||
className="mr-4"
|
||||
onClick={() => {
|
||||
application
|
||||
@@ -151,7 +151,7 @@ export const FilePreviewModal: FunctionComponent<Props> = ({
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
type="primary"
|
||||
variant="primary"
|
||||
className="mr-3"
|
||||
onClick={() => {
|
||||
getObjectUrl();
|
||||
@@ -160,7 +160,7 @@ export const FilePreviewModal: FunctionComponent<Props> = ({
|
||||
Try again
|
||||
</Button>
|
||||
<Button
|
||||
type="normal"
|
||||
variant="normal"
|
||||
onClick={() => {
|
||||
application.getAppState().files.downloadFile(file);
|
||||
}}
|
||||
@@ -176,7 +176,7 @@ export const FilePreviewModal: FunctionComponent<Props> = ({
|
||||
application.
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
application.getAppState().files.downloadFile(file);
|
||||
}}
|
||||
|
||||
@@ -120,7 +120,7 @@ export const Extensions: FunctionComponent<{
|
||||
<div className="min-h-2" />
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Install"
|
||||
onClick={() => submitExtensionUrl(customUrl)}
|
||||
/>
|
||||
|
||||
@@ -102,7 +102,7 @@ export const Listed = observer(({ application }: Props) => {
|
||||
<Text>Create a free Listed author account to get started.</Text>
|
||||
<Button
|
||||
className="mt-3"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
disabled={requestingAccount}
|
||||
label={
|
||||
requestingAccount ? 'Creating account...' : 'Create new author'
|
||||
|
||||
@@ -39,7 +39,7 @@ export const Authentication: FunctionComponent<{
|
||||
and enable end-to-end encryption.
|
||||
</Text>
|
||||
<Button
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label="Create free account"
|
||||
onClick={clickRegister}
|
||||
className="mb-3"
|
||||
|
||||
@@ -50,7 +50,7 @@ export const Credentials: FunctionComponent<Props> = observer(
|
||||
</Text>
|
||||
<Button
|
||||
className="min-w-20 mt-3"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Change email"
|
||||
onClick={() => {
|
||||
setIsChangeEmailDialogOpen(true);
|
||||
@@ -64,7 +64,7 @@ export const Credentials: FunctionComponent<Props> = observer(
|
||||
</Text>
|
||||
<Button
|
||||
className="min-w-20 mt-3"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Change password"
|
||||
onClick={presentPasswordWizard}
|
||||
/>
|
||||
|
||||
@@ -27,14 +27,14 @@ const SignOutView: FunctionComponent<{
|
||||
<div className="flex flex-row">
|
||||
<Button
|
||||
className="mr-3"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Sign out other sessions"
|
||||
onClick={() => {
|
||||
appState.accountMenu.setOtherSessionsSignOut(true);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Manage sessions"
|
||||
onClick={() => appState.openSessionsModal()}
|
||||
/>
|
||||
@@ -48,7 +48,7 @@ const SignOutView: FunctionComponent<{
|
||||
</Text>
|
||||
<div className="min-h-3" />
|
||||
<Button
|
||||
type="danger"
|
||||
dangerStyle={true}
|
||||
label="Sign out workspace"
|
||||
onClick={() => {
|
||||
appState.accountMenu.setSigningOut(true);
|
||||
@@ -76,7 +76,7 @@ const ClearSessionDataView: FunctionComponent<{
|
||||
</Text>
|
||||
<div className="min-h-3" />
|
||||
<Button
|
||||
type="danger"
|
||||
dangerStyle={true}
|
||||
label="Clear workspace"
|
||||
onClick={() => {
|
||||
appState.accountMenu.setSigningOut(true);
|
||||
|
||||
@@ -55,7 +55,7 @@ export const Sync: FunctionComponent<Props> = observer(
|
||||
</Text>
|
||||
<Button
|
||||
className="min-w-20 mt-3"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Sync now"
|
||||
disabled={isSyncingInProgress}
|
||||
onClick={doSynchronization}
|
||||
|
||||
@@ -169,7 +169,7 @@ export const ChangeEmail: FunctionalComponent<Props> = ({
|
||||
<ModalDialogButtons className="px-4.5">
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label={submitButtonTitle}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
|
||||
@@ -2,8 +2,6 @@ import { FunctionalComponent } from 'preact';
|
||||
import { Subtitle } from '@/components/Preferences/components';
|
||||
import { DecoratedInput } from '@/components/DecoratedInput';
|
||||
import { Button } from '@/components/Button';
|
||||
import { JSXInternal } from '@node_modules/preact/src/jsx';
|
||||
import TargetedEvent = JSXInternal.TargetedEvent;
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
@@ -40,9 +38,7 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(
|
||||
);
|
||||
};
|
||||
|
||||
const handleSubscriptionCodeSubmit = async (
|
||||
event: TargetedEvent<HTMLFormElement, Event>
|
||||
) => {
|
||||
const handleSubscriptionCodeSubmit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const result = await application.features.setOfflineFeaturesCode(
|
||||
@@ -117,7 +113,7 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(
|
||||
)}
|
||||
{hasUserPreviouslyStoredCode && (
|
||||
<Button
|
||||
type="danger"
|
||||
dangerStyle={true}
|
||||
label="Remove offline key"
|
||||
onClick={() => {
|
||||
handleRemoveClick();
|
||||
@@ -127,13 +123,9 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(
|
||||
{!hasUserPreviouslyStoredCode && !isSuccessfullyActivated && (
|
||||
<Button
|
||||
label={'Submit'}
|
||||
type="primary"
|
||||
variant="primary"
|
||||
disabled={activationCode === ''}
|
||||
onClick={(event) =>
|
||||
handleSubscriptionCodeSubmit(
|
||||
event as TargetedEvent<HTMLFormElement>
|
||||
)
|
||||
}
|
||||
onClick={(event) => handleSubscriptionCodeSubmit(event)}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
|
||||
@@ -46,7 +46,7 @@ export const NoSubscription: FunctionalComponent<{
|
||||
{application.hasAccount() && (
|
||||
<Button
|
||||
className="min-w-20 mt-3"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label="Subscribe"
|
||||
onClick={onPurchaseClick}
|
||||
/>
|
||||
|
||||
@@ -81,7 +81,7 @@ export const SubscriptionInformation = observer(
|
||||
<StatusText subscriptionState={subscriptionState} />
|
||||
<Button
|
||||
className="min-w-20 mt-3 mr-3"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Manage subscription"
|
||||
onClick={manageSubscription}
|
||||
/>
|
||||
|
||||
@@ -201,7 +201,7 @@ export const DataBackups = observer(({ application, appState }: Props) => {
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="normal"
|
||||
variant="normal"
|
||||
onClick={downloadDataArchive}
|
||||
label="Download backup"
|
||||
className="mt-2"
|
||||
@@ -212,7 +212,7 @@ export const DataBackups = observer(({ application, appState }: Props) => {
|
||||
|
||||
<div class="flex flex-row items-center mt-3">
|
||||
<Button
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Import backup"
|
||||
onClick={handleImportFile}
|
||||
/>
|
||||
|
||||
@@ -222,7 +222,7 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
|
||||
{shouldShowEnableButton && (
|
||||
<div>
|
||||
<Button
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Enable"
|
||||
className={`px-1 text-xs min-w-40 ${additionalClass}`}
|
||||
onClick={installIntegration}
|
||||
@@ -235,13 +235,13 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
|
||||
<div className={'flex flex-col items-end'}>
|
||||
<Button
|
||||
className={`min-w-40 mb-2 ${additionalClass}`}
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Perform Backup"
|
||||
onClick={performBackupNow}
|
||||
/>
|
||||
<Button
|
||||
className="min-w-40"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Disable"
|
||||
onClick={disable}
|
||||
/>
|
||||
|
||||
@@ -56,7 +56,7 @@ export const ConfirmCustomExtension: FunctionComponent<{
|
||||
<div className="flex flex-row">
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Cancel"
|
||||
onClick={() => callback(false)}
|
||||
/>
|
||||
@@ -65,7 +65,7 @@ export const ConfirmCustomExtension: FunctionComponent<{
|
||||
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Install"
|
||||
onClick={() => callback(true)}
|
||||
/>
|
||||
|
||||
@@ -104,7 +104,7 @@ export const ExtensionItem: FunctionComponent<ExtensionItemProps> = ({
|
||||
<div className="flex flex-row">
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Uninstall"
|
||||
onClick={() => uninstall(extension)}
|
||||
/>
|
||||
|
||||
@@ -224,7 +224,7 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
<Button
|
||||
label="Add passcode"
|
||||
onClick={handleAddPassCode}
|
||||
type="primary"
|
||||
variant="primary"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -258,13 +258,13 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
/>
|
||||
<div className="min-h-2" />
|
||||
<Button
|
||||
type="primary"
|
||||
variant="primary"
|
||||
onClick={submitPasscodeForm}
|
||||
label="Set Passcode"
|
||||
className="mr-3"
|
||||
/>
|
||||
<Button
|
||||
type="normal"
|
||||
variant="normal"
|
||||
onClick={() => setShowPasscodeForm(false)}
|
||||
label="Cancel"
|
||||
/>
|
||||
@@ -276,13 +276,13 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
<Text>Passcode lock is enabled.</Text>
|
||||
<div className="flex flex-row mt-3">
|
||||
<Button
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Change Passcode"
|
||||
onClick={changePasscodePressed}
|
||||
className="mr-3"
|
||||
/>
|
||||
<Button
|
||||
type="danger"
|
||||
dangerStyle={true}
|
||||
label="Remove Passcode"
|
||||
onClick={removePasscodePressed}
|
||||
/>
|
||||
|
||||
@@ -107,7 +107,7 @@ export const Protections: FunctionalComponent<Props> = ({ application }) => {
|
||||
{protectionsDisabledUntil && (
|
||||
<Button
|
||||
className="mt-3"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label="End Unprotected Access"
|
||||
onClick={enableProtections}
|
||||
/>
|
||||
|
||||
@@ -78,13 +78,13 @@ export const SaveSecretKey: FunctionComponent<{
|
||||
<ModalDialogButtons>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Back"
|
||||
onClick={() => act.openScanQRCode()}
|
||||
/>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label="Next"
|
||||
onClick={() => act.openVerification()}
|
||||
/>
|
||||
|
||||
@@ -63,13 +63,13 @@ export const ScanQRCode: FunctionComponent<{
|
||||
<ModalDialogButtons>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Cancel"
|
||||
onClick={() => act.cancelActivation()}
|
||||
/>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label="Next"
|
||||
onClick={() => act.openSaveSecretKey()}
|
||||
/>
|
||||
|
||||
@@ -27,7 +27,7 @@ export const TwoFactorSuccess: FunctionComponent<{
|
||||
<ModalDialogButtons>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label="Finish"
|
||||
onClick={act.finishActivation}
|
||||
/>
|
||||
|
||||
@@ -65,13 +65,13 @@ export const Verification: FunctionComponent<{
|
||||
)}
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="normal"
|
||||
variant="normal"
|
||||
label="Back"
|
||||
onClick={act.openSaveSecretKey}
|
||||
/>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label="Next"
|
||||
onClick={act.enable2FA}
|
||||
/>
|
||||
|
||||
@@ -204,7 +204,7 @@ export const CreateAccount: FunctionComponent<Props> = observer(
|
||||
</div>
|
||||
<Button
|
||||
className="py-2.5 xs:mb-4"
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label={
|
||||
isCreatingAccount ? 'Creating account...' : 'Create account'
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ export const SignIn: FunctionComponent<Props> = observer(
|
||||
</div>
|
||||
<Button
|
||||
className={`${isSigningIn ? 'min-w-30' : 'min-w-24'} py-2.5 mb-5`}
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label={isSigningIn ? 'Signing in...' : 'Sign in'}
|
||||
onClick={handleSignIn}
|
||||
disabled={isSigningIn}
|
||||
|
||||
@@ -46,7 +46,7 @@ export const RevisionContentLocked: FunctionComponent<{
|
||||
. Learn more about our other plans to upgrade your history capacity.
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
variant="primary"
|
||||
label="Discover plans"
|
||||
onClick={() => {
|
||||
if (window.plansUrl) {
|
||||
|
||||
@@ -292,7 +292,7 @@ export const RevisionHistoryModal: FunctionComponent<RevisionHistoryModalProps>
|
||||
label="Close"
|
||||
onClick={dismissModal}
|
||||
ref={closeButtonRef}
|
||||
type="normal"
|
||||
variant="normal"
|
||||
/>
|
||||
</div>
|
||||
{selectedRevision && (
|
||||
@@ -301,7 +301,7 @@ export const RevisionHistoryModal: FunctionComponent<RevisionHistoryModalProps>
|
||||
<Button
|
||||
className="py-1.35 mr-2.5"
|
||||
onClick={deleteSelectedRevision}
|
||||
type="normal"
|
||||
variant="normal"
|
||||
>
|
||||
{isDeletingRevision ? (
|
||||
<div className="sk-spinner my-1 w-3 h-3 spinner-info" />
|
||||
@@ -314,13 +314,13 @@ export const RevisionHistoryModal: FunctionComponent<RevisionHistoryModalProps>
|
||||
className="py-1.35 mr-2.5"
|
||||
label="Restore as a copy"
|
||||
onClick={restoreAsCopy}
|
||||
type="normal"
|
||||
variant="normal"
|
||||
/>
|
||||
<Button
|
||||
className="py-1.35"
|
||||
label="Restore version"
|
||||
onClick={restore}
|
||||
type="primary"
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1045,3 +1045,19 @@
|
||||
.vertical-middle {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.sn-component .focus\:brightness-default:focus {
|
||||
filter: brightness(100%);
|
||||
}
|
||||
|
||||
.sn-component .hover\:brightness-default:hover {
|
||||
filter: brightness(100%);
|
||||
}
|
||||
|
||||
.sn-component .focus\:bg-default:focus {
|
||||
background-color: var(--sn-stylekit-background-color);
|
||||
}
|
||||
|
||||
.sn-component .hover\:bg-default:hover {
|
||||
background-color: var(--sn-stylekit-background-color);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user