From 7384015505838d10431e34db5b5f50d6f2f66aee Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 7 Sep 2023 14:56:19 +0200 Subject: [PATCH] [Glitch] Add recent searches in web UI Port 9b2bc3d1de301c686208b43a8efef5bc808f8e4e to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/actions/search.js | 45 ++++++++++++++----- .../flavours/glitch/actions/store.js | 2 + .../features/compose/components/search.jsx | 26 +++++++++-- .../compose/containers/search_container.js | 2 +- .../flavours/glitch/reducers/search.js | 9 ++-- app/javascript/flavours/glitch/settings.js | 1 + 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/app/javascript/flavours/glitch/actions/search.js b/app/javascript/flavours/glitch/actions/search.js index e25d8534a..5bb3aa3a7 100644 --- a/app/javascript/flavours/glitch/actions/search.js +++ b/app/javascript/flavours/glitch/actions/search.js @@ -1,3 +1,7 @@ +import { fromJS } from 'immutable'; + +import { searchHistory } from 'flavours/glitch/settings'; + import api from '../api'; import { fetchRelationships } from './accounts'; @@ -15,8 +19,7 @@ export const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST'; export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS'; export const SEARCH_EXPAND_FAIL = 'SEARCH_EXPAND_FAIL'; -export const SEARCH_RESULT_CLICK = 'SEARCH_RESULT_CLICK'; -export const SEARCH_RESULT_FORGET = 'SEARCH_RESULT_FORGET'; +export const SEARCH_HISTORY_UPDATE = 'SEARCH_HISTORY_UPDATE'; export function changeSearch(value) { return { @@ -165,16 +168,34 @@ export const openURL = routerHistory => (dispatch, getState) => { }); }; -export const clickSearchResult = (q, type) => ({ - type: SEARCH_RESULT_CLICK, +export const clickSearchResult = (q, type) => (dispatch, getState) => { + const previous = getState().getIn(['search', 'recent']); + const me = getState().getIn(['meta', 'me']); + const current = previous.add(fromJS({ type, q })).takeLast(4); - result: { - type, - q, - }, + searchHistory.set(me, current.toJS()); + dispatch(updateSearchHistory(current)); +}; + +export const forgetSearchResult = q => (dispatch, getState) => { + const previous = getState().getIn(['search', 'recent']); + const me = getState().getIn(['meta', 'me']); + const current = previous.filterNot(result => result.get('q') === q); + + searchHistory.set(me, current.toJS()); + dispatch(updateSearchHistory(current)); +}; + +export const updateSearchHistory = recent => ({ + type: SEARCH_HISTORY_UPDATE, + recent, }); -export const forgetSearchResult = q => ({ - type: SEARCH_RESULT_FORGET, - q, -}); +export const hydrateSearch = () => (dispatch, getState) => { + const me = getState().getIn(['meta', 'me']); + const history = searchHistory.get(me); + + if (history !== null) { + dispatch(updateSearchHistory(history)); + } +}; \ No newline at end of file diff --git a/app/javascript/flavours/glitch/actions/store.js b/app/javascript/flavours/glitch/actions/store.js index e57b37a12..da07142b3 100644 --- a/app/javascript/flavours/glitch/actions/store.js +++ b/app/javascript/flavours/glitch/actions/store.js @@ -2,6 +2,7 @@ import { Iterable, fromJS } from 'immutable'; import { hydrateCompose } from './compose'; import { importFetchedAccounts } from './importer'; +import { hydrateSearch } from './search'; import { saveSettings } from './settings'; export const STORE_HYDRATE = 'STORE_HYDRATE'; @@ -34,6 +35,7 @@ export function hydrateStore(rawState) { }); dispatch(hydrateCompose()); + dispatch(hydrateSearch()); dispatch(importFetchedAccounts(Object.values(rawState.accounts))); dispatch(saveSettings()); }; diff --git a/app/javascript/flavours/glitch/features/compose/components/search.jsx b/app/javascript/flavours/glitch/features/compose/components/search.jsx index bebf4d8ab..3d79e43c5 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.jsx +++ b/app/javascript/flavours/glitch/features/compose/components/search.jsx @@ -18,7 +18,17 @@ const messages = defineMessages({ placeholderSignedIn: { id: 'search.search_or_paste', defaultMessage: 'Search or paste URL' }, }); -// The component. +const labelForRecentSearch = search => { + switch(search.get('type')) { + case 'account': + return `@${search.get('q')}`; + case 'hashtag': + return `#${search.get('q')}`; + default: + return search.get('q'); + } +}; + class Search extends PureComponent { static contextTypes = { @@ -198,12 +208,16 @@ class Search extends PureComponent { }; handleRecentSearchClick = search => { + const { onChange } = this.props; const { router } = this.context; if (search.get('type') === 'account') { router.history.push(`/@${search.get('q')}`); } else if (search.get('type') === 'hashtag') { router.history.push(`/tags/${search.get('q')}`); + } else { + onChange(search.get('q')); + this._submit(search.get('type')); } this._unfocus(); @@ -232,11 +246,15 @@ class Search extends PureComponent { } _submit (type) { - const { onSubmit, openInRoute } = this.props; + const { onSubmit, openInRoute, value, onClickSearchResult } = this.props; const { router } = this.context; onSubmit(type); + if (value) { + onClickSearchResult(value, type); + } + if (openInRoute) { router.history.push('/search'); } @@ -254,7 +272,7 @@ class Search extends PureComponent { const { recent } = this.props; return recent.toArray().map(search => ({ - label: search.get('type') === 'account' ? `@${search.get('q')}` : `#${search.get('q')}`, + label: labelForRecentSearch(search), action: () => this.handleRecentSearchClick(search), @@ -368,7 +386,7 @@ class Search extends PureComponent { {searchEnabled ? (
{this.defaultOptions.map(({ key, label, action }, i) => ( - ))} diff --git a/app/javascript/flavours/glitch/features/compose/containers/search_container.js b/app/javascript/flavours/glitch/features/compose/containers/search_container.js index 52dc65687..17be30edc 100644 --- a/app/javascript/flavours/glitch/features/compose/containers/search_container.js +++ b/app/javascript/flavours/glitch/features/compose/containers/search_container.js @@ -15,7 +15,7 @@ import Search from '../components/search'; const mapStateToProps = state => ({ value: state.getIn(['search', 'value']), submitted: state.getIn(['search', 'submitted']), - recent: state.getIn(['search', 'recent']), + recent: state.getIn(['search', 'recent']).reverse(), }); const mapDispatchToProps = dispatch => ({ diff --git a/app/javascript/flavours/glitch/reducers/search.js b/app/javascript/flavours/glitch/reducers/search.js index 787679a61..a215282aa 100644 --- a/app/javascript/flavours/glitch/reducers/search.js +++ b/app/javascript/flavours/glitch/reducers/search.js @@ -14,8 +14,7 @@ import { SEARCH_SHOW, SEARCH_EXPAND_REQUEST, SEARCH_EXPAND_SUCCESS, - SEARCH_RESULT_CLICK, - SEARCH_RESULT_FORGET, + SEARCH_HISTORY_UPDATE, } from 'flavours/glitch/actions/search'; const initialState = ImmutableMap({ @@ -73,10 +72,8 @@ export default function search(state = initialState, action) { case SEARCH_EXPAND_SUCCESS: const results = action.searchType === 'hashtags' ? ImmutableOrderedSet(fromJS(action.results.hashtags)) : action.results[action.searchType].map(item => item.id); return state.updateIn(['results', action.searchType], list => list.union(results)); - case SEARCH_RESULT_CLICK: - return state.update('recent', set => set.add(fromJS(action.result))); - case SEARCH_RESULT_FORGET: - return state.update('recent', set => set.filterNot(result => result.get('q') === action.q)); + case SEARCH_HISTORY_UPDATE: + return state.set('recent', ImmutableOrderedSet(fromJS(action.recent))); default: return state; } diff --git a/app/javascript/flavours/glitch/settings.js b/app/javascript/flavours/glitch/settings.js index 46cfadfa3..aefb8e0e9 100644 --- a/app/javascript/flavours/glitch/settings.js +++ b/app/javascript/flavours/glitch/settings.js @@ -46,3 +46,4 @@ export default class Settings { export const pushNotificationsSetting = new Settings('mastodon_push_notification_data'); export const tagHistory = new Settings('mastodon_tag_history'); export const bannerSettings = new Settings('mastodon_banner_settings'); +export const searchHistory = new Settings('mastodon_search_history'); \ No newline at end of file