diff --git a/src/app.jsx b/src/app.jsx index f43c470e..70b8c0a8 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -35,6 +35,7 @@ import Following from './pages/following'; import Hashtags from './pages/hashtags'; import Home from './pages/home'; import HomeV1 from './pages/home-v1'; +import List from './pages/list'; import Lists from './pages/lists'; import Login from './pages/login'; import Notifications from './pages/notifications'; @@ -209,7 +210,12 @@ function App() { {isLoggedIn && } />} {isLoggedIn && } />} {isLoggedIn && } />} - {isLoggedIn && } />} + {isLoggedIn && ( + + } /> + } /> + + )} } /> } /> diff --git a/src/components/icon.jsx b/src/components/icon.jsx index 896c1dba..4f610de0 100644 --- a/src/components/icon.jsx +++ b/src/components/icon.jsx @@ -49,6 +49,8 @@ const ICONS = { group: 'mingcute:group-line', bot: 'mingcute:android-2-line', menu: 'mingcute:rows-4-line', + list: 'mingcute:list-check-line', + search: 'mingcute:search-2-line', }; const modules = import.meta.glob('/node_modules/@iconify-icons/mingcute/*.js'); diff --git a/src/components/menu.jsx b/src/components/menu.jsx index 5b7508ff..f6e788cc 100644 --- a/src/components/menu.jsx +++ b/src/components/menu.jsx @@ -1,13 +1,18 @@ import { FocusableItem, Menu, MenuDivider, MenuItem } from '@szhsin/react-menu'; +import { api } from '../utils/api'; import states from '../utils/states'; import Icon from './icon'; import Link from './link'; function NavMenu(props) { + const { instance } = api(); return ( @@ -18,12 +23,29 @@ function NavMenu(props) { Home + + Notifications + + Bookmarks Favourites + + Lists + + + {/* + Search + */} + + Local + + + Federated + { diff --git a/src/pages/list.jsx b/src/pages/list.jsx new file mode 100644 index 00000000..3b7e2df4 --- /dev/null +++ b/src/pages/list.jsx @@ -0,0 +1,55 @@ +import { useEffect, useRef, useState } from 'preact/hooks'; +import { useParams } from 'react-router-dom'; + +import Icon from '../components/icon'; +import Link from '../components/link'; +import Timeline from '../components/timeline'; +import { api } from '../utils/api'; +import useTitle from '../utils/useTitle'; + +const LIMIT = 20; + +function List() { + const { masto } = api(); + const { id } = useParams(); + const listIterator = useRef(); + async function fetchList(firstLoad) { + if (firstLoad || !listIterator.current) { + listIterator.current = masto.v1.timelines.listList(id, { + limit: LIMIT, + }); + } + return await listIterator.current.next(); + } + + const [title, setTitle] = useState(`List`); + useTitle(title, `/l/:id`); + useEffect(() => { + (async () => { + try { + const list = await masto.v1.lists.fetch(id); + setTitle(list.title); + } catch (e) { + console.error(e); + } + })(); + }, [id]); + + return ( + + + + } + /> + ); +} + +export default List; diff --git a/src/pages/lists.css b/src/pages/lists.css new file mode 100644 index 00000000..53795ecb --- /dev/null +++ b/src/pages/lists.css @@ -0,0 +1,25 @@ +#lists-page ul { + list-style: none; + padding: 16px; + margin: 0; +} +#lists-page ul li { + padding: 0; + margin: 0; +} +#lists-page ul li a { + display: block; + background-color: var(--bg-faded-color); + border-radius: 8px; + line-height: 1.25; + padding: 12px; + text-decoration: none; + line-height: 1.4; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 500; +} +#lists-page ul li a * { + vertical-align: middle; +} diff --git a/src/pages/lists.jsx b/src/pages/lists.jsx index 3bfcb2b3..0736e65d 100644 --- a/src/pages/lists.jsx +++ b/src/pages/lists.jsx @@ -1,47 +1,71 @@ -import { useEffect, useRef, useState } from 'preact/hooks'; -import { useParams } from 'react-router-dom'; +import './lists.css'; -import Timeline from '../components/timeline'; +import { useEffect, useState } from 'preact/hooks'; + +import Icon from '../components/icon'; +import Link from '../components/link'; +import Loader from '../components/loader'; +import Menu from '../components/menu'; import { api } from '../utils/api'; -import useTitle from '../utils/useTitle'; - -const LIMIT = 20; function Lists() { const { masto } = api(); - const { id } = useParams(); - const listsIterator = useRef(); - async function fetchLists(firstLoad) { - if (firstLoad || !listsIterator.current) { - listsIterator.current = masto.v1.timelines.listList(id, { - limit: LIMIT, - }); - } - return await listsIterator.current.next(); - } + const [uiState, setUiState] = useState('default'); - const [title, setTitle] = useState(`List ${id}`); - useTitle(title, `/l/:id`); + const [lists, setLists] = useState([]); useEffect(() => { + setUiState('loading'); (async () => { try { - const list = await masto.v1.lists.fetch(id); - setTitle(list.title); + const lists = await masto.v1.lists.list(); + console.log(lists); + setLists(lists); + setUiState('default'); } catch (e) { console.error(e); + setUiState('error'); } })(); - }, [id]); + }, []); return ( - +
+
+
+
+
+ + + + +
+

Lists

+
+
+
+
+ {lists.length > 0 ? ( +
    + {lists.map((list) => ( +
  • + + {list.title} + +
  • + ))} +
+ ) : uiState === 'loading' ? ( +

+ +

+ ) : uiState === 'error' ? ( +

Unable to load lists.

+ ) : ( +

No lists yet.

+ )} +
+
+
); } diff --git a/src/pages/search.jsx b/src/pages/search.jsx index 05467cf0..64f88166 100644 --- a/src/pages/search.jsx +++ b/src/pages/search.jsx @@ -12,15 +12,18 @@ import { api } from '../utils/api'; function Search() { const { masto, instance, authenticated } = api(); + const [uiState, setUiState] = useState('default'); const [searchParams, setSearchParams] = useSearchParams(); const searchFieldRef = useRef(); const q = searchParams.get('q'); const [statusResults, setStatusResults] = useState([]); const [accountResults, setAccountResults] = useState([]); + const [hashtagResults, setHashtagResults] = useState([]); useEffect(() => { if (q) { searchFieldRef.current.value = q; + setUiState('loading'); (async () => { const results = await masto.v2.search({ q, @@ -30,12 +33,12 @@ function Search() { console.log(results); setStatusResults(results.statuses); setAccountResults(results.accounts); + setHashtagResults(results.hashtags); + setUiState('default'); })(); } }, [q]); - console.log({ accountResults }); - return (
@@ -65,35 +68,69 @@ function Search() {
-

Accounts

- {accountResults.length > 0 && ( -
    - {accountResults.map((account) => ( -
  • - - -
  • - ))} -
- )} -

Posts

- {statusResults.length > 0 && ( -
    - {statusResults.map((status) => ( -
  • - - - -
  • - ))} -
+ {!!q && uiState !== 'loading' ? ( + <> +

Accounts

+ {accountResults.length > 0 ? ( +
    + {accountResults.map((account) => ( +
  • + + +
  • + ))} +
+ ) : ( +

No accounts found.

+ )} +

Hashtags

+ {hashtagResults.length > 0 ? ( +
    + {hashtagResults.map((hashtag) => ( +
  • + + #{hashtag.name} + +
  • + ))} +
+ ) : ( +

No hashtags found.

+ )} +

Posts

+ {statusResults.length > 0 ? ( +
    + {statusResults.map((status) => ( +
  • + + + +
  • + ))} +
+ ) : ( +

No posts found.

+ )} + + ) : ( +

Enter your search term above to get started.

)}