Skip to content

Commit

Permalink
feat(client-electron): add simple pagination to analyze view
Browse files Browse the repository at this point in the history
fixes #294
  • Loading branch information
marcincichocki committed Oct 12, 2022
1 parent f26af63 commit fd714e8
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 29 deletions.
18 changes: 10 additions & 8 deletions src/core/solver/breach-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,20 +170,22 @@ export class BreachProtocol {
}

/** Solve grid with every sequence. */
solveAll() {
return Array.from(this.factory.getSequences()).map((s) =>
this.solveForSequence(s)
);
}

solve() {
*solveAll() {
for (const sequence of this.factory.getSequences()) {
const result = this.solveForSequence(sequence);

if (result) {
return result;
yield result;
}
}
}

solve() {
const { value } = this.solveAll().next();

if (value) {
return value;
}

return null;
}
Expand Down
8 changes: 8 additions & 0 deletions src/electron/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ScreenshotDisplayOutput } from 'screenshot-desktop';
import {
Action,
Analysis,
AnalysisResult,
AppSettings,
HistoryEntry,
UpdateStatus,
Expand All @@ -18,6 +19,7 @@ export const ActionTypes = {
SET_UPDATE_STATUS: 'SET_UPDATE_STATUS',
SET_ANALYSIS: 'SET_ANALYSIS',
CLEAR_ANALYSIS: 'CLEAR_ANALYSIS',
ADD_ANALYSIS_RESULTS: 'ADD_ANALYSIS_RESULTS',
} as const;

export class SetStatusAction implements Action {
Expand Down Expand Up @@ -75,3 +77,9 @@ export class ClearAnalysisAction implements Action {
readonly type = ActionTypes.CLEAR_ANALYSIS;
readonly payload: Analysis = null;
}

export class AddAnalysisResultsAction implements Action {
readonly type = ActionTypes.ADD_ANALYSIS_RESULTS;

constructor(public readonly payload: AnalysisResult) {}
}
7 changes: 6 additions & 1 deletion src/electron/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,14 @@ export interface AnalysisOptions {
origin: AnalysisOrigin;
}

export interface AnalysisResult {
items: BreachProtocolResultJSON[];
hasNext: boolean;
}

export interface Analysis {
entry: HistoryEntry;
results: BreachProtocolResultJSON[];
result: AnalysisResult;
options: AnalysisOptions;
}

Expand Down
16 changes: 16 additions & 0 deletions src/electron/main/store/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Action,
ActionTypes,
Analysis,
AnalysisResult,
AppSettings,
AppStats,
BreachProtocolStatus,
Expand Down Expand Up @@ -110,6 +111,20 @@ const removeHistoryEntry: Handler<string> = (state, { payload }) => ({
const setUpdateStatus = createSetHandler<UpdateStatus>('updateStatus');
const setAnalysis = createSetHandler<Analysis>('analysis');

const addAnalysisResults: Handler<AnalysisResult> = (
state: State,
{ payload }
) => {
const items = [...(state.analysis?.result.items ?? []), ...payload.items];
const result = { items, hasNext: payload.hasNext };
const analysis = { ...state.analysis, result };

return {
...state,
analysis,
};
};

export const appReducer = createReducer<State>({
[ActionTypes.SET_DISPLAYS]: setDisplays,
[ActionTypes.SET_STATUS]: setStatus,
Expand All @@ -120,4 +135,5 @@ export const appReducer = createReducer<State>({
[ActionTypes.SET_UPDATE_STATUS]: setUpdateStatus,
[ActionTypes.SET_ANALYSIS]: setAnalysis,
[ActionTypes.CLEAR_ANALYSIS]: setAnalysis,
[ActionTypes.ADD_ANALYSIS_RESULTS]: addAnalysisResults,
});
18 changes: 13 additions & 5 deletions src/electron/renderer/pages/SelectSequence.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { BreachProtocolResultJSON, isDaemonsFragment } from '@/core';
import { WorkerStatus } from '@/electron/common';
import { useContext, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import styled, { css } from 'styled-components';
import { dispatchAsyncRequest } from '../common';
import { Col, FlatButton, HistoryViewer, Row, Spacer } from '../components';
import { Only } from '../components/Only';
import { StateContext } from '../state';

const SequenceList = styled.ul`
Expand Down Expand Up @@ -46,18 +46,17 @@ function toUniqueValue(result: BreachProtocolResultJSON) {

export const SelectSequence = () => {
const {
analysis: { entry, options, results },
analysis: { entry, options, result },
} = useContext(StateContext);
const { status } = useContext(StateContext);
const [activeResult, setActiveResult] =
useState<BreachProtocolResultJSON>(null);
const navigate = useNavigate();
const { rawData: daemons } = entry.fragments.find(isDaemonsFragment);
const isWorking = status === WorkerStatus.Working;
const fromScreenShot = options.origin === 'screenshot';

useEffect(() => {
setActiveResult(null);
setActiveResult(result.items[0]);
}, [entry.uuid]);

function isActiveSequence(result: BreachProtocolResultJSON) {
Expand All @@ -74,11 +73,15 @@ export const SelectSequence = () => {
});
}

function loadMore() {
dispatchAsyncRequest({ type: 'ANALYZE_LOAD_MORE' });
}

return (
<>
<Col gap grow>
<SequenceList>
{results.map((r, i) => (
{result.items.map((r, i) => (
<Sequence
key={i}
active={isActiveSequence(r)}
Expand All @@ -92,6 +95,11 @@ export const SelectSequence = () => {
</Sequence>
))}
</SequenceList>
<Only when={result.hasNext}>
<FlatButton disabled={isWorking} color="accent" onClick={loadMore}>
Load more
</FlatButton>
</Only>
</Col>
<Col gap>
<HistoryViewer entry={entry} customResult={activeResult} />
Expand Down
52 changes: 40 additions & 12 deletions src/electron/worker/autosolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,9 @@ export class BreachProtocolAutosolver {
}
}

/** Get every unique result. */
getResults() {
return this.progress.has(BreachProtocolSolveProgress.FragmentsValid)
? this.game
.solveAll()
// Not every sequence have a solution.
.filter(Boolean)
.map((r) => r.toJSON())
// This filter does not guarantee that shortest sequence will be preserved.
.filter(uniqueWith((r) => r.resolvedSequence.parts.sort().join('')))
: [];
/** Returns paginated list of results. */
*getPaginatedResults(size: number) {
yield* this.paginate(this.getResults(), size);
}

async solve() {
Expand Down Expand Up @@ -133,6 +125,42 @@ export class BreachProtocolAutosolver {
this.resolveJob();
}

private *getResults() {
const registry = new Set<string>();

for (const result of this.game.solveAll()) {
const json = result.toJSON();
const id = json.resolvedSequence.parts.sort().join('');

if (!registry.has(id)) {
registry.add(id);

yield json;
}
}
}

private *paginate<T>(generator: Generator<T>, size: number) {
let items: T[] = [];

while (true) {
const { value, done } = generator.next();
const hasNext = !done;

if (items.length === size || done) {
yield { items, hasNext };

if (done) {
break;
}

items = [];
}

items.push(value);
}
}

private getNormalizedRawData({ rawData }: BreachProtocolRecognitionResult) {
const bufferSize = this.settings.useFixedBufferSize
? (this.settings.fixedBufferSize as BufferSize)
Expand Down Expand Up @@ -167,7 +195,7 @@ export class BreachProtocolAutosolver {
private async findSolution() {
if (this.status !== BreachProtocolStatus.Pending) return;

this.result = this.game.solve().toJSON();
this.result = this.game.solve()?.toJSON();

if (!this.result) {
return this.rejectJob();
Expand Down
32 changes: 29 additions & 3 deletions src/electron/worker/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import { HierarchyProvider } from '@/core/solver/hierarchy/hierarchy-provider';
import {
Action,
ActionTypes,
AddAnalysisResultsAction,
AddHistoryEntryAction,
AnalysisInput,
AnalysisOptions,
AnalysisResult,
AppSettings,
BreachProtocolStatus,
ClearAnalysisAction,
Expand Down Expand Up @@ -65,6 +67,7 @@ export class BreachProtocolWorker {
private readonly player = new BreachProtocolSoundPlayer(this.settings);

private bpa: BreachProtocolAutosolver = null;
private paginator: Generator<AnalysisResult, void, unknown>;

private status: WorkerStatus = WorkerStatus.Bootstrap;

Expand All @@ -76,6 +79,7 @@ export class BreachProtocolWorker {
ANALYZE_DISCARD: this.discardAnalyze.bind(this),
ANALYZE_RESOLVE: this.createTask(this.analyzeResolve),
ANALYZE_FILE: this.createTask(this.analyzeFile),
ANALYZE_LOAD_MORE: this.createTask(this.loadMoreResults),
};

private async loadAndSetActiveDisplay() {
Expand Down Expand Up @@ -224,11 +228,18 @@ export class BreachProtocolWorker {
this.focusRendererWindow();
}
} else {
const results = this.bpa.getResults();
this.paginator = this.bpa.getPaginatedResults(10);

const { value: result } = this.paginator.next();
const options = this.getAnalysisOptions(input);
this.dispatch(new SetAnalysisAction({ entry, results, options }));

this.focusRendererWindow();
if (result) {
this.dispatch(new SetAnalysisAction({ entry, options, result }));

this.focusRendererWindow();
} else {
throw new Error('There are no other results.');
}
}
}

Expand Down Expand Up @@ -309,6 +320,7 @@ export class BreachProtocolWorker {
if (this.bpa) {
this.bpa.dispose();
this.bpa = null;
this.paginator = null;

if (!skipClean) {
this.dispatch(new ClearAnalysisAction());
Expand All @@ -320,6 +332,20 @@ export class BreachProtocolWorker {
this.discardAnalysis();
}

private async loadMoreResults() {
if (!this.bpa || !this.paginator) {
throw new Error('Paginator not found.');
}

const { value } = this.paginator.next();

if (value) {
this.dispatch(new AddAnalysisResultsAction(value));
} else {
throw new Error('There are no other results.');
}
}

private async analyzeFile({ data }: Request<string>) {
await this.onWorkerAnazyle(null, data);
}
Expand Down

0 comments on commit fd714e8

Please sign in to comment.