fix: eslint errors

This commit is contained in:
Shusui MOYATANI
2023-11-23 00:54:04 +09:00
parent 93cf0ce825
commit 52f7573475
49 changed files with 130 additions and 253 deletions

View File

@@ -81,6 +81,13 @@ module.exports = {
'created-at', 'created-at',
'actions', 'actions',
'content', 'content',
'profile',
'profile-icon',
'profile-name',
'profile-username',
'notification-icon',
'notification-user',
'notification-event',
], ],
}, },
}, },

View File

@@ -4,7 +4,6 @@ about: Create a report to help us improve
title: '' title: ''
labels: bug labels: bug
assignees: syusui-s assignees: syusui-s
--- ---
**Describe the bug** **Describe the bug**
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce** **To Reproduce**
Steps to reproduce the behavior: Steps to reproduce the behavior:
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
3. Scroll down to '....' 3. Scroll down to '....'
@@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):** **Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari] - OS: [e.g. iOS]
- Version [e.g. 22] - Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):** **Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1] - Device: [e.g. iPhone6]
- Browser [e.g. stock browser, safari] - OS: [e.g. iOS8.1]
- Version [e.g. 22] - Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context** **Additional context**
Add any other context about the problem here. Add any other context about the problem here.

View File

@@ -4,7 +4,6 @@ about: 改善に取り組めるように報告を作成します
title: '' title: ''
labels: bug labels: bug
assignees: syusui-s assignees: syusui-s
--- ---
**バグの説明** **バグの説明**
@@ -12,6 +11,7 @@ assignees: syusui-s
**再現手順** **再現手順**
発生させる手順を説明してください: 発生させる手順を説明してください:
1. '...'に移動 1. '...'に移動
2. '....'をクリック 2. '....'をクリック
3. '....'までスクロールする 3. '....'までスクロールする
@@ -24,15 +24,17 @@ assignees: syusui-s
スクリーンショットがあればこちらに。 スクリーンショットがあればこちらに。
**デスクトップ (該当する場合は以下の情報を埋めてください):** **デスクトップ (該当する場合は以下の情報を埋めてください):**
- OS: (例: Windows 11, macOS Ventura
- ブラウザ: (例: Chrome, Firefox, Safari, Edge - OS: (例: Windows 11, macOS Ventura
- バージョン: (例: 110 - ブラウザ: (例: Chrome, Firefox, Safari, Edge
- バージョン: (例: 110
**スマートフォン (該当する場合は以下の情報を埋めてください):** **スマートフォン (該当する場合は以下の情報を埋めてください):**
- デバイス: (例: iPhone 12)
- OS: 例: iOS 16.2, Android 13 - デバイス: (例: iPhone 12)
- ブラウザ: (例: Safari, Chrome - OS: (例: iOS 16.2, Android 13
- バージョン: (例: 110 - ブラウザ: (例: Safari, Chrome
- バージョン: (例: 110
**追加の情報** **追加の情報**
問題に関する他の情報があればこちらに。 問題に関する他の情報があればこちらに。

View File

@@ -4,7 +4,6 @@ about: Suggest an idea for this project
title: '' title: ''
labels: enhancement labels: enhancement
assignees: syusui-s assignees: syusui-s
--- ---
**Is your feature request related to a problem? Please describe.** **Is your feature request related to a problem? Please describe.**

View File

@@ -4,7 +4,6 @@ about: このプロジェクトにアイデアを提案します
title: '' title: ''
labels: enhancement labels: enhancement
assignees: syusui-s assignees: syusui-s
--- ---
**要望は何らかの問題に関連していますか?説明してください** **要望は何らかの問題に関連していますか?説明してください**

View File

@@ -3,7 +3,6 @@ name: Pull request
about: Suggest changes about: Suggest changes
title: '' title: ''
labels: labels:
--- ---
**Describe the changes** **Describe the changes**

View File

@@ -3,7 +3,6 @@ name: プルリクエスト
about: 変更を提案します about: 変更を提案します
title: '' title: ''
labels: labels:
--- ---
**変更について説明してください** **変更について説明してください**

View File

@@ -3,7 +3,7 @@ name: CI
on: on:
push: push:
branch: branch:
- "*" - '*'
jobs: jobs:
ci: ci:
permissions: permissions:

View File

@@ -5,7 +5,7 @@ A nostr client like TweetDeck made with SolidJS.
## 使い方 ## 使い方
1. NIP-07に対応したブラウザ拡張機能のインストールが事前に必要です 1. NIP-07に対応したブラウザ拡張機能のインストールが事前に必要です
* [NIP-07](https://scrapbox.io/nostr/NIP-07#63e1c10c8b8fcb00000584fc) を参考に拡張機能をインストールしてください。 - [NIP-07](https://scrapbox.io/nostr/NIP-07#63e1c10c8b8fcb00000584fc) を参考に拡張機能をインストールしてください。
1. https://syusui-s.github.io/rabbit/ にアクセス 1. https://syusui-s.github.io/rabbit/ にアクセス
1. NIP-07拡張機能に許可を求められるので許可ボタンを押す 1. NIP-07拡張機能に許可を求められるので許可ボタンを押す
1. タイムラインが表示されます 1. タイムラインが表示されます
@@ -29,11 +29,11 @@ the Free Software Foundation, either version 3 of the License, or
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
### 日本語 ### 日本語
@@ -41,8 +41,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
GNUアフェロー一般公衆利用許諾書バージョン3か、それ以降のいずれかのバージョン GNUアフェロー一般公衆利用許諾書バージョン3か、それ以降のいずれかのバージョン
が定める条件の下で再頒布または改変することができます。 が定める条件の下で再頒布または改変することができます。
このプログラムは有用であることを願って頒布されますが、 *全くの無保証* です。 このプログラムは有用であることを願って頒布されますが、 _全くの無保証_ です。
*商業可能性**特定目的への適合性* に対する保証は言外に示されたものも含め、全く存在しません。 _商業可能性__特定目的への適合性_ に対する保証は言外に示されたものも含め、全く存在しません。
詳しくはGNUアフェロー一般公衆利用許諾書をご覧ください。 詳しくはGNUアフェロー一般公衆利用許諾書をご覧ください。
あなたはこのプログラムと共にGNUアフェロー一般公衆利用許諾書のコピーを一部受け取っているはずです。 あなたはこのプログラムと共にGNUアフェロー一般公衆利用許諾書のコピーを一部受け取っているはずです。

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="ja"> <html lang="ja">
<head prefix="og: http://ogp.me/ns# website: http://ogp.me/ns/website#"> <head prefix="og: http://ogp.me/ns# website: http://ogp.me/ns/website#">
<meta charset="utf-8" /> <meta charset="utf-8" />
@@ -6,12 +6,18 @@
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta name="twitter:card" content="summary" /> <meta name="twitter:card" content="summary" />
<meta name="twitter:creator" content="@syusui-s" /> <meta name="twitter:creator" content="@syusui-s" />
<meta name="twitter:image" content="https://syusui-s.github.io/rabbit/images/rabbit_app_1280.png" /> <meta
name="twitter:image"
content="https://syusui-s.github.io/rabbit/images/rabbit_app_1280.png"
/>
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://syusui-s.github.io/rabbit/" /> <meta property="og:url" content="https://syusui-s.github.io/rabbit/" />
<meta property="og:title" content="🐰rabbit" /> <meta property="og:title" content="🐰rabbit" />
<meta property="og:description" content="Nostr Client" /> <meta property="og:description" content="Nostr Client" />
<meta property="og:image" content="https://syusui-s.github.io/rabbit/images/rabbit_app_1280.png" /> <meta
property="og:image"
content="https://syusui-s.github.io/rabbit/images/rabbit_app_1280.png"
/>
<meta property="og:locale" content="ja_JP" /> <meta property="og:locale" content="ja_JP" />
<link rel="manifest" href="manifest.json" /> <link rel="manifest" href="manifest.json" />
<link rel="shortcut icon" type="image/png" href="./images/rabbit_256.png" /> <link rel="shortcut icon" type="image/png" href="./images/rabbit_256.png" />

View File

@@ -9,9 +9,11 @@
"dev": "npm run generatePackageInfo && vite", "dev": "npm run generatePackageInfo && vite",
"build": "npm run generatePackageInfo && vite build", "build": "npm run generatePackageInfo && vite build",
"serve": "npm run generatePackageInfo && vite preview", "serve": "npm run generatePackageInfo && vite preview",
"eslint": "eslint --cache .", "lint": "npm run prettier && npm run eslint",
"fix": "npm run prettier-fix && npm run eslint-fix",
"eslint": "eslint .",
"prettier": "prettier -c .", "prettier": "prettier -c .",
"eslint-fix": "eslint --cache --fix .", "eslint-fix": "eslint --fix .",
"prettier-fix": "prettier --write .", "prettier-fix": "prettier --write .",
"tsc": "tsc --noEmit --skipLibCheck", "tsc": "tsc --noEmit --skipLibCheck",
"test": "vitest run --no-watch", "test": "vitest run --no-watch",

View File

@@ -1,15 +1,14 @@
<!doctype html> <!doctype html>
<html lang="ja"> <html lang="ja">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Rabbit</title> <title>Rabbit</title>
</head> </head>
<body> <body>
<h1>お探しのページは見つかりませんでした</h1> <h1>お探しのページは見つかりませんでした</h1>
<p> <p>
<a href="/rabbit/">トップに戻る</a> <a href="/rabbit/">トップに戻る</a>
</p> </p>
</body> </body>
</html> </html>

View File

@@ -6,13 +6,16 @@
"display": "browser", "display": "browser",
"background_color": "#fff", "background_color": "#fff",
"description": "A deck style nostr client.", "description": "A deck style nostr client.",
"icons": [{ "icons": [
"src": "images/rabbit_app_256.png", {
"sizes": "256x256", "src": "images/rabbit_app_256.png",
"type": "image/png" "sizes": "256x256",
}, { "type": "image/png"
"src": "images/rabbit_app_1280.png", },
"sizes": "1280x1280", {
"type": "image/png" "src": "images/rabbit_app_1280.png",
}] "sizes": "1280x1280",
"type": "image/png"
}
]
} }

View File

@@ -1,4 +1,4 @@
#!/bin/sh #!/bin/sh
prettier --write "$@" prettier --write "$@"
eslint --cache --fix "$@" eslint --fix "$@"

View File

@@ -76,6 +76,7 @@ const EmojiPicker: Component<EmojiPickerProps> = (props) => {
}, },
}); });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
pickerElement = picker as any as HTMLElement; pickerElement = picker as any as HTMLElement;
popupRef?.elem?.appendChild(pickerElement); popupRef?.elem?.appendChild(pickerElement);
}; };

View File

@@ -1,4 +1,4 @@
import { Component, Show, createEffect, onCleanup, onMount } from 'solid-js'; import { Component, Show } from 'solid-js';
import BookmarkIcon from 'heroicons/24/outline/bookmark.svg'; import BookmarkIcon from 'heroicons/24/outline/bookmark.svg';
@@ -9,7 +9,6 @@ import Bookmark from '@/components/timeline/Bookmark';
import { BookmarkColumnType } from '@/core/column'; import { BookmarkColumnType } from '@/core/column';
import useConfig from '@/core/useConfig'; import useConfig from '@/core/useConfig';
import { useTranslation } from '@/i18n/useTranslation'; import { useTranslation } from '@/i18n/useTranslation';
import useDecrypt from '@/nostr/useDecrypt';
import useParameterizedReplaceableEvent from '@/nostr/useParameterizedReplaceableEvent'; import useParameterizedReplaceableEvent from '@/nostr/useParameterizedReplaceableEvent';
type BookmarkColumnDisplayProps = { type BookmarkColumnDisplayProps = {
@@ -28,6 +27,8 @@ const BookmarkColumn: Component<BookmarkColumnDisplayProps> = (props) => {
identifier: props.column.identifier, identifier: props.column.identifier,
})); }));
// TODO 暗号化されたデータがある場合は復号する
return ( return (
<Column <Column
header={ header={

View File

@@ -1,14 +1,13 @@
import { Component, createEffect, onCleanup, onMount } from 'solid-js'; import { Component } from 'solid-js';
import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg'; import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg';
import { uniq } from 'lodash';
import { Kind } from 'nostr-tools'; import { Kind } from 'nostr-tools';
import BasicColumnHeader from '@/components/column/BasicColumnHeader'; import BasicColumnHeader from '@/components/column/BasicColumnHeader';
import Column from '@/components/column/Column'; import Column from '@/components/column/Column';
import ColumnSettings from '@/components/column/ColumnSettings'; import ColumnSettings from '@/components/column/ColumnSettings';
import Timeline from '@/components/timeline/Timeline'; import Timeline from '@/components/timeline/Timeline';
import { ChannelColumnType, FollowingColumnType } from '@/core/column'; import { ChannelColumnType } from '@/core/column';
import { applyContentFilter } from '@/core/contentFilter'; import { applyContentFilter } from '@/core/contentFilter';
import useConfig from '@/core/useConfig'; import useConfig from '@/core/useConfig';
import { useTranslation } from '@/i18n/useTranslation'; import { useTranslation } from '@/i18n/useTranslation';

View File

@@ -19,14 +19,12 @@ type ColumnSettingsSectionProps = {
children: JSX.Element; children: JSX.Element;
}; };
const ColumnSettingsSection: Component<ColumnSettingsSectionProps> = (props) => { const ColumnSettingsSection: Component<ColumnSettingsSectionProps> = (props) => (
return ( <div class="flex flex-col gap-2 border-b p-2">
<div class="flex flex-col gap-2 border-b p-2"> <div>{props.title}</div>
<div>{props.title}</div> <div>{props.children}</div>
<div>{props.children}</div> </div>
</div> );
);
};
const ColumnSettings: Component<ColumnSettingsProps> = (props) => { const ColumnSettings: Component<ColumnSettingsProps> = (props) => {
const i18n = useTranslation(); const i18n = useTranslation();

View File

@@ -39,10 +39,10 @@ const EventDisplay: Component<EventDisplayProps> = (props) => {
<EventLink eventId={props.event.id} kind={props.event.kind} /> <EventLink eventId={props.event.id} kind={props.event.kind} />
</span> </span>
</Match> </Match>
<Match when={props.event.kind === Kind.Text}> <Match when={props.event.kind === (Kind.Text as number)}>
<TextNote event={props.event} embedding={props.actions} actions={props.actions} /> <TextNote event={props.event} embedding={props.actions} actions={props.actions} />
</Match> </Match>
<Match when={(props.event.kind as number) === 6}> <Match when={props.event.kind === (Kind.Repost as number)}>
<Repost event={props.event} /> <Repost event={props.event} />
</Match> </Match>
</Switch> </Switch>

View File

@@ -1,24 +0,0 @@
import { Component } from 'solid-js';
import DocumentText from 'heroicons/24/outline/document-text.svg';
import { Kind, Event as NostrEvent } from 'nostr-tools';
import { genericEvent } from '@/nostr/event';
export type LongFormContentProps = {
event: NostrEvent;
};
const LongFormContent: Component<LongFormContentProps> = (props) => (
// const event = () => genericEvent(props.event);
<button class="flex flex-col gap-1 px-1">
<div class="flex items-center gap-1">
<span class="inline-block h-4 w-4 text-purple-400">
<DocumentText />
</span>
<span>TODO</span>
</div>
</button>
);
export default LongFormContent;

View File

@@ -1,6 +1,5 @@
import { Show, type Component } from 'solid-js'; import { Show, type Component } from 'solid-js';
import ColumnItem from '@/components/ColumnItem';
// eslint-disable-next-line import/no-cycle // eslint-disable-next-line import/no-cycle
import TextNoteDisplay, { TextNoteDisplayProps } from '@/components/event/textNote/TextNoteDisplay'; import TextNoteDisplay, { TextNoteDisplayProps } from '@/components/event/textNote/TextNoteDisplay';
import useConfig from '@/core/useConfig'; import useConfig from '@/core/useConfig';

View File

@@ -1,13 +1,13 @@
import { Component } from 'solid-js'; import { Component } from 'solid-js';
import Bell from 'heroicons/24/outline/bell.svg'; import Bell from 'heroicons/24/outline/bell.svg';
import BookmarkIcon from 'heroicons/24/outline/bookmark.svg';
import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg';
import GlobeAlt from 'heroicons/24/outline/globe-alt.svg'; import GlobeAlt from 'heroicons/24/outline/globe-alt.svg';
import Heart from 'heroicons/24/outline/heart.svg'; import Heart from 'heroicons/24/outline/heart.svg';
import Home from 'heroicons/24/outline/home.svg'; import Home from 'heroicons/24/outline/home.svg';
import MagnifyingGlass from 'heroicons/24/outline/magnifying-glass.svg'; import MagnifyingGlass from 'heroicons/24/outline/magnifying-glass.svg';
import User from 'heroicons/24/outline/user.svg'; import User from 'heroicons/24/outline/user.svg';
// import BookmarkIcon from 'heroicons/24/outline/bookmark.svg';
// import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg';
import BasicModal from '@/components/modal/BasicModal'; import BasicModal from '@/components/modal/BasicModal';
import { import {

View File

@@ -1,4 +1,4 @@
import { Component, For, createMemo } from 'solid-js'; import { Component, createMemo } from 'solid-js';
import { type Event as NostrEvent } from 'nostr-tools'; import { type Event as NostrEvent } from 'nostr-tools';

View File

@@ -25,7 +25,6 @@ import useVerification from '@/nostr/useVerification';
import ensureNonNull from '@/utils/ensureNonNull'; import ensureNonNull from '@/utils/ensureNonNull';
import epoch from '@/utils/epoch'; import epoch from '@/utils/epoch';
import npubEncodeFallback from '@/utils/npubEncodeFallback'; import npubEncodeFallback from '@/utils/npubEncodeFallback';
import stripMargin from '@/utils/stripMargin';
import timeout from '@/utils/timeout'; import timeout from '@/utils/timeout';
export type ProfileDisplayProps = { export type ProfileDisplayProps = {
@@ -83,7 +82,6 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
})), })),
); );
const following = () => myFollowingPubkeys().includes(props.pubkey); const following = () => myFollowingPubkeys().includes(props.pubkey);
const refetchMyFollowing = () => myFollowingQuery.refetch();
const { followingPubkeys: userFollowingPubkeys, query: userFollowingQuery } = useFollowings( const { followingPubkeys: userFollowingPubkeys, query: userFollowingQuery } = useFollowings(
() => ({ pubkey: props.pubkey }), () => ({ pubkey: props.pubkey }),

View File

@@ -6,7 +6,6 @@ import ColumnItem from '@/components/ColumnItem';
import Reaction from '@/components/event/Reaction'; import Reaction from '@/components/event/Reaction';
import Repost from '@/components/event/Repost'; import Repost from '@/components/event/Repost';
import TextNote from '@/components/event/TextNote'; import TextNote from '@/components/event/TextNote';
import ZapReceipt from '@/components/event/ZapReceipt';
import useConfig from '@/core/useConfig'; import useConfig from '@/core/useConfig';
export type NotificationProps = { export type NotificationProps = {
@@ -21,29 +20,22 @@ const Notification: Component<NotificationProps> = (props) => {
{(event) => ( {(event) => (
<Show when={!shouldMuteEvent(event)}> <Show when={!shouldMuteEvent(event)}>
<Switch fallback={<div>unknown event</div>}> <Switch fallback={<div>unknown event</div>}>
<Match when={event.kind === Kind.Text}> <Match when={event.kind === (Kind.Text as number)}>
<ColumnItem> <ColumnItem>
<TextNote event={event} /> <TextNote event={event} />
</ColumnItem> </ColumnItem>
</Match> </Match>
<Match when={event.kind === Kind.Reaction}> <Match when={event.kind === (Kind.Reaction as number)}>
<ColumnItem> <ColumnItem>
<Reaction event={event} /> <Reaction event={event} />
</ColumnItem> </ColumnItem>
</Match> </Match>
{/* TODO ちゃんとnotification用のコンポーネント使う */} {/* TODO ちゃんとnotification用のコンポーネント使う */}
<Match when={(event.kind as number) === 6}> <Match when={event.kind === (Kind.Repost as number)}>
<ColumnItem> <ColumnItem>
<Repost event={event} /> <Repost event={event} />
</ColumnItem> </ColumnItem>
</Match> </Match>
{/*
<Match when={event.kind === Kind.Zap}>
<ColumnItem>
<ZapReceipt event={event} />
</ColumnItem>
</Match>
*/}
</Switch> </Switch>
</Show> </Show>
)} )}

View File

@@ -1,4 +1,4 @@
import { createSignal, Show, type Component, type JSX } from 'solid-js'; import { createSignal, Show, type Component } from 'solid-js';
import ClipboardDocument from 'heroicons/24/outline/clipboard-document.svg'; import ClipboardDocument from 'heroicons/24/outline/clipboard-document.svg';
@@ -13,7 +13,7 @@ const Copy: Component<CopyProps> = (props) => {
const handleClick = () => { const handleClick = () => {
navigator.clipboard navigator.clipboard
.writeText(props.text) .writeText(props.text)
.then((e) => { .then(() => {
setShowPopup(true); setShowPopup(true);
setTimeout(() => setShowPopup(false), 1000); setTimeout(() => setShowPopup(false), 1000);
}) })

View File

@@ -181,7 +181,7 @@ const useConfig = (): UseConfig => {
const isPubkeyMuted = (pubkey: string) => config.mutedPubkeys.includes(pubkey); const isPubkeyMuted = (pubkey: string) => config.mutedPubkeys.includes(pubkey);
const hasMutedKeyword = (event: NostrEvent) => { const hasMutedKeyword = (event: NostrEvent) => {
if (event.kind === Kind.Text) { if (event.kind === (Kind.Text as number)) {
return config.mutedKeywords.some((keyword) => event.content.includes(keyword)); return config.mutedKeywords.some((keyword) => event.content.includes(keyword));
} }
return false; return false;
@@ -192,7 +192,7 @@ const useConfig = (): UseConfig => {
return ( return (
isPubkeyMuted(event.pubkey) || isPubkeyMuted(event.pubkey) ||
ev.taggedPubkeys().some(isPubkeyMuted) || ev.taggedPubkeys().some(isPubkeyMuted) ||
(event.kind === Kind.Text && hasMutedKeyword(event)) (event.kind === (Kind.Text as number) && hasMutedKeyword(event))
); );
}; };

View File

@@ -1,11 +0,0 @@
import { createSignal, type JSX } from 'solid-js';
const useFileInput = () => {
const [file, setFile] = createSignal<File | undefined>();
const handleChange: JSX.EventHandler<HTMLInputElement, Event> = (ev) => {
setFile(ev.currentTarget.files?.[0]);
};
return { file, handleChange };
};

View File

@@ -16,6 +16,7 @@ export type MessageChannelRequest<T> = {
export type MessageChannelResponse<T> = export type MessageChannelResponse<T> =
| { requestId: string; ok: true; response: T } | { requestId: string; ok: true; response: T }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
| { requestId: string; ok: false; error: any }; | { requestId: string; ok: false; error: any };
const [channels, setChannels]: Signal<Record<string, MessageChannel>> = createSignal({}); const [channels, setChannels]: Signal<Record<string, MessageChannel>> = createSignal({});

View File

@@ -5,14 +5,14 @@ export type UseResizedImageProps = {
imageUrl: Accessor<string | undefined>; imageUrl: Accessor<string | undefined>;
width: number; width: number;
height: number; height: number;
encoderOption?: number; encoderOptions?: number;
}; };
const useResizedImage = ({ const useResizedImage = ({
imageUrl, imageUrl,
width, width,
height, height,
encoderOption, encoderOptions,
}: UseResizedImageProps): Accessor<string | undefined> => { }: UseResizedImageProps): Accessor<string | undefined> => {
const [resizedImage, setResizedImage] = createSignal<string | undefined>(undefined); const [resizedImage, setResizedImage] = createSignal<string | undefined>(undefined);
@@ -39,7 +39,7 @@ const useResizedImage = ({
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, dw, dh); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, dw, dh);
const dataUrl = canvas.toDataURL('image/jpeg'); const dataUrl = canvas.toDataURL('image/jpeg', encoderOptions);
setResizedImage(dataUrl); setResizedImage(dataUrl);
}); });

View File

@@ -1,7 +1,4 @@
// const commands = ['openPostForm'] as const; import { onMount, onCleanup } from 'solid-js';
// type Commands = (typeof commands)[number];
import { onMount, onCleanup, type JSX } from 'solid-js';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';

View File

@@ -1,4 +1,4 @@
import { Kind, Event as NostrEvent } from 'nostr-tools'; import { Event as NostrEvent } from 'nostr-tools';
import GenericEvent from '@/nostr/event/GenericEvent'; import GenericEvent from '@/nostr/event/GenericEvent';
import Reaction from '@/nostr/event/Reaction'; import Reaction from '@/nostr/event/Reaction';

View File

@@ -1,44 +0,0 @@
import { Event as NostrEvent, Kind } from 'nostr-tools';
import GenericEvent from '@/nostr/event/GenericEvent';
class LongFormContent extends GenericEvent {
constructor(rawEvent: NostrEvent) {
if (rawEvent.kind !== Kind.Article) {
throw new TypeError('kind should be 30023');
}
super(rawEvent);
}
#getMeta(tagName: string): string | null {
const tags = this.findTagsByName(tagName);
if (tags.length === 0) return null;
const [, value] = tags[0];
return value;
}
title(): string | null {
return this.#getMeta('title');
}
image(): string | null {
return this.#getMeta('image');
}
summary(): string | null {
return this.#getMeta('image');
}
publishedAt(): string | null {
return this.#getMeta('publishedAt');
}
publishedAtAsDate(): Date | null {
const publishedAt = this.publishedAt();
if (publishedAt == null) return null;
return new Date(parseInt(publishedAt, 10) * 1000);
}
}
export default LongFormContent;

View File

@@ -19,6 +19,7 @@ export type NonStandardProfile = {
export type Profile = StandardProfile & NonStandardProfile; export type Profile = StandardProfile & NonStandardProfile;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ProfileWithOtherProperties = Profile & Record<string, any>; export type ProfileWithOtherProperties = Profile & Record<string, any>;
export const parseProfile = (content: string | null): Profile => { export const parseProfile = (content: string | null): Profile => {

View File

@@ -30,7 +30,7 @@ const reactionToReactionTypes = (event: Reaction): ReactionTypes => {
export default class Reaction extends GenericEvent { export default class Reaction extends GenericEvent {
constructor(rawEvent: NostrEvent) { constructor(rawEvent: NostrEvent) {
if (rawEvent.kind !== Kind.Reaction) { if (rawEvent.kind !== (Kind.Reaction as number)) {
throw new TypeError('kind should be 7'); throw new TypeError('kind should be 7');
} }
super(rawEvent); super(rawEvent);

View File

@@ -1,5 +1,4 @@
import { Event as NostrEvent, Kind } from 'nostr-tools'; import { Event as NostrEvent, Kind } from 'nostr-tools';
import { z } from 'zod';
import GenericEvent from '@/nostr/event/GenericEvent'; import GenericEvent from '@/nostr/event/GenericEvent';
import isValidId from '@/nostr/event/isValidId'; import isValidId from '@/nostr/event/isValidId';
@@ -9,7 +8,6 @@ import parseTextNote, {
ParsedTextNote, ParsedTextNote,
TagReference, TagReference,
} from '@/nostr/parseTextNote'; } from '@/nostr/parseTextNote';
import ensureSchema from '@/utils/ensureSchema';
export type EventMarker = 'reply' | 'root' | 'mention'; export type EventMarker = 'reply' | 'root' | 'mention';
@@ -69,7 +67,7 @@ export default class TextNote extends GenericEvent {
#memoizedParsed: ParsedTextNote | undefined; #memoizedParsed: ParsedTextNote | undefined;
constructor(rawEvent: NostrEvent) { constructor(rawEvent: NostrEvent) {
if (rawEvent.kind !== Kind.Text) { if (rawEvent.kind !== (Kind.Text as number)) {
throw new TypeError('kind should be 1'); throw new TypeError('kind should be 1');
} }
super(rawEvent); super(rawEvent);

View File

@@ -1,9 +1,8 @@
import assert from 'assert'; import assert from 'assert';
import { type Event as NostrEvent } from 'nostr-tools';
import { describe, it } from 'vitest'; import { describe, it } from 'vitest';
import parseTextNote, { type ParsedTextNoteNode, TagReference } from '@/nostr/parseTextNote'; import parseTextNote, { type ParsedTextNoteNode } from '@/nostr/parseTextNote';
describe('parseTextNote', () => { describe('parseTextNote', () => {
/* /*

View File

@@ -1,4 +1,4 @@
import { nip19, type Event as NostrEvent } from 'nostr-tools'; import { nip19 } from 'nostr-tools';
import { DecodeResult } from 'nostr-tools/lib/nip19'; import { DecodeResult } from 'nostr-tools/lib/nip19';
const { decode } = nip19; const { decode } = nip19;

View File

@@ -162,33 +162,33 @@ const { addTask, removeTask } = useBatch<BatchedEventsTask>(() => ({
count += 1; count += 1;
sub.on('event', (event: NostrEvent & { id: string }) => { sub.on('event', (event: NostrEvent & { id: string }) => {
if (event.kind === Kind.Metadata) { if (event.kind === (Kind.Metadata as number)) {
const registeredTasks = profileTasks.get(event.pubkey) ?? []; const registeredTasks = profileTasks.get(event.pubkey) ?? [];
resolveTasks(registeredTasks, event); resolveTasks(registeredTasks, event);
return; return;
} }
if (event.kind === Kind.Reaction) { if (event.kind === (Kind.Reaction as number)) {
// Use the last event id // Use the last event id
const id = genericEvent(event).lastTaggedEventId(); const id = genericEvent(event).lastTaggedEventId();
if (id != null) { if (id != null) {
const registeredTasks = reactionsTasks.get(id) ?? []; const registeredTasks = reactionsTasks.get(id) ?? [];
resolveTasks(registeredTasks, event); resolveTasks(registeredTasks, event);
} }
} else if ((event.kind as number) === 6) { } else if (event.kind === (Kind.Repost as number)) {
// Use the last event id // Use the last event id
const id = genericEvent(event).lastTaggedEventId(); const id = genericEvent(event).lastTaggedEventId();
if (id != null) { if (id != null) {
const registeredTasks = repostsTasks.get(id) ?? []; const registeredTasks = repostsTasks.get(id) ?? [];
resolveTasks(registeredTasks, event); resolveTasks(registeredTasks, event);
} }
} else if (event.kind === Kind.Zap) { } else if (event.kind === (Kind.Zap as number)) {
const eTags = genericEvent(event).eTags(); const eTags = genericEvent(event).eTags();
eTags.forEach(([, id]) => { eTags.forEach(([, id]) => {
const registeredTasks = repostsTasks.get(id) ?? []; const registeredTasks = repostsTasks.get(id) ?? [];
resolveTasks(registeredTasks, event); resolveTasks(registeredTasks, event);
}); });
} else if (event.kind === Kind.Contacts) { } else if (event.kind === (Kind.Contacts as number)) {
const registeredTasks = followingsTasks.get(event.pubkey) ?? []; const registeredTasks = followingsTasks.get(event.pubkey) ?? [];
resolveTasks(registeredTasks, event); resolveTasks(registeredTasks, event);
} else if (isParameterizedReplaceableEvent(event)) { } else if (isParameterizedReplaceableEvent(event)) {

View File

@@ -6,7 +6,9 @@ type UseDecryptProps = {
encrypted: string; encrypted: string;
}; };
// eslint-disable-next-line solid/reactivity
const [memo, setMemo] = createRoot(() => createSignal<Record<string, string>>({})); const [memo, setMemo] = createRoot(() => createSignal<Record<string, string>>({}));
// eslint-disable-next-line solid/reactivity
const [decrypting, setDecrypting] = createRoot(() => createSignal<Record<string, boolean>>({})); const [decrypting, setDecrypting] = createRoot(() => createSignal<Record<string, boolean>>({}));
const useDecrypt = (propsProvider: () => UseDecryptProps | null) => { const useDecrypt = (propsProvider: () => UseDecryptProps | null) => {

View File

@@ -1,6 +1,6 @@
import { createMemo } from 'solid-js'; import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query'; import { createQuery, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools'; import { Event as NostrEvent } from 'nostr-tools';
import { registerTask, BatchedEventsTask } from '@/nostr/useBatchedEvents'; import { registerTask, BatchedEventsTask } from '@/nostr/useBatchedEvents';

View File

@@ -1,4 +1,4 @@
import { createMemo, observable } from 'solid-js'; import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query'; import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools'; import { Event as NostrEvent } from 'nostr-tools';

View File

@@ -16,7 +16,7 @@ const useVerification = (propsProvider: () => UseVerificationProps | null): UseV
const props = createMemo(propsProvider); const props = createMemo(propsProvider);
const query = createQuery( const query = createQuery(
() => ['useVerification', props()] as const, () => ['useVerification', props()] as const,
({ queryKey, signal }) => { ({ queryKey }) => {
const [, currentProps] = queryKey; const [, currentProps] = queryKey;
if (currentProps == null) return Promise.resolve(null); if (currentProps == null) return Promise.resolve(null);
const { nip05: nip05string } = currentProps; const { nip05: nip05string } = currentProps;

View File

@@ -6,7 +6,6 @@ import Columns from '@/components/column/Columns';
import GlobalModal from '@/components/modal/GlobalModal'; import GlobalModal from '@/components/modal/GlobalModal';
import SideBar from '@/components/SideBar'; import SideBar from '@/components/SideBar';
import useConfig from '@/core/useConfig'; import useConfig from '@/core/useConfig';
import useModalState from '@/hooks/useModalState';
import usePersistStatus from '@/hooks/usePersistStatus'; import usePersistStatus from '@/hooks/usePersistStatus';
import { useMountShortcutKeys } from '@/hooks/useShortcutKeys'; import { useMountShortcutKeys } from '@/hooks/useShortcutKeys';
import usePool from '@/nostr/usePool'; import usePool from '@/nostr/usePool';

View File

@@ -1,18 +1,15 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any /* eslint-disable @typescript-eslint/no-explicit-any */
export type TupleNonNull<T extends readonly any[]> = { export type TupleNonNull<T extends readonly any[]> = {
[P in keyof T]: NonNullable<T[P]>; [P in keyof T]: NonNullable<T[P]>;
}; };
const ensureNonNull = const ensureNonNull =
// eslint-disable-next-line @typescript-eslint/no-explicit-any <T extends readonly any[]>(tuple: T) =>
<R>(f: (tupleNonNull: TupleNonNull<T>) => R): R | null => {
if (tuple.some((e) => e == null)) {
<T extends readonly any[]>(tuple: T) => return null;
<R>(f: (tupleNonNull: TupleNonNull<T>) => R): R | null => { }
if (tuple.some((e) => e == null)) { return f(tuple as TupleNonNull<T>);
return null; };
}
return f(tuple as TupleNonNull<T>);
};
export default ensureNonNull; export default ensureNonNull;

View File

@@ -2,6 +2,7 @@ import { z } from 'zod';
const ensureSchema = const ensureSchema =
<T>(schema: z.Schema<T>) => <T>(schema: z.Schema<T>) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(value: any): value is T => (value: any): value is T =>
schema.safeParse(value).success; schema.safeParse(value).success;

View File

@@ -36,17 +36,6 @@ export type NostrBuildResult = {
}[]; }[];
}; };
const toHexString = (buff: ArrayBuffer): string => {
const arr = new Array(buff.byteLength);
const view = new Int8Array(buff);
for (let i = 0; i < view.byteLength; i += 1) {
arr[i] = view[i].toString(16).padStart(2, '0');
}
return arr.join();
};
export const uploadNostrBuild = async (blob: Blob): Promise<UploadResult> => { export const uploadNostrBuild = async (blob: Blob): Promise<UploadResult> => {
const form = new FormData(); const form = new FormData();
form.set('file', blob); form.set('file', blob);
@@ -69,27 +58,6 @@ export const uploadNostrBuild = async (blob: Blob): Promise<UploadResult> => {
return { imageUrl: result.data[0].url }; return { imageUrl: result.data[0].url };
}; };
export const uploadVoidCat = async (blob: Blob): Promise<any> => {
const data = await blob.arrayBuffer();
const digestBuffer = await window.crypto.subtle.digest('SHA-256', data);
const digest = toHexString(digestBuffer);
const res = await fetch('https://void.cat/upload', {
method: 'POST',
headers: {
Accept: 'application/json',
'V-Content-Type': blob.type,
'V-Full-Digest': digest,
},
mode: 'cors',
body: data,
});
if (!res.ok) throw new Error('failed to post image: status code was not 2xx');
return res.json();
};
export const uploaders = { export const uploaders = {
nostrBuild: { nostrBuild: {
name: 'nostr.build', name: 'nostr.build',

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const stripMargin = (strings: TemplateStringsArray, ...values: any[]) => { const stripMargin = (strings: TemplateStringsArray, ...values: any[]) => {
const s = String.raw(strings, values); const s = String.raw(strings, values);

View File

@@ -5,11 +5,7 @@
"module": "ESNext", "module": "ESNext",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"lib": [ "lib": ["dom", "dom.iterable", "esnext"],
"dom",
"dom.iterable",
"esnext"
],
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"esModuleInterop": true, "esModuleInterop": true,
@@ -20,19 +16,10 @@
"isolatedModules": true, "isolatedModules": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["src/*"], "@/*": ["src/*"]
}, },
"incremental": true "incremental": true
}, },
"include": [ "include": ["vite.config.ts", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "./.eslintrc.js"],
"vite.config.ts", "exclude": ["node_modules"]
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.jsx",
"./.eslintrc.js"
],
"exclude": [
"node_modules"
]
} }