mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(app): keep quick switcher focus when results are recomputed (#1746)
This commit is contained in:
1 parent
30a61ce90f
commit
7b5c82c6cf
3 files changed
+64
-1
No files matched your search
@@ -19,6 +19,7 @@ import {
|
|||||||
generateQueryModeResults,
|
generateQueryModeResults,
|
||||||
resolveTransformedMember,
|
resolveTransformedMember,
|
||||||
} from '@app/features/search/state/QuickSwitcherResultGenerators';
|
} from '@app/features/search/state/QuickSwitcherResultGenerators';
|
||||||
|
import {resolveRecomputedSelectedIndex} from '@app/features/search/state/QuickSwitcherSelection';
|
||||||
import type {
|
import type {
|
||||||
CandidateSets,
|
CandidateSets,
|
||||||
ComputeResultsForQueryResult,
|
ComputeResultsForQueryResult,
|
||||||
@@ -404,10 +405,11 @@ class QuickSwitcher {
|
|||||||
if (options.invalidateCandidates ?? true) {
|
if (options.invalidateCandidates ?? true) {
|
||||||
this.invalidateCandidateSets();
|
this.invalidateCandidateSets();
|
||||||
}
|
}
|
||||||
|
const previous = this.results[this.selectedIndex];
|
||||||
const {queryMode, results, selectedIndex} = this.computeResultsForQuery(this.query);
|
const {queryMode, results, selectedIndex} = this.computeResultsForQuery(this.query);
|
||||||
this.queryMode = queryMode;
|
this.queryMode = queryMode;
|
||||||
this.results = results;
|
this.results = results;
|
||||||
this.selectedIndex = selectedIndex;
|
this.selectedIndex = resolveRecomputedSelectedIndex(previous, results, selectedIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private computeResultsForQuery(query: string): ComputeResultsForQueryResult {
|
private computeResultsForQuery(query: string): ComputeResultsForQueryResult {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import {resolveRecomputedSelectedIndex} from '@app/features/search/state/QuickSwitcherSelection';
|
||||||
|
import type {QuickSwitcherResult} from '@app/features/search/state/QuickSwitcherTypes';
|
||||||
|
import {QuickSwitcherResultTypes} from '@fluxer/constants/src/QuickSwitcherConstants';
|
||||||
|
import {describe, expect, it} from 'vitest';
|
||||||
|
|
||||||
|
const result = (type: QuickSwitcherResult['type'], id: string) => ({type, id}) as QuickSwitcherResult;
|
||||||
|
|
||||||
|
describe('resolveRecomputedSelectedIndex', () => {
|
||||||
|
it('follows the focused result when the list is reordered', () => {
|
||||||
|
const previous = result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'c3');
|
||||||
|
const results = [
|
||||||
|
result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'c3'),
|
||||||
|
result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'c1'),
|
||||||
|
result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'c2'),
|
||||||
|
];
|
||||||
|
expect(resolveRecomputedSelectedIndex(previous, results, 0)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the focused result when entries are inserted above it', () => {
|
||||||
|
const previous = result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'c2');
|
||||||
|
const results = [
|
||||||
|
result(QuickSwitcherResultTypes.HEADER, 'h1'),
|
||||||
|
result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'c9'),
|
||||||
|
result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'c2'),
|
||||||
|
];
|
||||||
|
expect(resolveRecomputedSelectedIndex(previous, results, 1)).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back when the focused result disappears', () => {
|
||||||
|
const previous = result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'gone');
|
||||||
|
const results = [result(QuickSwitcherResultTypes.TEXT_CHANNEL, 'c1')];
|
||||||
|
expect(resolveRecomputedSelectedIndex(previous, results, 0)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not confuse ids that repeat across result types', () => {
|
||||||
|
const previous = result(QuickSwitcherResultTypes.GUILD, '42');
|
||||||
|
const results = [result(QuickSwitcherResultTypes.TEXT_CHANNEL, '42'), result(QuickSwitcherResultTypes.GUILD, '42')];
|
||||||
|
expect(resolveRecomputedSelectedIndex(previous, results, 0)).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the fallback when nothing was focused', () => {
|
||||||
|
expect(resolveRecomputedSelectedIndex(undefined, [result(QuickSwitcherResultTypes.GUILD, 'g')], 0)).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import type {QuickSwitcherResult} from '@app/features/search/state/QuickSwitcherTypes';
|
||||||
|
|
||||||
|
export function resolveRecomputedSelectedIndex(
|
||||||
|
previous: QuickSwitcherResult | undefined,
|
||||||
|
results: ReadonlyArray<QuickSwitcherResult>,
|
||||||
|
fallbackIndex: number,
|
||||||
|
): number {
|
||||||
|
if (!previous) {
|
||||||
|
return fallbackIndex;
|
||||||
|
}
|
||||||
|
const restored = results.findIndex((result) => result.type === previous.type && result.id === previous.id);
|
||||||
|
return restored >= 0 ? restored : fallbackIndex;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user