mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-16 07:11:15 +03:00
Experiment: allow Search in Shortcuts
This commit is contained in:
parent
6bcee318e4
commit
da58336285
5 changed files with 125 additions and 23 deletions
|
@ -9,6 +9,7 @@ import List from '../pages/list';
|
|||
import Mentions from '../pages/mentions';
|
||||
import Notifications from '../pages/notifications';
|
||||
import Public from '../pages/public';
|
||||
import Search from '../pages/search';
|
||||
import Trending from '../pages/trending';
|
||||
import states from '../utils/states';
|
||||
import useTitle from '../utils/useTitle';
|
||||
|
@ -33,8 +34,11 @@ function Columns() {
|
|||
hashtag: Hashtag,
|
||||
mentions: Mentions,
|
||||
trending: Trending,
|
||||
search: Search,
|
||||
}[type];
|
||||
if (!Component) return null;
|
||||
// Don't show Search column with no query, for now
|
||||
if (type === 'search' && !params.query) return null;
|
||||
return (
|
||||
<Component key={type + JSON.stringify(params)} {...params} columnMode />
|
||||
);
|
||||
|
|
|
@ -123,6 +123,11 @@
|
|||
min-width: 0;
|
||||
max-width: 320px;
|
||||
}
|
||||
#shortcut-settings-form .form-note {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
#shortcut-settings-form form footer {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
|
|
@ -32,12 +32,12 @@ const TYPES = [
|
|||
'list',
|
||||
'public',
|
||||
'trending',
|
||||
// NOTE: Hide for now
|
||||
// 'search', // Search on Mastodon ain't great
|
||||
// 'account-statuses', // Need @acct search first
|
||||
'search',
|
||||
'hashtag',
|
||||
'bookmarks',
|
||||
'favourites',
|
||||
// NOTE: Hide for now
|
||||
// 'account-statuses', // Need @acct search first
|
||||
];
|
||||
const TYPE_TEXT = {
|
||||
following: 'Home / Following',
|
||||
|
@ -87,6 +87,8 @@ const TYPE_PARAMS = {
|
|||
text: 'Search term',
|
||||
name: 'query',
|
||||
type: 'text',
|
||||
placeholder: 'Optional, unless for multi-column mode',
|
||||
notRequired: true,
|
||||
},
|
||||
],
|
||||
'account-statuses': [
|
||||
|
@ -168,9 +170,11 @@ export const SHORTCUTS_META = {
|
|||
},
|
||||
search: {
|
||||
id: 'search',
|
||||
title: ({ query }) => query,
|
||||
path: ({ query }) => `/search?q=${query}`,
|
||||
title: ({ query }) => (query ? `"${query}"` : 'Search'),
|
||||
path: ({ query }) =>
|
||||
query ? `/search?q=${query}&type=statuses` : '/search',
|
||||
icon: 'search',
|
||||
excludeViewMode: ({ query }) => (!query ? ['multi-column'] : []),
|
||||
},
|
||||
'account-statuses': {
|
||||
id: 'account-statuses',
|
||||
|
@ -279,7 +283,8 @@ function ShortcutsSettings({ onClose }) {
|
|||
const key = Object.values(shortcut).join('-');
|
||||
const { type } = shortcut;
|
||||
if (!SHORTCUTS_META[type]) return null;
|
||||
let { icon, title, subtitle } = SHORTCUTS_META[type];
|
||||
let { icon, title, subtitle, excludeViewMode } =
|
||||
SHORTCUTS_META[type];
|
||||
if (typeof title === 'function') {
|
||||
title = title(shortcut, i);
|
||||
}
|
||||
|
@ -289,6 +294,12 @@ function ShortcutsSettings({ onClose }) {
|
|||
if (typeof icon === 'function') {
|
||||
icon = icon(shortcut, i);
|
||||
}
|
||||
if (typeof excludeViewMode === 'function') {
|
||||
excludeViewMode = excludeViewMode(shortcut, i);
|
||||
}
|
||||
const excludedViewMode = excludeViewMode?.includes(
|
||||
snapStates.settings.shortcutsViewMode,
|
||||
);
|
||||
return (
|
||||
<li key={key}>
|
||||
<Icon icon={icon} />
|
||||
|
@ -300,6 +311,11 @@ function ShortcutsSettings({ onClose }) {
|
|||
<small class="ib insignificant">{subtitle}</small>
|
||||
</>
|
||||
)}
|
||||
{excludedViewMode && (
|
||||
<span class="tag">
|
||||
Not available in current view mode
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span class="shortcut-actions">
|
||||
<button
|
||||
|
@ -468,6 +484,11 @@ const fetchLists = pmem(
|
|||
},
|
||||
);
|
||||
|
||||
const FORM_NOTES = {
|
||||
search: `For multi-column mode, search term is required, else the column will not be shown.`,
|
||||
hashtag: 'Multiple hashtags are supported. Space-separated.',
|
||||
};
|
||||
|
||||
function ShortcutForm({
|
||||
onSubmit,
|
||||
disabled,
|
||||
|
@ -615,6 +636,7 @@ function ShortcutForm({
|
|||
<span>{text}</span>{' '}
|
||||
<input
|
||||
type={type}
|
||||
switch={type === 'checkbox' || undefined}
|
||||
name={name}
|
||||
placeholder={placeholder}
|
||||
required={type === 'text' && !notRequired}
|
||||
|
@ -642,6 +664,12 @@ function ShortcutForm({
|
|||
);
|
||||
},
|
||||
)}
|
||||
{!!FORM_NOTES[currentType] && (
|
||||
<p class="form-note insignificant">
|
||||
<Icon icon="info" />
|
||||
{FORM_NOTES[currentType]}
|
||||
</p>
|
||||
)}
|
||||
<footer>
|
||||
<button
|
||||
type="submit"
|
||||
|
|
|
@ -1,18 +1,47 @@
|
|||
#search-page .deck > header .header-grid {
|
||||
grid-template-columns: auto 1fr auto;
|
||||
}
|
||||
#search-page header input {
|
||||
width: 100%;
|
||||
padding: 8px 16px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background-color: var(--bg-faded-color);
|
||||
border: 2px solid transparent;
|
||||
#search-page header {
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 8px 16px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background-color: var(--bg-faded-color);
|
||||
border: 2px solid transparent;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
background-color: var(--bg-color);
|
||||
border-color: var(--link-color);
|
||||
}
|
||||
|
||||
#columns & {
|
||||
font-weight: bold;
|
||||
background-color: transparent;
|
||||
text-align: center;
|
||||
padding-inline: 8px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
#search-page header input:focus {
|
||||
outline: 0;
|
||||
background-color: var(--bg-color);
|
||||
border-color: var(--link-color);
|
||||
|
||||
#columns #search-page {
|
||||
.header-grid {
|
||||
.header-side {
|
||||
min-width: 40px;
|
||||
|
||||
&:last-of-type {
|
||||
button {
|
||||
display: block;
|
||||
|
||||
&:not(:hover, :focus) {
|
||||
color: var(--text-insignificant-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#search-page ul.accounts-list {
|
||||
|
|
|
@ -16,21 +16,26 @@ import Status from '../components/status';
|
|||
import { api } from '../utils/api';
|
||||
import { fetchRelationships } from '../utils/relationships';
|
||||
import shortenNumber from '../utils/shorten-number';
|
||||
import usePageVisibility from '../utils/usePageVisibility';
|
||||
import useScroll from '../utils/useScroll';
|
||||
import useTitle from '../utils/useTitle';
|
||||
|
||||
const SHORT_LIMIT = 5;
|
||||
const LIMIT = 40;
|
||||
const emptySearchParams = new URLSearchParams();
|
||||
|
||||
function Search(props) {
|
||||
const params = useParams();
|
||||
function Search({ columnMode, ...props }) {
|
||||
const params = columnMode ? {} : useParams();
|
||||
const { masto, instance, authenticated } = api({
|
||||
instance: params.instance,
|
||||
});
|
||||
const [uiState, setUIState] = useState('default');
|
||||
const [searchParams] = useSearchParams();
|
||||
const [searchParams] = columnMode ? [emptySearchParams] : useSearchParams();
|
||||
const searchFormRef = useRef();
|
||||
const q = props?.query || searchParams.get('q');
|
||||
const type = props?.type || searchParams.get('type');
|
||||
const type = columnMode
|
||||
? 'statuses'
|
||||
: props?.type || searchParams.get('type');
|
||||
useTitle(
|
||||
q
|
||||
? `Search: ${q}${
|
||||
|
@ -86,6 +91,10 @@ function Search(props) {
|
|||
};
|
||||
|
||||
function loadResults(firstLoad) {
|
||||
if (firstLoad) {
|
||||
offsetRef.current = 0;
|
||||
}
|
||||
|
||||
if (!firstLoad && !authenticated) {
|
||||
// Search results pagination is only available to authenticated users
|
||||
return;
|
||||
|
@ -142,6 +151,22 @@ function Search(props) {
|
|||
})();
|
||||
}
|
||||
|
||||
const { reachStart } = useScroll({
|
||||
scrollableRef,
|
||||
});
|
||||
const lastHiddenTime = useRef();
|
||||
usePageVisibility((visible) => {
|
||||
if (visible && reachStart) {
|
||||
const timeDiff = Date.now() - lastHiddenTime.current;
|
||||
if (!lastHiddenTime.current || timeDiff > 1000 * 3) {
|
||||
// 3 seconds
|
||||
loadResults(true);
|
||||
} else {
|
||||
lastHiddenTime.current = Date.now();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (q) {
|
||||
searchFormRef.current?.setValue?.(q);
|
||||
|
@ -172,11 +197,22 @@ function Search(props) {
|
|||
<NavMenu />
|
||||
</div>
|
||||
<SearchForm ref={searchFormRef} />
|
||||
<div class="header-side"> </div>
|
||||
<div class="header-side">
|
||||
<button
|
||||
type="button"
|
||||
class="plain"
|
||||
onClick={() => {
|
||||
loadResults(true);
|
||||
}}
|
||||
disabled={uiState === 'loading'}
|
||||
>
|
||||
<Icon icon="search" size="l" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
{!!q && (
|
||||
{!!q && !columnMode && (
|
||||
<div
|
||||
ref={filterBarParent}
|
||||
class={`filter-bar ${uiState === 'loading' ? 'loading' : ''}`}
|
||||
|
|
Loading…
Add table
Reference in a new issue