diff --git a/src/js/views/KeyConverter.tsx b/src/js/views/KeyConverter.tsx index c4ac236a..c6a693dc 100644 --- a/src/js/views/KeyConverter.tsx +++ b/src/js/views/KeyConverter.tsx @@ -1,11 +1,12 @@ -import { useState } from 'react'; +import React, { useState } from 'react'; + +import { RouteProps } from '@/views/types.ts'; import Header from '../components/Header'; import Key from '../nostr/Key'; import { translate as t } from '../translations/Translation.mjs'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export default function KeyConverter(_props) { +const KeyConverter: React.FC = () => { const [key, setKey] = useState(''); const hex = Key.toNostrHexAddress(key); @@ -55,4 +56,6 @@ export default function KeyConverter(_props) { ); -} +}; + +export default KeyConverter; diff --git a/src/js/views/Note.tsx b/src/js/views/Note.tsx index 6111a6c0..b0ec4eed 100644 --- a/src/js/views/Note.tsx +++ b/src/js/views/Note.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'preact/hooks'; import { route } from 'preact-router'; import CreateNoteForm from '../components/create/CreateNoteForm'; @@ -5,55 +6,39 @@ import EventComponent from '../components/events/EventComponent'; import Key from '../nostr/Key'; import { translate as t } from '../translations/Translation.mjs'; -import View from './View'; - -class Note extends View { - constructor() { - super(); - this.class = 'public-messages-view'; - } - - componentDidMount() { - const nostrBech32Id = Key.toNostrBech32Address(this.props.id, 'note'); - if (nostrBech32Id && this.props.id !== nostrBech32Id) { +const Note = (props) => { + useEffect(() => { + const nostrBech32Id = Key.toNostrBech32Address(props.id, 'note'); + if (nostrBech32Id && props.id !== nostrBech32Id) { route(`/${nostrBech32Id}`, true); return; } - this.restoreScrollPosition(); - } + }, [props.id]); - componentDidUpdate(prevProps) { - if (prevProps.id !== this.props.id) { - this.restoreScrollPosition(); - } - } - - renderView() { - let content; - if (this.props.id === 'new') { - content = ( -
- route('/')} - /> -
- ); - } else { - content = ( - + route('/')} /> - ); - } - return
{content}
; + + ); + } else { + content = ( + + ); } -} + return
{content}
; +}; export default Note; diff --git a/src/js/views/feeds/Global.tsx b/src/js/views/feeds/Global.tsx index f25fbf4b..28fa7b59 100644 --- a/src/js/views/feeds/Global.tsx +++ b/src/js/views/feeds/Global.tsx @@ -1,50 +1,41 @@ +import React, { useMemo } from 'react'; + import CreateNoteForm from '@/components/create/CreateNoteForm'; import FeedComponent from '@/components/feed/Feed'; import OnboardingNotification from '@/components/onboarding/OnboardingNotification'; import { getEventReplyingTo } from '@/nostr/utils'; import { translate as t } from '@/translations/Translation.mjs'; +import { RouteProps } from '@/views/types.ts'; -import View from '../View'; +const Global: React.FC = () => { + const filterOptions = useMemo( + () => [ + { + name: t('posts'), + filter: { kinds: [1, 6], limit: 10 }, + filterFn: (event) => !getEventReplyingTo(event), + eventProps: { showRepliedMsg: true }, + }, + { + name: t('posts_and_replies'), + filter: { kinds: [1, 6], limit: 5 }, + eventProps: { showRepliedMsg: true, fullWidth: false }, + }, + ], + [], + ); -class Global extends View { - constructor() { - super(); - this.state = { sortedMessages: [] }; - this.id = 'message-view'; - this.class = 'public-messages-view'; - } - - componentDidMount() { - this.restoreScrollPosition(); - } - - renderView() { - return ( -
-
- -
- -
- !getEventReplyingTo(event), - eventProps: { showRepliedMsg: true }, - }, - { - name: t('posts_and_replies'), - filter: { kinds: [1, 6], limit: 5 }, - eventProps: { showRepliedMsg: true, fullWidth: false }, - }, - ]} - /> + return ( +
+
+ +
+
+
- ); - } -} +
+ ); +}; export default Global; diff --git a/src/js/views/feeds/Home.tsx b/src/js/views/feeds/Home.tsx index cf982055..dea3ea82 100644 --- a/src/js/views/feeds/Home.tsx +++ b/src/js/views/feeds/Home.tsx @@ -1,77 +1,71 @@ +import { useEffect, useMemo, useState } from 'react'; + import CreateNoteForm from '@/components/create/CreateNoteForm'; import FeedComponent from '@/components/feed/Feed'; import Show from '@/components/helpers/Show'; import OnboardingNotification from '@/components/onboarding/OnboardingNotification'; import Key from '@/nostr/Key'; -import { Unsubscribe } from '@/nostr/PubSub'; import { getEventReplyingTo } from '@/nostr/utils'; import { translate as t } from '@/translations/Translation.mjs'; import { ID, STR } from '@/utils/UniqueIds'; +import { RouteProps } from '@/views/types.ts'; import SocialNetwork from '../../nostr/SocialNetwork'; -import View from '../View'; -class Home extends View { - unsub?: Unsubscribe; - - constructor() { - super(); - const followedUsers: string[] = Array.from( +const Home: React.FC = () => { + const [followedUsers, setFollowedUsers] = useState(() => { + const initialFollowedUsers = Array.from( SocialNetwork.followedByUser.get(ID(Key.getPubKey())) || [], - ).map((n) => STR(n)); - this.state = { - followedUsers, - }; - this.id = 'message-view'; - this.class = 'public-messages-view'; - } + ); + return initialFollowedUsers.map((n) => STR(n)); + }); - componentDidMount() { - this.restoreScrollPosition(); - this.unsub = SocialNetwork.getFollowedByUser( + useEffect(() => { + const unsub = SocialNetwork.getFollowedByUser( Key.getPubKey(), - (followedUsers) => { - this.setState({ followedUsers: Array.from(followedUsers) }); + (newFollowedUsers) => { + setFollowedUsers(Array.from(newFollowedUsers)); }, true, ); - } - componentWillUnmount() { - super.componentWillUnmount(); - this.unsub?.(); - } + return () => { + unsub?.(); + }; + }, []); - renderView() { - return ( -
-
- -
- -
- - !getEventReplyingTo(event), - eventProps: { showRepliedMsg: true }, - }, - { - name: t('posts_and_replies'), - filter: { kinds: [1, 6], authors: this.state.followedUsers, limit: 5 }, - eventProps: { showRepliedMsg: true, fullWidth: false }, - }, - ]} - /> - + const filterOptions = useMemo( + () => [ + { + name: t('posts'), + filter: { kinds: [1, 6], authors: followedUsers, limit: 10 }, + filterFn: (event) => !getEventReplyingTo(event), + eventProps: { showRepliedMsg: true }, + }, + { + name: t('posts_and_replies'), + filter: { kinds: [1, 6], authors: followedUsers, limit: 5 }, + eventProps: { showRepliedMsg: true, fullWidth: false }, + }, + ], + [followedUsers], + ); + + console.log('followedUsers.length', followedUsers.length); // TODO this keeps changing, fix + + return ( +
+
+ +
+
+ + +
- ); - } -} +
+ ); +}; export default Home; diff --git a/src/js/views/feeds/Notifications.tsx b/src/js/views/feeds/Notifications.tsx index 9738a61b..231ff8cb 100644 --- a/src/js/views/feeds/Notifications.tsx +++ b/src/js/views/feeds/Notifications.tsx @@ -1,64 +1,73 @@ +import React, { useEffect, useMemo } from 'react'; import debounce from 'lodash/debounce'; import { Event } from 'nostr-tools'; import EventDB from '@/nostr/EventDB'; import Events from '@/nostr/Events'; import Key from '@/nostr/Key'; +import { RouteProps } from '@/views/types.ts'; import Feed from '../../components/feed/Feed'; import localState from '../../LocalState'; import Session from '../../nostr/Session'; import { translate as t } from '../../translations/Translation.mjs'; -import View from '../View'; -export default class Notifications extends View { - class = 'public-messages-view'; +const Notifications: React.FC = () => { + const filterOptions = useMemo( + () => [ + { + name: 'notifications', + filter: { kinds: [1, 6, 7, 9735], '#p': [Key.getPubKey()], limit: 20 }, + eventProps: { fullWidth: false }, + }, + ], + [], + ); - updateNotificationsLastOpened = debounce(() => { - const node = localState.get('settings').get('notifications').get('saveLastOpened'); - node.once((saveLastOpened) => { - if (saveLastOpened !== false) { - const time = Math.floor(Date.now() / 1000); - const success = Session.public?.set('notifications/lastOpened', time); - if (!success) { - console.log('user rejected'); - // stop pestering if user rejects signature request - node.put(false); - } - localState.get('unseenNotificationCount').put(0); - } - }); - }, 1000); + const fetchEvents = () => { + const events = Events.notifications.eventIds + .map((id) => EventDB.get(id)) + .filter((event): event is Event => Boolean(event)) as Event[]; - componentDidMount() { - this.restoreScrollPosition(); - this.updateNotificationsLastOpened(); - } + return { + events, + loadMore: () => {}, + }; + }; - renderView() { - return ( - { - const events = Events.notifications.eventIds - .map((id) => EventDB.get(id)) - .filter((event): event is Event => Boolean(event)) as Event[]; + const updateNotificationsLastOpened = useMemo( + () => + debounce(() => { + const node = localState.get('settings').get('notifications').get('saveLastOpened'); + node.once((saveLastOpened) => { + if (saveLastOpened !== false) { + const time = Math.floor(Date.now() / 1000); + const success = Session.public?.set('notifications/lastOpened', time); + if (!success) { + console.log('user rejected'); + // stop pestering if user rejects signature request + node.put(false); + } + localState.get('unseenNotificationCount').put(0); + } + }); + }, 1000), + [], + ); - return { - events, - loadMore: () => {}, - }; - }} - /> - ); - } -} + useEffect(() => { + updateNotificationsLastOpened(); + }, [updateNotificationsLastOpened]); + + return ( + + ); +}; + +export default Notifications; diff --git a/src/js/views/profile/Follows.tsx b/src/js/views/profile/Follows.tsx index 5fa7ab73..83e11daa 100644 --- a/src/js/views/profile/Follows.tsx +++ b/src/js/views/profile/Follows.tsx @@ -8,7 +8,6 @@ import Follow from '../../components/buttons/Follow.tsx'; import Show from '../../components/helpers/Show.tsx'; import Avatar from '../../components/user/Avatar.tsx'; import Name from '../../components/user/Name.tsx'; -import localState from '../../LocalState.ts'; import Key from '../../nostr/Key.ts'; import SocialNetwork from '../../nostr/SocialNetwork.ts'; import { translate as t } from '../../translations/Translation.mjs'; @@ -45,7 +44,7 @@ class Follows extends View { this.myPub = null; this.follows = new Set(); this.id = 'follows-view'; - this.state = { follows: [], contacts: {} }; + this.state = { follows: [] }; } sortByName(aK, bK) { @@ -115,7 +114,6 @@ class Follows extends View { if (this.props.id) { this.myPub = Key.toNostrBech32Address(Key.getPubKey(), 'npub'); this.props.followers ? this.getFollowers() : this.getFollows(); - localState.get('contacts').on(this.inject()); } }