chore: cleanup AsyncIcon element

This commit is contained in:
Kieran 2023-10-16 16:54:55 +01:00
parent 497ef7bf9a
commit dec2b9ce2e
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
10 changed files with 66 additions and 78 deletions

View File

@ -360,5 +360,9 @@
<symbol id="x" viewBox="0 0 24 25" fill="none">
<path d="M17.7071 8.20711C18.0976 7.81658 18.0976 7.18342 17.7071 6.79289C17.3166 6.40237 16.6834 6.40237 16.2929 6.79289L12 11.0858L7.70711 6.79289C7.31658 6.40237 6.68342 6.40237 6.29289 6.79289C5.90237 7.18342 5.90237 7.81658 6.29289 8.20711L10.5858 12.5L6.29289 16.7929C5.90237 17.1834 5.90237 17.8166 6.29289 18.2071C6.68342 18.5976 7.31658 18.5976 7.70711 18.2071L12 13.9142L16.2929 18.2071C16.6834 18.5976 17.3166 18.5976 17.7071 18.2071C18.0976 17.8166 18.0976 17.1834 17.7071 16.7929L13.4142 12.5L17.7071 8.20711Z" fill="currentColor"/>
</symbol>
<symbol id="settings-04" viewBox="0 0 20 20" fill="none">
<path d="M11.772 7.4987L2.50033 7.4987C2.04009 7.4987 1.66699 7.1256 1.66699 6.66536C1.66699 6.20513 2.04009 5.83203 2.50033 5.83203L11.772 5.83203C12.142 4.39434 13.4471 3.33203 15.0003 3.33203C16.8413 3.33203 18.3337 4.82442 18.3337 6.66536C18.3337 8.50631 16.8413 9.9987 15.0003 9.9987C13.4471 9.9987 12.142 8.93639 11.772 7.4987Z" fill="currentColor"/>
<path d="M5.00033 9.9987C3.15938 9.9987 1.66699 11.4911 1.66699 13.332C1.66699 15.173 3.15938 16.6654 5.00033 16.6654C6.55352 16.6654 7.85861 15.6031 8.22864 14.1654L17.5003 14.1654C17.9606 14.1654 18.3337 13.7923 18.3337 13.332C18.3337 12.8718 17.9606 12.4987 17.5003 12.4987L8.22864 12.4987C7.85861 11.061 6.55352 9.9987 5.00033 9.9987Z" fill="currentColor"/>
</symbol>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -1,31 +1,15 @@
import "./AsyncButton.css";
import React, { useState, ForwardedRef } from "react";
import React, { ForwardedRef } from "react";
import Spinner from "../Icons/Spinner";
import useLoading from "Hooks/useLoading";
import classNames from "classnames";
interface AsyncButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
disabled?: boolean;
onClick(e: React.MouseEvent): Promise<void> | void;
children?: React.ReactNode;
export interface AsyncButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
onClick?: (e: React.MouseEvent) => Promise<void> | void;
}
const AsyncButton = React.forwardRef<HTMLButtonElement, AsyncButtonProps>((props, ref) => {
const [loading, setLoading] = useState<boolean>(false);
async function handle(e: React.MouseEvent) {
e.stopPropagation();
if (loading || props.disabled) return;
setLoading(true);
try {
if (typeof props.onClick === "function") {
const f = props.onClick(e);
if (f instanceof Promise) {
await f;
}
}
} finally {
setLoading(false);
}
}
const { handle, loading } = useLoading(props.onClick, props.disabled);
return (
<button
@ -33,7 +17,7 @@ const AsyncButton = React.forwardRef<HTMLButtonElement, AsyncButtonProps>((props
type="button"
disabled={loading || props.disabled}
{...props}
className={`spinner-button${props.className ? ` ${props.className}` : ""}`}
className={classNames("spinner-button", props.className)}
onClick={handle}>
<span style={{ visibility: loading ? "hidden" : "visible" }}>{props.children}</span>
{loading && (

View File

@ -1,35 +1,23 @@
import Icon from "Icons/Icon";
import useLoading from "Hooks/useLoading";
import Spinner from "Icons/Spinner";
import { HTMLProps, useState } from "react";
import classNames from "classnames";
export interface AsyncIconProps extends HTMLProps<HTMLDivElement> {
export type AsyncIconProps = React.HTMLProps<HTMLDivElement> & {
iconName: string;
iconSize?: number;
loading?: boolean;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => Promise<void>;
}
onClick?: (e: React.MouseEvent) => Promise<void> | void;
};
export function AsyncIcon(props: AsyncIconProps) {
const [loading, setLoading] = useState(props.loading ?? false);
async function handleClick(e: React.MouseEvent<HTMLDivElement>) {
setLoading(true);
try {
if (props.onClick) {
await props.onClick(e);
}
} catch (ex) {
console.error(ex);
}
setLoading(false);
}
const { loading, handle } = useLoading(props.onClick, props.disabled);
const mergedProps = { ...props } as Record<string, unknown>;
delete mergedProps["iconName"];
delete mergedProps["iconSize"];
delete mergedProps["loading"];
return (
<div {...mergedProps} onClick={e => handleClick(e)}>
<div {...mergedProps} onClick={handle} className={classNames("button-icon-sm", props.className)}>
{loading ? <Spinner /> : <Icon name={props.iconName} size={props.iconSize} />}
{props.children}
</div>

View File

@ -211,7 +211,7 @@ export function NoteCreator() {
});
}
async function onSubmit(ev: React.MouseEvent<HTMLButtonElement>) {
async function onSubmit(ev: React.MouseEvent) {
ev.stopPropagation();
await sendNote();
}
@ -450,14 +450,20 @@ export function NoteCreator() {
showFollowingMark={false}
/>
{note.pollOptions === undefined && !note.replyTo && (
<div className="note-creator-icon">
<Icon name="pie-chart" onClick={() => note.update(v => (v.pollOptions = ["A", "B"]))} size={24} />
</div>
<AsyncIcon
iconName="pie-chart"
iconSize={24}
onClick={() => note.update(v => (v.pollOptions = ["A", "B"]))}
className="note-creator-icon"
/>
)}
<AsyncIcon iconName="image-plus" iconSize={24} onClick={attachFile} className="note-creator-icon" />
<button className="secondary" onClick={() => note.update(v => (v.advanced = !v.advanced))}>
<FormattedMessage defaultMessage="Advanced" />
</button>
<AsyncIcon
iconName="settings-04"
iconSize={24}
onClick={() => note.update(v => (v.advanced = !v.advanced))}
className="note-creator-icon"
/>
</div>
<div className="flex g8">
<button className="secondary" onClick={cancel}>

View File

@ -1,9 +1,10 @@
import React, { HTMLProps, useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useLongPress } from "use-long-press";
import { TaggedNostrEvent, ParsedZap, countLeadingZeros, NostrLink } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import { Menu, MenuItem } from "@szhsin/react-menu";
import classNames from "classnames";
import { formatShort } from "Number";
import useEventPublisher from "Hooks/useEventPublisher";
@ -11,7 +12,7 @@ import { delay, findTag, normalizeReaction } from "SnortUtils";
import { NoteCreator } from "Element/Event/NoteCreator";
import SendSats from "Element/SendSats";
import { ZapsSummary } from "Element/Event/Zap";
import { AsyncIcon } from "Element/AsyncIcon";
import { AsyncIcon, AsyncIconProps } from "Element/AsyncIcon";
import { useWallet } from "Wallet";
import useLogin from "Hooks/useLogin";
@ -308,18 +309,11 @@ export default function NoteFooter(props: NoteFooterProps) {
);
}
interface AsyncFooterIconProps extends HTMLProps<HTMLDivElement> {
iconName: string;
value: number;
loading?: boolean;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => Promise<void>;
}
function AsyncFooterIcon(props: AsyncFooterIconProps) {
function AsyncFooterIcon(props: AsyncIconProps & { value: number }) {
const mergedProps = {
...props,
iconSize: 18,
className: `transition duration-200 ease-in-out reaction-pill${props.className ? ` ${props.className}` : ""}`,
className: classNames("transition duration-200 ease-in-out reaction-pill", props.className),
};
return (
<AsyncIcon {...mergedProps}>

View File

@ -0,0 +1,20 @@
import { useState } from "react";
export default function useLoading<T>(fn: ((e: React.MouseEvent) => Promise<T> | T) | undefined, disabled?: boolean) {
const [loading, setLoading] = useState<boolean>(false);
async function handle(e: React.MouseEvent) {
e.stopPropagation();
if (loading || disabled) return;
setLoading(true);
try {
if (typeof fn === "function") {
await fn(e);
}
} finally {
setLoading(false);
}
}
return { handle, loading };
}

View File

@ -24,6 +24,7 @@ import { getDisplayName } from "Element/User/DisplayName";
import { Day } from "Const";
import Tabs, { Tab } from "Element/Tabs";
import classNames from "classnames";
import { AsyncIcon } from "Element/AsyncIcon";
function notificationContext(ev: TaggedNostrEvent) {
switch (ev.kind) {
@ -194,15 +195,15 @@ function NotificationSummary({ evs }: { evs: Array<TaggedNostrEvent> }) {
);
}, [evs, period]);
const filterIcon = (f: NotificationSummaryFilter, icon: string, iconActiveClass?: string) => {
const filterIcon = (f: NotificationSummaryFilter, icon: string, iconActiveClass: string) => {
const active = hasFlag(filter, f);
return (
<button
type="button"
className={classNames("icon-sm transparent", { active: active })}
onClick={() => setFilter(v => v ^ f)}>
<Icon name={icon} className={active ? iconActiveClass : undefined} />
</button>
<AsyncIcon
className={classNames("button-icon-sm transparent", { active, [iconActiveClass]: active })}
onClick={() => setFilter(v => v ^ f)}
name={""}
iconName={icon}
/>
);
};

View File

@ -337,20 +337,15 @@ button.icon:hover {
color: var(--highlight);
}
button.icon-sm {
.button-icon-sm {
padding: 4px;
border-radius: 8px;
display: flex;
align-items: center;
cursor: pointer;
color: var(--gray-light) !important;
}
button.icon-sm:not(.active):hover {
background-color: var(--gray-dark);
}
button.icon-sm.active {
.button-icon-sm.active {
background: rgba(255, 255, 255, 0.1);
}

View File

@ -120,9 +120,6 @@
"3KNMbJ": {
"defaultMessage": "Articles"
},
"3Rx6Qo": {
"defaultMessage": "Advanced"
},
"3cc4Ct": {
"defaultMessage": "Light"
},

View File

@ -39,7 +39,6 @@
"2k0Cv+": "Dislikes ({n})",
"2ukA4d": "{n} hours",
"3KNMbJ": "Articles",
"3Rx6Qo": "Advanced",
"3cc4Ct": "Light",
"3gOsZq": "Translators",
"3qnJlS": "You are voting with {amount} sats",