eslint: react-refresh, react-hooks
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Martti Malmi 2024-01-04 11:54:58 +02:00
parent b143520901
commit 2a2c713486
22 changed files with 62 additions and 27 deletions

View File

@ -1,7 +1,12 @@
module.exports = { module.exports = {
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react/recommended"], extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
],
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint", "formatjs"], plugins: ["@typescript-eslint", "formatjs", "react-refresh"],
rules: { rules: {
"formatjs/enforce-id": [ "formatjs/enforce-id": [
"error", "error",
@ -10,6 +15,8 @@ module.exports = {
}, },
], ],
"react/react-in-jsx-scope": "off", "react/react-in-jsx-scope": "off",
"react-hooks/exhaustive-deps": "off",
"react-refresh/only-export-components": "warn",
}, },
root: true, root: true,
ignorePatterns: ["build/", "*.test.ts", "*.js"], ignorePatterns: ["build/", "*.test.ts", "*.js"],

View File

@ -100,6 +100,8 @@
"eslint": "^8.48.0", "eslint": "^8.48.0",
"eslint-plugin-formatjs": "^4.11.3", "eslint-plugin-formatjs": "^4.11.3",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"postcss-preset-env": "^9.2.0", "postcss-preset-env": "^9.2.0",
"prettier": "2.8.3", "prettier": "2.8.3",

View File

@ -29,4 +29,6 @@ const AsyncButton = React.forwardRef<HTMLButtonElement, AsyncButtonProps>((props
); );
}); });
AsyncButton.displayName = "AsyncButton";
export default AsyncButton; export default AsyncButton;

View File

