mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-17 14:04:21 +01:00
fix: eslint errors
This commit is contained in:
@@ -81,6 +81,13 @@ module.exports = {
|
||||
'created-at',
|
||||
'actions',
|
||||
'content',
|
||||
'profile',
|
||||
'profile-icon',
|
||||
'profile-name',
|
||||
'profile-username',
|
||||
'notification-icon',
|
||||
'notification-user',
|
||||
'notification-event',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -4,7 +4,6 @@ about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: syusui-s
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
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.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
18
.github/ISSUE_TEMPLATE/bug_report_jp.md
vendored
18
.github/ISSUE_TEMPLATE/bug_report_jp.md
vendored
@@ -4,7 +4,6 @@ about: 改善に取り組めるように報告を作成します
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: syusui-s
|
||||
|
||||
---
|
||||
|
||||
**バグの説明**
|
||||
@@ -12,6 +11,7 @@ assignees: syusui-s
|
||||
|
||||
**再現手順**
|
||||
発生させる手順を説明してください:
|
||||
|
||||
1. '...'に移動
|
||||
2. '....'をクリック
|
||||
3. '....'までスクロールする
|
||||
@@ -24,15 +24,17 @@ assignees: syusui-s
|
||||
スクリーンショットがあればこちらに。
|
||||
|
||||
**デスクトップ (該当する場合は以下の情報を埋めてください):**
|
||||
- OS: (例: Windows 11, macOS Ventura)
|
||||
- ブラウザ: (例: Chrome, Firefox, Safari, Edge)
|
||||
- バージョン: (例: 110)
|
||||
|
||||
- OS: (例: Windows 11, macOS Ventura)
|
||||
- ブラウザ: (例: Chrome, Firefox, Safari, Edge)
|
||||
- バージョン: (例: 110)
|
||||
|
||||
**スマートフォン (該当する場合は以下の情報を埋めてください):**
|
||||
- デバイス: (例: iPhone 12)
|
||||
- OS: (例: iOS 16.2, Android 13)
|
||||
- ブラウザ: (例: Safari, Chrome)
|
||||
- バージョン: (例: 110)
|
||||
|
||||
- デバイス: (例: iPhone 12)
|
||||
- OS: (例: iOS 16.2, Android 13)
|
||||
- ブラウザ: (例: Safari, Chrome)
|
||||
- バージョン: (例: 110)
|
||||
|
||||
**追加の情報**
|
||||
問題に関する他の情報があればこちらに。
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -4,7 +4,6 @@ about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: syusui-s
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/feature_request_jp.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request_jp.md
vendored
@@ -4,7 +4,6 @@ about: このプロジェクトにアイデアを提案します
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: syusui-s
|
||||
|
||||
---
|
||||
|
||||
**要望は何らかの問題に関連していますか?説明してください**
|
||||
|
||||
@@ -3,7 +3,6 @@ name: Pull request
|
||||
about: Suggest changes
|
||||
title: ''
|
||||
labels:
|
||||
|
||||
---
|
||||
|
||||
**Describe the changes**
|
||||
|
||||
@@ -3,7 +3,6 @@ name: プルリクエスト
|
||||
about: 変更を提案します
|
||||
title: ''
|
||||
labels:
|
||||
|
||||
---
|
||||
|
||||
**変更について説明してください**
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -3,7 +3,7 @@ name: CI
|
||||
on:
|
||||
push:
|
||||
branch:
|
||||
- "*"
|
||||
- '*'
|
||||
jobs:
|
||||
ci:
|
||||
permissions:
|
||||
|
||||
@@ -5,7 +5,7 @@ A nostr client like TweetDeck made with SolidJS.
|
||||
## 使い方
|
||||
|
||||
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. NIP-07拡張機能に許可を求められるので許可ボタンを押す
|
||||
1. タイムラインが表示されます
|
||||
@@ -41,8 +41,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
GNUアフェロー一般公衆利用許諾書(バージョン3か、それ以降のいずれかのバージョン)
|
||||
が定める条件の下で再頒布または改変することができます。
|
||||
|
||||
このプログラムは有用であることを願って頒布されますが、 *全くの無保証* です。
|
||||
*商業可能性* や *特定目的への適合性* に対する保証は言外に示されたものも含め、全く存在しません。
|
||||
このプログラムは有用であることを願って頒布されますが、 _全くの無保証_ です。
|
||||
_商業可能性_ や _特定目的への適合性_ に対する保証は言外に示されたものも含め、全く存在しません。
|
||||
詳しくはGNUアフェロー一般公衆利用許諾書をご覧ください。
|
||||
|
||||
あなたはこのプログラムと共にGNUアフェロー一般公衆利用許諾書のコピーを一部受け取っているはずです。
|
||||
|
||||
12
index.html
12
index.html
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head prefix="og: http://ogp.me/ns# website: http://ogp.me/ns/website#">
|
||||
<meta charset="utf-8" />
|
||||
@@ -6,12 +6,18 @@
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<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:url" content="https://syusui-s.github.io/rabbit/" />
|
||||
<meta property="og:title" content="🐰rabbit" />
|
||||
<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" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link rel="shortcut icon" type="image/png" href="./images/rabbit_256.png" />
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
"dev": "npm run generatePackageInfo && vite",
|
||||
"build": "npm run generatePackageInfo && vite build",
|
||||
"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 .",
|
||||
"eslint-fix": "eslint --cache --fix .",
|
||||
"eslint-fix": "eslint --fix .",
|
||||
"prettier-fix": "prettier --write .",
|
||||
"tsc": "tsc --noEmit --skipLibCheck",
|
||||
"test": "vitest run --no-watch",
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Rabbit</title>
|
||||
</head>
|
||||
<body>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Rabbit</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>お探しのページは見つかりませんでした</h1>
|
||||
<p>
|
||||
<a href="/rabbit/">トップに戻る</a>
|
||||
</p>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
"display": "browser",
|
||||
"background_color": "#fff",
|
||||
"description": "A deck style nostr client.",
|
||||
"icons": [{
|
||||
"icons": [
|
||||
{
|
||||
"src": "images/rabbit_app_256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"src": "images/rabbit_app_1280.png",
|
||||
"sizes": "1280x1280",
|
||||
"type": "image/png"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
prettier --write "$@"
|
||||
eslint --cache --fix "$@"
|
||||
eslint --fix "$@"
|
||||
|
||||
@@ -76,6 +76,7 @@ const EmojiPicker: Component<EmojiPickerProps> = (props) => {
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
pickerElement = picker as any as HTMLElement;
|
||||
popupRef?.elem?.appendChild(pickerElement);
|
||||
};
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -9,7 +9,6 @@ import Bookmark from '@/components/timeline/Bookmark';
|
||||
import { BookmarkColumnType } from '@/core/column';
|
||||
import useConfig from '@/core/useConfig';
|
||||
import { useTranslation } from '@/i18n/useTranslation';
|
||||
import useDecrypt from '@/nostr/useDecrypt';
|
||||
import useParameterizedReplaceableEvent from '@/nostr/useParameterizedReplaceableEvent';
|
||||
|
||||
type BookmarkColumnDisplayProps = {
|
||||
@@ -28,6 +27,8 @@ const BookmarkColumn: Component<BookmarkColumnDisplayProps> = (props) => {
|
||||
identifier: props.column.identifier,
|
||||
}));
|
||||
|
||||
// TODO 暗号化されたデータがある場合は復号する
|
||||
|
||||
return (
|
||||
<Column
|
||||
header={
|
||||
|
||||
@@ -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 { uniq } from 'lodash';
|
||||
import { Kind } from 'nostr-tools';
|
||||
|
||||
import BasicColumnHeader from '@/components/column/BasicColumnHeader';
|
||||
import Column from '@/components/column/Column';
|
||||
import ColumnSettings from '@/components/column/ColumnSettings';
|
||||
import Timeline from '@/components/timeline/Timeline';
|
||||
import { ChannelColumnType, FollowingColumnType } from '@/core/column';
|
||||
import { ChannelColumnType } from '@/core/column';
|
||||
import { applyContentFilter } from '@/core/contentFilter';
|
||||
import useConfig from '@/core/useConfig';
|
||||
import { useTranslation } from '@/i18n/useTranslation';
|
||||
|
||||
@@ -19,14 +19,12 @@ type ColumnSettingsSectionProps = {
|
||||
children: JSX.Element;
|
||||
};
|
||||
|
||||
const ColumnSettingsSection: Component<ColumnSettingsSectionProps> = (props) => {
|
||||
return (
|
||||
const ColumnSettingsSection: Component<ColumnSettingsSectionProps> = (props) => (
|
||||
<div class="flex flex-col gap-2 border-b p-2">
|
||||
<div>{props.title}</div>
|
||||
<div>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
||||
const ColumnSettings: Component<ColumnSettingsProps> = (props) => {
|
||||
const i18n = useTranslation();
|
||||
|
||||
@@ -39,10 +39,10 @@ const EventDisplay: Component<EventDisplayProps> = (props) => {
|
||||
<EventLink eventId={props.event.id} kind={props.event.kind} />
|
||||
</span>
|
||||
</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} />
|
||||
</Match>
|
||||
<Match when={(props.event.kind as number) === 6}>
|
||||
<Match when={props.event.kind === (Kind.Repost as number)}>
|
||||
<Repost event={props.event} />
|
||||
</Match>
|
||||
</Switch>
|
||||
|
||||
@@ -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;
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Show, type Component } from 'solid-js';
|
||||
|
||||
import ColumnItem from '@/components/ColumnItem';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import TextNoteDisplay, { TextNoteDisplayProps } from '@/components/event/textNote/TextNoteDisplay';
|
||||
import useConfig from '@/core/useConfig';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Component } from 'solid-js';
|
||||
|
||||
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 Heart from 'heroicons/24/outline/heart.svg';
|
||||
import Home from 'heroicons/24/outline/home.svg';
|
||||
import MagnifyingGlass from 'heroicons/24/outline/magnifying-glass.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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import useVerification from '@/nostr/useVerification';
|
||||
import ensureNonNull from '@/utils/ensureNonNull';
|
||||
import epoch from '@/utils/epoch';
|
||||
import npubEncodeFallback from '@/utils/npubEncodeFallback';
|
||||
import stripMargin from '@/utils/stripMargin';
|
||||
import timeout from '@/utils/timeout';
|
||||
|
||||
export type ProfileDisplayProps = {
|
||||
@@ -83,7 +82,6 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
||||
})),
|
||||
);
|
||||
const following = () => myFollowingPubkeys().includes(props.pubkey);
|
||||
const refetchMyFollowing = () => myFollowingQuery.refetch();
|
||||
|
||||
const { followingPubkeys: userFollowingPubkeys, query: userFollowingQuery } = useFollowings(
|
||||
() => ({ pubkey: props.pubkey }),
|
||||
|
||||
@@ -6,7 +6,6 @@ import ColumnItem from '@/components/ColumnItem';
|
||||
import Reaction from '@/components/event/Reaction';
|
||||
import Repost from '@/components/event/Repost';
|
||||
import TextNote from '@/components/event/TextNote';
|
||||
import ZapReceipt from '@/components/event/ZapReceipt';
|
||||
import useConfig from '@/core/useConfig';
|
||||
|
||||
export type NotificationProps = {
|
||||
@@ -21,29 +20,22 @@ const Notification: Component<NotificationProps> = (props) => {
|
||||
{(event) => (
|
||||
<Show when={!shouldMuteEvent(event)}>
|
||||
<Switch fallback={<div>unknown event</div>}>
|
||||
<Match when={event.kind === Kind.Text}>
|
||||
<Match when={event.kind === (Kind.Text as number)}>
|
||||
<ColumnItem>
|
||||
<TextNote event={event} />
|
||||
</ColumnItem>
|
||||
</Match>
|
||||
<Match when={event.kind === Kind.Reaction}>
|
||||
<Match when={event.kind === (Kind.Reaction as number)}>
|
||||
<ColumnItem>
|
||||
<Reaction event={event} />
|
||||
</ColumnItem>
|
||||
</Match>
|
||||
{/* TODO ちゃんとnotification用のコンポーネント使う */}
|
||||
<Match when={(event.kind as number) === 6}>
|
||||
<Match when={event.kind === (Kind.Repost as number)}>
|
||||
<ColumnItem>
|
||||
<Repost event={event} />
|
||||
</ColumnItem>
|
||||
</Match>
|
||||
{/*
|
||||
<Match when={event.kind === Kind.Zap}>
|
||||
<ColumnItem>
|
||||
<ZapReceipt event={event} />
|
||||
</ColumnItem>
|
||||
</Match>
|
||||
*/}
|
||||
</Switch>
|
||||
</Show>
|
||||
)}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -13,7 +13,7 @@ const Copy: Component<CopyProps> = (props) => {
|
||||
const handleClick = () => {
|
||||
navigator.clipboard
|
||||
.writeText(props.text)
|
||||
.then((e) => {
|
||||
.then(() => {
|
||||
setShowPopup(true);
|
||||
setTimeout(() => setShowPopup(false), 1000);
|
||||
})
|
||||
|
||||
@@ -181,7 +181,7 @@ const useConfig = (): UseConfig => {
|
||||
const isPubkeyMuted = (pubkey: string) => config.mutedPubkeys.includes(pubkey);
|
||||
|
||||
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 false;
|
||||
@@ -192,7 +192,7 @@ const useConfig = (): UseConfig => {
|
||||
return (
|
||||
isPubkeyMuted(event.pubkey) ||
|
||||
ev.taggedPubkeys().some(isPubkeyMuted) ||
|
||||
(event.kind === Kind.Text && hasMutedKeyword(event))
|
||||
(event.kind === (Kind.Text as number) && hasMutedKeyword(event))
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
@@ -16,6 +16,7 @@ export type MessageChannelRequest<T> = {
|
||||
|
||||
export type MessageChannelResponse<T> =
|
||||
| { requestId: string; ok: true; response: T }
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
| { requestId: string; ok: false; error: any };
|
||||
|
||||
const [channels, setChannels]: Signal<Record<string, MessageChannel>> = createSignal({});
|
||||
|
||||
@@ -5,14 +5,14 @@ export type UseResizedImageProps = {
|
||||
imageUrl: Accessor<string | undefined>;
|
||||
width: number;
|
||||
height: number;
|
||||
encoderOption?: number;
|
||||
encoderOptions?: number;
|
||||
};
|
||||
|
||||
const useResizedImage = ({
|
||||
imageUrl,
|
||||
width,
|
||||
height,
|
||||
encoderOption,
|
||||
encoderOptions,
|
||||
}: UseResizedImageProps): Accessor<string | 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);
|
||||
|
||||
const dataUrl = canvas.toDataURL('image/jpeg');
|
||||
const dataUrl = canvas.toDataURL('image/jpeg', encoderOptions);
|
||||
|
||||
setResizedImage(dataUrl);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
// const commands = ['openPostForm'] as const;
|
||||
// type Commands = (typeof commands)[number];
|
||||
|
||||
import { onMount, onCleanup, type JSX } from 'solid-js';
|
||||
import { onMount, onCleanup } from 'solid-js';
|
||||
|
||||
import throttle from 'lodash/throttle';
|
||||
|
||||
|
||||
@@ -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 Reaction from '@/nostr/event/Reaction';
|
||||
|
||||
@@ -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;
|
||||
@@ -19,6 +19,7 @@ export type NonStandardProfile = {
|
||||
|
||||
export type Profile = StandardProfile & NonStandardProfile;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ProfileWithOtherProperties = Profile & Record<string, any>;
|
||||
|
||||
export const parseProfile = (content: string | null): Profile => {
|
||||
|
||||
@@ -30,7 +30,7 @@ const reactionToReactionTypes = (event: Reaction): ReactionTypes => {
|
||||
|
||||
export default class Reaction extends GenericEvent {
|
||||
constructor(rawEvent: NostrEvent) {
|
||||
if (rawEvent.kind !== Kind.Reaction) {
|
||||
if (rawEvent.kind !== (Kind.Reaction as number)) {
|
||||
throw new TypeError('kind should be 7');
|
||||
}
|
||||
super(rawEvent);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Event as NostrEvent, Kind } from 'nostr-tools';
|
||||
import { z } from 'zod';
|
||||
|
||||
import GenericEvent from '@/nostr/event/GenericEvent';
|
||||
import isValidId from '@/nostr/event/isValidId';
|
||||
@@ -9,7 +8,6 @@ import parseTextNote, {
|
||||
ParsedTextNote,
|
||||
TagReference,
|
||||
} from '@/nostr/parseTextNote';
|
||||
import ensureSchema from '@/utils/ensureSchema';
|
||||
|
||||
export type EventMarker = 'reply' | 'root' | 'mention';
|
||||
|
||||
@@ -69,7 +67,7 @@ export default class TextNote extends GenericEvent {
|
||||
#memoizedParsed: ParsedTextNote | undefined;
|
||||
|
||||
constructor(rawEvent: NostrEvent) {
|
||||
if (rawEvent.kind !== Kind.Text) {
|
||||
if (rawEvent.kind !== (Kind.Text as number)) {
|
||||
throw new TypeError('kind should be 1');
|
||||
}
|
||||
super(rawEvent);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import assert from 'assert';
|
||||
|
||||
import { type Event as NostrEvent } from 'nostr-tools';
|
||||
import { describe, it } from 'vitest';
|
||||
|
||||
import parseTextNote, { type ParsedTextNoteNode, TagReference } from '@/nostr/parseTextNote';
|
||||
import parseTextNote, { type ParsedTextNoteNode } from '@/nostr/parseTextNote';
|
||||
|
||||
describe('parseTextNote', () => {
|
||||
/*
|
||||
|
||||
@@ -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';
|
||||
|
||||
const { decode } = nip19;
|
||||
|
||||
@@ -162,33 +162,33 @@ const { addTask, removeTask } = useBatch<BatchedEventsTask>(() => ({
|
||||
count += 1;
|
||||
|
||||
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) ?? [];
|
||||
resolveTasks(registeredTasks, event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.kind === Kind.Reaction) {
|
||||
if (event.kind === (Kind.Reaction as number)) {
|
||||
// Use the last event id
|
||||
const id = genericEvent(event).lastTaggedEventId();
|
||||
if (id != null) {
|
||||
const registeredTasks = reactionsTasks.get(id) ?? [];
|
||||
resolveTasks(registeredTasks, event);
|
||||
}
|
||||
} else if ((event.kind as number) === 6) {
|
||||
} else if (event.kind === (Kind.Repost as number)) {
|
||||
// Use the last event id
|
||||
const id = genericEvent(event).lastTaggedEventId();
|
||||
if (id != null) {
|
||||
const registeredTasks = repostsTasks.get(id) ?? [];
|
||||
resolveTasks(registeredTasks, event);
|
||||
}
|
||||
} else if (event.kind === Kind.Zap) {
|
||||
} else if (event.kind === (Kind.Zap as number)) {
|
||||
const eTags = genericEvent(event).eTags();
|
||||
eTags.forEach(([, id]) => {
|
||||
const registeredTasks = repostsTasks.get(id) ?? [];
|
||||
resolveTasks(registeredTasks, event);
|
||||
});
|
||||
} else if (event.kind === Kind.Contacts) {
|
||||
} else if (event.kind === (Kind.Contacts as number)) {
|
||||
const registeredTasks = followingsTasks.get(event.pubkey) ?? [];
|
||||
resolveTasks(registeredTasks, event);
|
||||
} else if (isParameterizedReplaceableEvent(event)) {
|
||||
|
||||
@@ -6,7 +6,9 @@ type UseDecryptProps = {
|
||||
encrypted: string;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
const [memo, setMemo] = createRoot(() => createSignal<Record<string, string>>({}));
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
const [decrypting, setDecrypting] = createRoot(() => createSignal<Record<string, boolean>>({}));
|
||||
|
||||
const useDecrypt = (propsProvider: () => UseDecryptProps | null) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 { registerTask, BatchedEventsTask } from '@/nostr/useBatchedEvents';
|
||||
|
||||
@@ -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 { Event as NostrEvent } from 'nostr-tools';
|
||||
|
||||
@@ -16,7 +16,7 @@ const useVerification = (propsProvider: () => UseVerificationProps | null): UseV
|
||||
const props = createMemo(propsProvider);
|
||||
const query = createQuery(
|
||||
() => ['useVerification', props()] as const,
|
||||
({ queryKey, signal }) => {
|
||||
({ queryKey }) => {
|
||||
const [, currentProps] = queryKey;
|
||||
if (currentProps == null) return Promise.resolve(null);
|
||||
const { nip05: nip05string } = currentProps;
|
||||
|
||||
@@ -6,7 +6,6 @@ import Columns from '@/components/column/Columns';
|
||||
import GlobalModal from '@/components/modal/GlobalModal';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import useConfig from '@/core/useConfig';
|
||||
import useModalState from '@/hooks/useModalState';
|
||||
import usePersistStatus from '@/hooks/usePersistStatus';
|
||||
import { useMountShortcutKeys } from '@/hooks/useShortcutKeys';
|
||||
import usePool from '@/nostr/usePool';
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type TupleNonNull<T extends readonly any[]> = {
|
||||
[P in keyof T]: NonNullable<T[P]>;
|
||||
};
|
||||
|
||||
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)) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
||||
|
||||
const ensureSchema =
|
||||
<T>(schema: z.Schema<T>) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(value: any): value is T =>
|
||||
schema.safeParse(value).success;
|
||||
|
||||
|
||||
@@ -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> => {
|
||||
const form = new FormData();
|
||||
form.set('file', blob);
|
||||
@@ -69,27 +58,6 @@ export const uploadNostrBuild = async (blob: Blob): Promise<UploadResult> => {
|
||||
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 = {
|
||||
nostrBuild: {
|
||||
name: 'nostr.build',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stripMargin = (strings: TemplateStringsArray, ...values: any[]) => {
|
||||
const s = String.raw(strings, values);
|
||||
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": true,
|
||||
@@ -20,19 +16,10 @@
|
||||
"isolatedModules": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@/*": ["src/*"]
|
||||
},
|
||||
"incremental": true
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.js",
|
||||
"**/*.jsx",
|
||||
"./.eslintrc.js"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"include": ["vite.config.ts", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "./.eslintrc.js"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user