pl-fe: Remove location search reducer

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-11-09 13:41:49 +01:00
parent df3b5dfc35
commit 6c61f749fa
10 changed files with 43 additions and 113 deletions

View file

@ -679,7 +679,7 @@ const selectComposeSuggestion = (composeId: string, position: number, token: str
(dispatch: AppDispatch, getState: () => RootState) => {
let completion = '', startPosition = position;
if (typeof suggestion === 'object' && suggestion.id) {
if (typeof suggestion === 'object' && 'id' in suggestion) {
completion = isNativeEmoji(suggestion) ? suggestion.native : suggestion.colons;
startPosition = position - 1;

View file

@ -9,10 +9,6 @@ import { STATUS_FETCH_SOURCE_FAIL, STATUS_FETCH_SOURCE_REQUEST, STATUS_FETCH_SOU
import type { Account, CreateEventParams, Location, MediaAttachment, PaginatedResponse, Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';
const LOCATION_SEARCH_REQUEST = 'LOCATION_SEARCH_REQUEST' as const;
const LOCATION_SEARCH_SUCCESS = 'LOCATION_SEARCH_SUCCESS' as const;
const LOCATION_SEARCH_FAIL = 'LOCATION_SEARCH_FAIL' as const;
const EVENT_SUBMIT_REQUEST = 'EVENT_SUBMIT_REQUEST' as const;
const EVENT_SUBMIT_SUCCESS = 'EVENT_SUBMIT_SUCCESS' as const;
const EVENT_SUBMIT_FAIL = 'EVENT_SUBMIT_FAIL' as const;
@ -73,18 +69,6 @@ const messages = defineMessages({
rejected: { id: 'compose_event.participation_requests.reject_success', defaultMessage: 'User rejected' },
});
const locationSearch = (query: string, signal?: AbortSignal) =>
(dispatch: AppDispatch, getState: () => RootState) => {
dispatch({ type: LOCATION_SEARCH_REQUEST, query });
return getClient(getState).search.searchLocation(query, { signal }).then((locations) => {
dispatch({ type: LOCATION_SEARCH_SUCCESS, locations });
return locations;
}).catch(error => {
dispatch({ type: LOCATION_SEARCH_FAIL });
throw error;
});
};
const submitEvent = ({
statusId,
name,
@ -509,9 +493,6 @@ type EventsAction =
| EventFormSetAction;
export {
LOCATION_SEARCH_REQUEST,
LOCATION_SEARCH_SUCCESS,
LOCATION_SEARCH_FAIL,
EVENT_SUBMIT_REQUEST,
EVENT_SUBMIT_SUCCESS,
EVENT_SUBMIT_FAIL,
@ -547,7 +528,6 @@ export {
JOINED_EVENTS_FETCH_REQUEST,
JOINED_EVENTS_FETCH_SUCCESS,
JOINED_EVENTS_FETCH_FAIL,
locationSearch,
submitEvent,
submitEventRequest,
submitEventSuccess,

View file

@ -0,0 +1,16 @@
import { useQuery } from '@tanstack/react-query';
import { useClient } from 'pl-fe/hooks/use-client';
const useSearchLocation = (query: string) => {
const client = useClient();
return useQuery({
queryKey: ['search', 'location', query],
queryFn: ({ signal }) => client.search.searchLocation(query, { signal }),
gcTime: 60 * 1000,
enabled: !!query.trim(),
});
};
export { useSearchLocation };

View file

@ -8,15 +8,18 @@ import Portal from 'pl-fe/components/ui/portal';
import AutosuggestAccount from 'pl-fe/features/compose/components/autosuggest-account';
import { textAtCursorMatchesToken } from 'pl-fe/utils/suggestions';
import AutosuggestLocation from './autosuggest-location';
import type { Location } from 'pl-api';
import type { Menu, MenuItem } from 'pl-fe/components/dropdown-menu';
import type { InputThemes } from 'pl-fe/components/ui/input';
import type { Emoji } from 'pl-fe/features/emoji';
type AutoSuggestion = string | Emoji;
type AutoSuggestion = string | Emoji | Location;
interface IAutosuggestInput extends Pick<React.HTMLAttributes<HTMLInputElement>, 'onChange' | 'onKeyUp' | 'onKeyDown'> {
value: string;
suggestions: Array<any>;
suggestions: Array<AutoSuggestion>;
disabled?: boolean;
placeholder?: string;
onSuggestionSelected: (tokenStart: number, lastToken: string | null, suggestion: AutoSuggestion) => void;
@ -29,7 +32,6 @@ interface IAutosuggestInput extends Pick<React.HTMLAttributes<HTMLInputElement>,
searchTokens?: string[];
maxLength?: number;
menu?: Menu;
renderSuggestion?: React.FC<{ id: string }>;
hidePortal?: boolean;
theme?: InputThemes;
}
@ -165,16 +167,12 @@ const AutosuggestInput: React.FC<IAutosuggestInput> = ({
const renderSuggestion = (suggestion: AutoSuggestion, i: number) => {
let inner, key;
if (props.renderSuggestion && typeof suggestion === 'string') {
const RenderSuggestion = props.renderSuggestion;
inner = <RenderSuggestion id={suggestion} />;
key = suggestion;
if (typeof suggestion === 'object' && 'origin_id' in suggestion) {
inner = <AutosuggestLocation location={suggestion} />;
key = suggestion.origin_id;
} else if (typeof suggestion === 'object') {
inner = <AutosuggestEmoji emoji={suggestion} />;
key = suggestion.id;
} else if (suggestion[0] === '#') {
inner = suggestion;
key = suggestion;
} else {
inner = <AutosuggestAccount id={suggestion} />;
key = suggestion;

View file

@ -4,7 +4,8 @@ import HStack from 'pl-fe/components/ui/hstack';
import Icon from 'pl-fe/components/ui/icon';
import Stack from 'pl-fe/components/ui/stack';
import Text from 'pl-fe/components/ui/text';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import type { Location } from 'pl-api';
const buildingCommunityIcon = require('@tabler/icons/outline/building-community.svg');
const homeIcon = require('@tabler/icons/outline/home-2.svg');
@ -21,12 +22,10 @@ const ADDRESS_ICONS: Record<string, string> = {
};
interface IAutosuggestLocation {
id: string;
location: Location;
}
const AutosuggestLocation: React.FC<IAutosuggestLocation> = ({ id }) => {
const location = useAppSelector((state) => state.locations.get(id));
const AutosuggestLocation: React.FC<IAutosuggestLocation> = ({ location }) => {
if (!location) return null;
return (

View file

@ -1,14 +1,13 @@
import { useDebounce } from '@uidotdev/usehooks';
import clsx from 'clsx';
import throttle from 'lodash/throttle';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import React, { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { locationSearch } from 'pl-fe/actions/events';
import { useSearchLocation } from 'pl-fe/api/hooks/search/use-search-location';
import AutosuggestInput, { AutoSuggestion } from 'pl-fe/components/autosuggest-input';
import Icon from 'pl-fe/components/icon';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import AutosuggestLocation from './autosuggest-location';
import type { Location } from 'pl-api';
const noOp = () => {};
@ -17,27 +16,24 @@ const messages = defineMessages({
});
interface ILocationSearch {
onSelected: (locationId: string) => void;
onSelected: (location: Location) => void;
}
const LocationSearch: React.FC<ILocationSearch> = ({ onSelected }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const [locationIds, setLocationIds] = useState<Array<string>>([]);
const controller = useRef(new AbortController());
const [value, setValue] = useState('');
const debouncedValue = useDebounce(value, 400);
const locationsQuery = useSearchLocation(debouncedValue);
const empty = !(value.length > 0);
const handleChange: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
refreshCancelToken();
handleLocationSearch(target.value);
setValue(target.value);
};
const handleSelected = (_tokenStart: number, _lastToken: string | null, suggestion: AutoSuggestion) => {
if (typeof suggestion === 'string') {
if (typeof suggestion === 'object' && 'origin_id' in suggestion) {
onSelected(suggestion);
}
};
@ -56,30 +52,6 @@ const LocationSearch: React.FC<ILocationSearch> = ({ onSelected }) => {
}
};
const refreshCancelToken = () => {
controller.current.abort();
controller.current = new AbortController();
};
const clearResults = () => {
setLocationIds([]);
};
const handleLocationSearch = useCallback(throttle(q => {
dispatch(locationSearch(q, controller.current.signal))
.then((locations: { origin_id: string }[]) => {
const locationIds = locations.map(location => location.origin_id);
setLocationIds(locationIds);
})
.catch(noOp);
}, 900, { leading: true, trailing: true }), []);
useEffect(() => {
if (value === '') {
clearResults();
}
}, [value]);
return (
<div className='relative'>
<AutosuggestInput
@ -87,13 +59,12 @@ const LocationSearch: React.FC<ILocationSearch> = ({ onSelected }) => {
placeholder={intl.formatMessage(messages.placeholder)}
value={value}
onChange={handleChange}
suggestions={locationIds}
suggestions={locationsQuery.data || []}
onSuggestionsFetchRequested={noOp}
onSuggestionsClearRequested={noOp}
onSuggestionSelected={handleSelected}
searchTokens={[]}
onKeyDown={handleKeyDown}
renderSuggestion={AutosuggestLocation}
/>
<div role='button' tabIndex={0} className='absolute inset-y-0 right-0 flex cursor-pointer items-center px-3 rtl:left-0 rtl:right-auto' onClick={handleClear}>
<Icon src={require('@tabler/icons/outline/search.svg')} className={clsx('size-5 text-gray-600', { 'hidden': !empty })} />

View file

@ -93,16 +93,8 @@ const EditEvent: React.FC<IEditEvent> = ({ statusId }) => {
setApprovalRequired(target.checked);
};
const onChangeLocation = (value: string | null) => {
dispatch((_, getState) => {
let location = null;
if (value) {
location = getState().locations.get(value, null);
}
setLocation(location);
});
const onChangeLocation = (location: Location | null) => {
setLocation(location);
};
const handleFiles = (files: FileList) => {

View file

@ -44,7 +44,9 @@ import AutosuggestAccount from '../../components/autosuggest-account';
import { $createEmojiNode } from '../nodes/emoji-node';
import { $createMentionNode } from '../nodes/mention-node';
import type { AutoSuggestion } from 'pl-fe/components/autosuggest-input';
import type { Emoji } from 'pl-fe/features/emoji';
type AutoSuggestion = string | Emoji;
type QueryMatch = {
leadOffset: number;

View file

@ -23,7 +23,6 @@ import instance from './instance';
import listAdder from './list-adder';
import listEditor from './list-editor';
import lists from './lists';
import locations from './locations';
import me from './me';
import meta from './meta';
import notifications from './notifications';
@ -65,7 +64,6 @@ const reducers = {
listAdder,
listEditor,
lists,
locations,
me,
meta,
notifications,

View file

@ -1,26 +0,0 @@
import { Map as ImmutableMap } from 'immutable';
import { Location } from 'pl-api';
import { AnyAction } from 'redux';
import { LOCATION_SEARCH_SUCCESS } from 'pl-fe/actions/events';
type State = ImmutableMap<any, Location>;
const initialState: State = ImmutableMap();
const normalizeLocations = (state: State, locations: Array<Location>) =>
locations.reduce(
(state: State, location: Location) => state.set(location.origin_id, location),
state,
);
const locations = (state: State = initialState, action: AnyAction): State => {
switch (action.type) {
case LOCATION_SEARCH_SUCCESS:
return normalizeLocations(state, action.locations);
default:
return state;
}
};
export { locations as default };