@ -24,7 +24,7 @@ export default function ZapstrEmbed({ ev }: { ev: NostrEvent }) {
<audio src={media?.[1] ?? ""} controls={true} /> <audio src={media?.[1] ?? ""} controls={true} />
<div> <div>
{refPersons.map(a => ( {refPersons.map(a => (
<ProfileImage pubkey={a[1]} subHeader={<>{a[2] ?? ""}</>} className="" defaultNip=" " /> <ProfileImage key={a[1]} pubkey={a[1]} subHeader={<>{a[2] ?? ""}</>} className="" defaultNip=" " />
))} ))}
</div> </div>
</div> </div>

View File

@ -345,7 +345,7 @@ export function NoteCreator() {
{Object.keys(relays.item || {}) {Object.keys(relays.item || {})
.filter(el => relays.item[el].write) .filter(el => relays.item[el].write)
.map((r, i, a) => ( .map((r, i, a) => (
<div className="p flex justify-between note-creator-relay"> <div className="p flex justify-between note-creator-relay" key={r}>
<div>{r}</div> <div>{r}</div>
<div> <div>
<input <input
@ -406,8 +406,8 @@ export function NoteCreator() {
</h4> </h4>
<FormattedMessage defaultMessage="Zaps on this note will be split to the following users." id="LwYmVi" /> <FormattedMessage defaultMessage="Zaps on this note will be split to the following users." id="LwYmVi" />
<div className="flex flex-col g8"> <div className="flex flex-col g8">
{[...(note.zapSplits ?? [])].map((v, i, arr) => ( {[...(note.zapSplits ?? [])].map((v: ZapTarget, i, arr) => (
<div className="flex items-center g8"> <div className="flex items-center g8" key={`${v.name}-${v.value}`}>
<div className="flex flex-col flex-4 g4"> <div className="flex flex-col flex-4 g4">
<h4> <h4>
<FormattedMessage defaultMessage="Recipient" id="8Rkoyb" /> <FormattedMessage defaultMessage="Recipient" id="8Rkoyb" />

View File

@ -5,7 +5,7 @@ export default function FileUploadProgress({ progress }: { progress: Array<Uploa
return ( return (
<div className="flex flex-col g8"> <div className="flex flex-col g8">
{progress.map(p => ( {progress.map(p => (
<div className="flex flex-col g2" id={p.id}> <div key={p.id} className="flex flex-col g2" id={p.id}>
{p.file.name} {p.file.name}
<Progress value={p.progress} status={p.stage} /> <Progress value={p.progress} status={p.stage} />
</div> </div>

View File

@ -1,5 +1,5 @@
import "./Thread.css"; import "./Thread.css";
import { useMemo, useState, ReactNode, useContext } from "react"; import { useMemo, useState, ReactNode, useContext, Fragment } from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { TaggedNostrEvent, u256, NostrPrefix, EventExt, parseNostrLink, NostrLink } from "@snort/system"; import { TaggedNostrEvent, u256, NostrPrefix, EventExt, parseNostrLink, NostrLink } from "@snort/system";
@ -41,7 +41,7 @@ const Subthread = ({ active, notes, related, chains, onNavigate }: SubthreadProp
const isLastSubthread = idx === notes.length - 1; const isLastSubthread = idx === notes.length - 1;
const replies = getReplies(a.id, chains); const replies = getReplies(a.id, chains);
return ( return (
<> <Fragment key={a.id}>
<div className={`subthread-container ${replies.length > 0 ? "subthread-multi" : ""}`}> <div className={`subthread-container ${replies.length > 0 ? "subthread-multi" : ""}`}>
<Divider /> <Divider />
<Note <Note
@ -65,7 +65,7 @@ const Subthread = ({ active, notes, related, chains, onNavigate }: SubthreadProp
onNavigate={onNavigate} onNavigate={onNavigate}
/> />
)} )}
</> </Fragment>
); );
}; };
@ -138,6 +138,7 @@ const TierTwo = ({ active, isLastSubthread, notes, related, chains, onNavigate }
const lastReply = idx === rest.length - 1; const lastReply = idx === rest.length - 1;
return ( return (
<ThreadNote <ThreadNote
key={r.id}
active={active} active={active}
onNavigate={onNavigate} onNavigate={onNavigate}
note={r} note={r}

View File

@ -111,7 +111,7 @@ export function TimelineRenderer(props: TimelineRendererProps) {
<> <>
<div className="card latest-notes" onClick={() => props.showLatest(false)} ref={ref}> <div className="card latest-notes" onClick={() => props.showLatest(false)} ref={ref}>
{props.latest.slice(0, 3).map(p => { {props.latest.slice(0, 3).map(p => {
return <ProfileImage pubkey={p} showUsername={false} link={""} showFollowDistance={false} />; return <ProfileImage key={p} pubkey={p} showUsername={false} link={""} showFollowDistance={false} />;
})} })}
<FormattedMessage <FormattedMessage
defaultMessage="{n} new {n, plural, =1 {note} other {notes}}" defaultMessage="{n} new {n, plural, =1 {note} other {notes}}"
@ -128,6 +128,7 @@ export function TimelineRenderer(props: TimelineRendererProps) {
{props.latest.slice(0, 3).map(p => { {props.latest.slice(0, 3).map(p => {
return ( return (
<ProfileImage <ProfileImage
key={p}
pubkey={p} pubkey={p}
showProfileCard={false} showProfileCard={false}
showUsername={false} showUsername={false}

View File

@ -27,7 +27,7 @@ export function ReBroadcaster({ onClose, ev }: { onClose: () => void; ev: Tagged
{Object.keys(relays.item || {}) {Object.keys(relays.item || {})
.filter(el => relays.item[el].write) .filter(el => relays.item[el].write)
.map((r, i, a) => ( .map((r, i, a) => (
<div className="card flex justify-between"> <div key={r} className="card flex justify-between">
<div>{r}</div> <div>{r}</div>
<div> <div>
<input <input

View File

@ -165,6 +165,7 @@ export default function SendSats(props: SendSatsProps) {
<div className="flex g4 f-wrap"> <div className="flex g4 f-wrap">
{props.targets.map(v => ( {props.targets.map(v => (
<ProfileImage <ProfileImage
key={v.value}
pubkey={v.value} pubkey={v.value}
showUsername={false} showUsername={false}
showFollowDistance={false} showFollowDistance={false}

View File

@ -93,7 +93,7 @@ export function HashTagHeader({ tag, events, className }: { tag: string; events?
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
{pubkeys.slice(0, 5).map(a => ( {pubkeys.slice(0, 5).map(a => (
<ProfileImage pubkey={a} showUsername={false} showFollowDistance={false} size={40} /> <ProfileImage key={a} pubkey={a} showUsername={false} showFollowDistance={false} size={40} />
))} ))}
{pubkeys.length > 5 && ( {pubkeys.length > 5 && (
<span> <span>

View File

@ -173,7 +173,7 @@ export default function ZapPoolPage() {
<FormattedMessage defaultMessage="Relays" id="RoOyAh" /> <FormattedMessage defaultMessage="Relays" id="RoOyAh" />
</h3> </h3>
{relayConnections.map(a => ( {relayConnections.map(a => (
<div> <div key={a.address}>
<h4>{getRelayName(a.address)}</h4> <h4>{getRelayName(a.address)}</h4>
<ZapPoolTarget <ZapPoolTarget
target={ target={
@ -191,7 +191,7 @@ export default function ZapPoolPage() {
<FormattedMessage defaultMessage="File hosts" id="XICsE8" /> <FormattedMessage defaultMessage="File hosts" id="XICsE8" />
</h3> </h3>
{UploaderServices.map(a => ( {UploaderServices.map(a => (
<div> <div key={a.name}>
<h4>{a.name}</h4> <h4>{a.name}</h4>
<ZapPoolTarget <ZapPoolTarget
target={ target={
@ -209,7 +209,7 @@ export default function ZapPoolPage() {
<FormattedMessage defaultMessage="Data Providers" id="ELbg9p" /> <FormattedMessage defaultMessage="Data Providers" id="ELbg9p" />
</h3> </h3>
{DataProviders.map(a => ( {DataProviders.map(a => (
<div> <div key={a.name}>
<h4>{a.name}</h4> <h4>{a.name}</h4>
<ZapPoolTarget <ZapPoolTarget
target={ target={

View File

@ -25,7 +25,7 @@ function OnboardingLayout() {
<Icon name="translate" /> <Icon name="translate" />
<select value={lang} onChange={e => setOverride(e.target.value)} className="capitalize"> <select value={lang} onChange={e => setOverride(e.target.value)} className="capitalize">
{AllLanguageCodes.sort().map(a => ( {AllLanguageCodes.sort().map(a => (
<option value={a}> <option key={a} value={a}>
{new Intl.DisplayNames([a], { {new Intl.DisplayNames([a], {
type: "language", type: "language",
}).of(a)} }).of(a)}

View File

@ -33,7 +33,7 @@ export default function ExportKeys() {
{hexToMnemonic(generatedEntropy ?? "") {hexToMnemonic(generatedEntropy ?? "")
.split(" ") .split(" ")
.map((a, i) => ( .map((a, i) => (
<div className="flex items-center word"> <div key={a} className="flex items-center word">
<div>{i + 1}</div> <div>{i + 1}</div>
<div>{a}</div> <div>{a}</div>
</div> </div>

View File

@ -80,7 +80,7 @@ export default function ModerationSettingsPage() {
</button> </button>
</div> </div>
{appData.mutedWords.map(v => ( {appData.mutedWords.map(v => (
<div className="p br b flex items-center justify-between"> <div key={v} className="p br b flex items-center justify-between">
<div>{v}</div> <div>{v}</div>
<button type="button" onClick={() => removeMutedWord(v)}> <button type="button" onClick={() => removeMutedWord(v)}>
<FormattedMessage defaultMessage="Delete" id="K3r6DQ" /> <FormattedMessage defaultMessage="Delete" id="K3r6DQ" />

View File

@ -59,7 +59,7 @@ const PreferencesPage = () => {
} }
style={{ textTransform: "capitalize" }}> style={{ textTransform: "capitalize" }}>
{AllLanguageCodes.sort().map(a => ( {AllLanguageCodes.sort().map(a => (
<option value={a}> <option key={a} value={a}>
{new Intl.DisplayNames([a], { {new Intl.DisplayNames([a], {
type: "language", type: "language",
}).of(a)} }).of(a)}

View File

@ -75,7 +75,7 @@ const RelayInfo = () => {
</h4> </h4>
<div className="grow"> <div className="grow">
{stats.info.supported_nips.map(a => ( {stats.info.supported_nips.map(a => (
<a target="_blank" rel="noreferrer" href={`https://nips.be/${a}`} className="pill"> <a key={a} target="_blank" rel="noreferrer" href={`https://nips.be/${a}`} className="pill">
NIP-{a.toString().padStart(2, "0")} NIP-{a.toString().padStart(2, "0")}
</a> </a>
))} ))}

View File

@ -148,7 +148,7 @@ export function CloseRelays() {
?.filter(a => !relayUrls.includes(unwrap(sanitizeRelayUrl(a.url))) && !a.is_paid) ?.filter(a => !relayUrls.includes(unwrap(sanitizeRelayUrl(a.url))) && !a.is_paid)
.sort((a, b) => (a.distance > b.distance ? 1 : -1)) .sort((a, b) => (a.distance > b.distance ? 1 : -1))
.map(a => ( .map(a => (
<div className="bg-dark p br flex flex-col g8"> <div key={a.url} className="bg-dark p br flex flex-col g8">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div className="bold">{getRelayName(a.url)}</div> <div className="bold">{getRelayName(a.url)}</div>
<AsyncButton <AsyncButton

View File

@ -62,6 +62,7 @@ export function FollowsRelayHealth({
<div> <div>
{missingRelays.map(a => ( {missingRelays.map(a => (
<ProfilePreview <ProfilePreview
key={a}
pubkey={a} pubkey={a}
options={{ options={{
about: false, about: false,
@ -80,7 +81,7 @@ export function FollowsRelayHealth({
.sort((a, b) => (a.count > b.count ? -1 : 1)) .sort((a, b) => (a.count > b.count ? -1 : 1))
.slice(0, 10) .slice(0, 10)
.map(a => ( .map(a => (
<div className="flex justify-between"> <div key={a.relay} className="flex justify-between">
<div>{getRelayName(a.relay)}</div> <div>{getRelayName(a.relay)}</div>
<div> <div>
{a.count} (<FormattedNumber style="percent" value={a.count / uniqueFollows.length} />) {a.count} (<FormattedNumber style="percent" value={a.count / uniqueFollows.length} />)

View File

@ -141,7 +141,7 @@ export function PruneFollowList() {
.sort(([, a], [, b]) => (a > b ? -1 : 1)) .sort(([, a], [, b]) => (a > b ? -1 : 1))
.map(([k, v]) => { .map(([k, v]) => {
return ( return (
<div className="flex justify-between"> <div key={k} className="flex justify-between">
<ProfileImage pubkey={k} /> <ProfileImage pubkey={k} />
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<FormattedMessage <FormattedMessage

View File

@ -91,7 +91,7 @@ export function SubscribePage() {
{Plans.map(a => { {Plans.map(a => {
const lower = Plans.filter(b => b.id < a.id); const lower = Plans.filter(b => b.id < a.id);
return ( return (
<div className={classNames("p flex flex-col g8", { disabled: a.disabled })}> <div key={a.id} className={classNames("p flex flex-col g8", { disabled: a.disabled })}>
<div className="grow"> <div className="grow">
<h2>{mapPlanName(a.id)}</h2> <h2>{mapPlanName(a.id)}</h2>
<p> <p>
@ -108,10 +108,10 @@ export function SubscribePage() {
</p> </p>
<ul className="list-disc"> <ul className="list-disc">
{a.unlocks.map(b => ( {a.unlocks.map(b => (
<li>{mapFeatureName(b)} </li> <li key={`unlocks-${b}`}>{mapFeatureName(b)} </li>
))} ))}
{lower.map(b => ( {lower.map(b => (
<li> <li key={`lower-${b}`}>
<FormattedMessage <FormattedMessage
defaultMessage="Everything in {plan}" defaultMessage="Everything in {plan}"
id="l+ikU1" id="l+ikU1"

View File

@ -2964,6 +2964,8 @@ __metadata:
eslint: ^8.48.0 eslint: ^8.48.0
eslint-plugin-formatjs: ^4.11.3 eslint-plugin-formatjs: ^4.11.3
eslint-plugin-react: ^7.33.2 eslint-plugin-react: ^7.33.2
eslint-plugin-react-hooks: ^4.6.0
eslint-plugin-react-refresh: ^0.4.5
fuse.js: ^7.0.0 fuse.js: ^7.0.0
highlight.js: ^11.8.0 highlight.js: ^11.8.0
light-bolt11-decoder: ^2.1.0 light-bolt11-decoder: ^2.1.0
@ -5904,6 +5906,24 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"eslint-plugin-react-hooks@npm:^4.6.0":
version: 4.6.0
resolution: "eslint-plugin-react-hooks@npm:4.6.0"
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
checksum: 23001801f14c1d16bf0a837ca7970d9dd94e7b560384b41db378b49b6e32dc43d6e2790de1bd737a652a86f81a08d6a91f402525061b47719328f586a57e86c3
languageName: node
linkType: hard
"eslint-plugin-react-refresh@npm:^0.4.5":
version: 0.4.5
resolution: "eslint-plugin-react-refresh@npm:0.4.5"
peerDependencies:
eslint: ">=7"
checksum: 953e665f6aaf097291a2bb07fde05466338aecd169bcd6faa708b50166912e3a757f45a45252cf7738b5e0d986224d363cc227864e98c979fe9978770b2b9f42
languageName: node
linkType: hard
"eslint-plugin-react@npm:^7.33.2": "eslint-plugin-react@npm:^7.33.2":
version: 7.33.2 version: 7.33.2
resolution: "eslint-plugin-react@npm:7.33.2" resolution: "eslint-plugin-react@npm:7.33.2"