mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-03-14 04:08:32 +03:00
New experiment: multi-column mode
This commit is contained in:
parent
45a1fc057e
commit
522d55ebb8
9 changed files with 176 additions and 53 deletions
80
src/app.css
80
src/app.css
|
@ -1197,12 +1197,12 @@ meter.donut:is(.danger, .explode):after {
|
|||
width: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
:is(#home-page, #welcome) ~ .deck-container {
|
||||
:is(#home-page, #welcome, #columns) ~ .deck-container {
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
}
|
||||
:is(#home-page, #welcome):has(~ .deck-container) {
|
||||
:is(#home-page, #welcome, #columns):has(~ .deck-container) {
|
||||
display: block;
|
||||
position: absolute;
|
||||
user-select: none;
|
||||
|
@ -1338,28 +1338,82 @@ ul.link-list li a .icon {
|
|||
display: flex;
|
||||
width: 100vw;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
overflow-x: scroll;
|
||||
scroll-snap-type: x mandatory;
|
||||
scroll-behavior: smooth;
|
||||
scrollbar-width: none;
|
||||
/* scrollbar-width: none; */
|
||||
overscroll-behavior: contain;
|
||||
overscroll-behavior-x: contain;
|
||||
}
|
||||
#columns::-webkit-scrollbar {
|
||||
/* #columns::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
} */
|
||||
#columns > * {
|
||||
overscroll-behavior: auto;
|
||||
scroll-snap-align: left;
|
||||
scroll-snap-stop: always;
|
||||
position: static !important;
|
||||
opacity: 1 !important;
|
||||
content-visibility: auto !important;
|
||||
pointer-events: auto !important;
|
||||
user-select: auto !important;
|
||||
/* background-color: var(--bg-color); */
|
||||
flex-basis: min(100vw, 480px);
|
||||
overscroll-behavior: auto;
|
||||
flex-basis: min(100vw, 360px);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
#columns .header-grid input {
|
||||
pointer-events: none;
|
||||
}
|
||||
#columns
|
||||
.header-grid
|
||||
.header-side:first-of-type
|
||||
:is(button, .button)
|
||||
~ :is(button, .button),
|
||||
#columns .deck-container:not(:first-of-type) .header-grid .header-side > * {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 40em) {
|
||||
#columns {
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
background-color: var(--bg-blur-color);
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
justify-content: stretch;
|
||||
align-items: stretch;
|
||||
}
|
||||
#columns > * {
|
||||
padding: 0 16px;
|
||||
border: var(--hairline-width) solid var(--outline-color);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 16px var(--drop-shadow-color);
|
||||
height: unset;
|
||||
background-image: linear-gradient(
|
||||
160deg,
|
||||
transparent 20%,
|
||||
var(--bg-color),
|
||||
transparent 75%
|
||||
);
|
||||
}
|
||||
#columns > *:focus-visible,
|
||||
#columns > *:has(:focus-visible) {
|
||||
box-shadow: 0 4px 16px var(--drop-shadow-color),
|
||||
0 4px 16px var(--drop-shadow-color);
|
||||
border-color: var(--outline-hover-color);
|
||||
}
|
||||
#columns .timeline:not(.flat) > li:has(.status-link.is-active),
|
||||
#columns
|
||||
.timeline:not(.flat)
|
||||
> li:not(:has(.status-carousel)):has(+ li .status-link.is-active),
|
||||
#columns
|
||||
.timeline:not(.flat)
|
||||
> li:not(:has(.status-carousel)):has(.status-link.is-active)
|
||||
+ li {
|
||||
transform: none;
|
||||
}
|
||||
#columns .timeline-deck > header {
|
||||
margin: 0;
|
||||
}
|
||||
#columns li:has(.status-carousel) {
|
||||
width: auto;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* OTHERS */
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import Bookmarks from './pages/bookmarks';
|
|||
import Favourites from './pages/favourites';
|
||||
import FollowedHashtags from './pages/followed-hashtags';
|
||||
import Following from './pages/following';
|
||||
import Hashtags from './pages/hashtags';
|
||||
import Hashtag from './pages/hashtag';
|
||||
import Home from './pages/home';
|
||||
import HomeV1 from './pages/home-v1';
|
||||
import List from './pages/list';
|
||||
|
@ -269,7 +269,7 @@ function App() {
|
|||
</Route>
|
||||
)}
|
||||
{isLoggedIn && <Route path="/ft" element={<FollowedHashtags />} />}
|
||||
<Route path="/:instance?/t/:hashtag" element={<Hashtags />} />
|
||||
<Route path="/:instance?/t/:hashtag" element={<Hashtag />} />
|
||||
<Route path="/:instance?/a/:id" element={<AccountStatuses />} />
|
||||
<Route path="/:instance?/p">
|
||||
<Route index element={<Public />} />
|
||||
|
@ -298,7 +298,7 @@ function App() {
|
|||
</Link>
|
||||
</li>
|
||||
</nav>
|
||||
<Shortcuts />
|
||||
{!snapStates.settings.shortcutsColumnsMode && <Shortcuts />}
|
||||
{!!snapStates.showCompose && (
|
||||
<Modal>
|
||||
<Compose
|
||||
|
|
|
@ -16,15 +16,15 @@ const TYPES = [
|
|||
'notifications',
|
||||
'list',
|
||||
'public',
|
||||
'search',
|
||||
// NOTE: Hide for now, can't think of a good way to handle this
|
||||
// 'account-statuses',
|
||||
// NOTE: Hide for now
|
||||
// 'search', // Search on Mastodon ain't great
|
||||
// 'account-statuses', // Need @acct search first
|
||||
'bookmarks',
|
||||
'favourites',
|
||||
'hashtag',
|
||||
];
|
||||
const TYPE_TEXT = {
|
||||
following: 'Home',
|
||||
following: 'Home / Following',
|
||||
notifications: 'Notifications',
|
||||
list: 'List',
|
||||
public: 'Public',
|
||||
|
@ -80,7 +80,7 @@ const TYPE_PARAMS = {
|
|||
};
|
||||
export const SHORTCUTS_META = {
|
||||
following: {
|
||||
title: 'Home',
|
||||
title: 'Home / Following',
|
||||
path: (_, index) => (index === 0 ? '/' : '/l/f'),
|
||||
icon: 'home',
|
||||
},
|
||||
|
@ -188,7 +188,24 @@ function ShortcutsSettings() {
|
|||
Specify a list of shortcuts that'll appear in the floating Shortcuts
|
||||
button.
|
||||
</p>
|
||||
{snapStates.shortcuts.length > 0 ? (
|
||||
<p>
|
||||
<details>
|
||||
<summary class="insignificant">
|
||||
Experimental Multi-column mode
|
||||
</summary>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={snapStates.settings.shortcutsColumnsMode}
|
||||
onChange={(e) => {
|
||||
states.settings.shortcutsColumnsMode = e.target.checked;
|
||||
}}
|
||||
/>{' '}
|
||||
Show shortcuts in multiple columns instead of the floating button.
|
||||
</label>
|
||||
</details>
|
||||
</p>
|
||||
{shortcuts.length > 0 ? (
|
||||
<ol class="shortcuts-list">
|
||||
{shortcuts.map((shortcut, i) => {
|
||||
const key = i + Object.values(shortcut);
|
||||
|
|
|
@ -7,8 +7,9 @@ import useTitle from '../utils/useTitle';
|
|||
|
||||
const LIMIT = 20;
|
||||
|
||||
function Hashtags() {
|
||||
function Hashtags(props) {
|
||||
let { hashtag, ...params } = useParams();
|
||||
if (props.hashtag) hashtag = props.hashtag;
|
||||
const { masto, instance } = api({ instance: params.instance });
|
||||
const title = instance ? `#${hashtag} on ${instance}` : `#${hashtag}`;
|
||||
useTitle(title, `/:instance?/t/:hashtag`);
|
|
@ -1,4 +1,5 @@
|
|||
import { useEffect } from 'preact/hooks';
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import Icon from '../components/icon';
|
||||
|
@ -25,27 +26,69 @@ function Home() {
|
|||
})();
|
||||
}, []);
|
||||
|
||||
const { shortcuts } = snapStates;
|
||||
const { shortcutsColumnsMode } = snapStates.settings || {};
|
||||
const [shortcutsComponents, setShortcutsComponents] = useState([]);
|
||||
useEffect(() => {
|
||||
if (shortcutsColumnsMode) {
|
||||
const componentsPromises = shortcuts.map((shortcut) => {
|
||||
const { type, ...params } = shortcut;
|
||||
// Uppercase type
|
||||
return import(`./${type}`).then((module) => {
|
||||
const { default: Component } = module;
|
||||
return <Component {...params} />;
|
||||
});
|
||||
});
|
||||
Promise.all(componentsPromises)
|
||||
.then((components) => {
|
||||
setShortcutsComponents(components);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}, [shortcutsColumnsMode, shortcuts]);
|
||||
|
||||
useHotkeys(
|
||||
['1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
||||
(e, handler) => {
|
||||
try {
|
||||
const index = parseInt(handler.keys[0], 10) - 1;
|
||||
document.querySelectorAll('#columns > *')[index].focus();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
{
|
||||
enabled: shortcutsColumnsMode,
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Following
|
||||
title="Home"
|
||||
path="/"
|
||||
id="home"
|
||||
headerStart={false}
|
||||
headerEnd={
|
||||
<Link
|
||||
to="/notifications"
|
||||
class={`button plain ${
|
||||
snapStates.notificationsShowNew ? 'has-badge' : ''
|
||||
}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Icon icon="notification" size="l" alt="Notifications" />
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
{shortcutsColumnsMode ? (
|
||||
<div id="columns">{shortcutsComponents}</div>
|
||||
) : (
|
||||
<Following
|
||||
title="Home"
|
||||
path="/"
|
||||
id="home"
|
||||
headerStart={false}
|
||||
headerEnd={
|
||||
<Link
|
||||
to="/notifications"
|
||||
class={`button plain ${
|
||||
snapStates.notificationsShowNew ? 'has-badge' : ''
|
||||
}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Icon icon="notification" size="l" alt="Notifications" />
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
// hidden={scrollDirection === 'end' && !nearReachStart}
|
||||
type="button"
|
||||
|
|
|
@ -9,9 +9,9 @@ import useTitle from '../utils/useTitle';
|
|||
|
||||
const LIMIT = 20;
|
||||
|
||||
function List() {
|
||||
function List(props) {
|
||||
const { masto } = api();
|
||||
const { id } = useParams();
|
||||
const id = props?.id || useParams()?.id;
|
||||
const latestItem = useRef();
|
||||
|
||||
const listIterator = useRef();
|
||||
|
|
|
@ -9,10 +9,12 @@ import useTitle from '../utils/useTitle';
|
|||
|
||||
const LIMIT = 20;
|
||||
|
||||
function Public({ local }) {
|
||||
function Public({ local, ...props }) {
|
||||
const isLocal = !!local;
|
||||
const params = useParams();
|
||||
const { masto, instance } = api({ instance: params.instance });
|
||||
const { masto, instance } = api({
|
||||
instance: props?.instance || params.instance,
|
||||
});
|
||||
const title = `${isLocal ? 'Local' : 'Federated'} timeline (${instance})`;
|
||||
useTitle(title, isLocal ? `/:instance?/p/l` : `/:instance?/p`);
|
||||
const navigate = useNavigate();
|
||||
|
|
|
@ -12,7 +12,7 @@ import Status from '../components/status';
|
|||
import { api } from '../utils/api';
|
||||
import useTitle from '../utils/useTitle';
|
||||
|
||||
function Search() {
|
||||
function Search(props) {
|
||||
const params = useParams();
|
||||
const { masto, instance, authenticated } = api({
|
||||
instance: params.instance,
|
||||
|
@ -20,7 +20,7 @@ function Search() {
|
|||
const [uiState, setUiState] = useState('default');
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const searchFieldRef = useRef();
|
||||
const q = searchParams.get('q');
|
||||
const q = props?.query || searchParams.get('q');
|
||||
useTitle(q ? `Search: ${q}` : 'Search', `/search`);
|
||||
|
||||
const [statusResults, setStatusResults] = useState([]);
|
||||
|
|
|
@ -35,6 +35,8 @@ const states = proxy({
|
|||
shortcuts: store.account.get('shortcuts') ?? [],
|
||||
// Settings
|
||||
settings: {
|
||||
shortcutsColumnsMode:
|
||||
store.account.get('settings-shortcutsColumnsMode') ?? false,
|
||||
boostsCarousel: store.account.get('settings-boostsCarousel') ?? true,
|
||||
},
|
||||
});
|
||||
|
@ -45,11 +47,15 @@ subscribeKey(states, 'notificationsLast', (v) => {
|
|||
console.log('CHANGE', v);
|
||||
store.account.set('notificationsLast', states.notificationsLast);
|
||||
});
|
||||
subscribeKey(states, 'settings-boostsCarousel', (v) => {
|
||||
store.account.set('settings-boostsCarousel', !!v);
|
||||
});
|
||||
subscribe(states, (v) => {
|
||||
const [action, path, value] = v[0];
|
||||
console.debug('STATES change', v);
|
||||
const [action, path, value, prevValue] = v[0];
|
||||
if (path.join('.') === 'settings.boostsCarousel') {
|
||||
store.account.set('settings-boostsCarousel', !!value);
|
||||
}
|
||||
if (path.join('.') === 'settings.shortcutsColumnsMode') {
|
||||
store.account.set('settings-shortcutsColumnsMode', !!value);
|
||||
}
|
||||
if (path?.[0] === 'shortcuts') {
|
||||
store.account.set('shortcuts', states.shortcuts);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue