refac: 턴 조각기 함수 중 공용 함수 분리

This commit is contained in:
2022-03-26 21:37:04 +09:00
parent 6d49d564c5
commit 162f0ff38a
2 changed files with 228 additions and 189 deletions
+55 -189
View File
@@ -34,10 +34,10 @@
<div v-if="isEditMode" class="row gx-1">
<div class="col-4 d-grid">
<BDropdown left text="범위">
<BDropdownItem @click="selectTurn()">해제</BDropdownItem>
<BDropdownItem @click="selectAll(true)">모든턴</BDropdownItem>
<BDropdownItem @click="selectStep(0, 2)">홀수턴</BDropdownItem>
<BDropdownItem @click="selectStep(1, 2)">짝수턴</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectTurn()">해제</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectAll()">모든턴</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(0, 2)">홀수턴</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(1, 2)">짝수턴</BDropdownItem>
<BDropdownDivider></BDropdownDivider>
<BDropdownText v-for="spanIdx in [3, 4, 5, 6, 7]" :key="spanIdx">
@@ -48,7 +48,7 @@
class="ignoreMe"
v-for="beginIdx in spanIdx"
:key="beginIdx"
@click="selectStep(beginIdx - 1, spanIdx)"
@click="queryActionHelper.selectStep(beginIdx - 1, spanIdx)"
>{{ beginIdx }}</BButton>
</BButtonGroup>
</BDropdownText>
@@ -73,7 +73,7 @@
<BDropdownItem
v-for="(action, idx) in recentActions"
:key="idx"
@click="void reserveCommandDirect([[Array.from(turnList.values()), action]])"
@click="void reserveCommandDirect([[Array.from(selectedTurnList.values()), action]])"
>{{ action.brief }}</BDropdownItem>
</BDropdown>
</div>
@@ -138,7 +138,7 @@
@dragStart="isDragToggle = true"
@dragDone="
isDragToggle = false;
toggleTurn(...$event);
queryActionHelper.toggleTurn(...$event);
"
v-slot="{ selected }"
>
@@ -156,9 +156,9 @@ toggleTurn(...$event);
size="sm"
:variant="
(isDragToggle && selected.has(`${turnIdx}`)) ? 'light' :
turnList.has(turnIdx)
selectedTurnList.has(turnIdx)
? 'info'
: turnList.size == 0 && prevTurnList.has(turnIdx)
: selectedTurnList.size == 0 && prevSelectedTurnList.has(turnIdx)
? 'success'
: 'primary'
"
@@ -173,7 +173,7 @@ toggleTurn(...$event);
@dragStart="isDragSingle = true"
@dragDone="
isDragSingle = false;
selectTurn(...$event);
queryActionHelper.selectTurn(...$event);
"
v-slot="{ selected }"
>
@@ -309,7 +309,7 @@ declare const staticValues: {
<script lang="ts" setup>
import addMilliseconds from "date-fns/esm/addMilliseconds";
import addMinutes from "date-fns/esm/addMinutes";
import { clone, isString, min, range, repeat, trim } from "lodash";
import { range, trim } from "lodash";
import { stringifyUrl } from "query-string";
import { onMounted, ref, watch } from "vue";
import { formatTime } from "@util/formatTime";
@@ -318,22 +318,16 @@ import { mb_strwidth } from "@util/mb_strwidth";
import { parseTime } from "@util/parseTime";
import { parseYearMonth } from "@util/parseYearMonth";
import DragSelect from "@/components/DragSelect.vue";
import { SammoAPI, type InvalidResponse } from "./SammoAPI";
import type { CommandItem, ReserveBulkCommandResponse, ReserveCommandResponse } from "@/defs";
import { SammoAPI } from "./SammoAPI";
import type { CommandItem, ReserveCommandResponse } from "@/defs";
import CommandSelectForm from "@/components/CommandSelectForm.vue";
import { BButton, BButtonGroup, BDropdownItem, BDropdown, BDropdownText, BDropdownDivider } from "bootstrap-vue-3";
import { StoredActionsHelper } from "./util/StoredActionsHelper";
import type { TurnObj } from '@/defs';
import { unwrap } from "./util/unwrap";
import type { Args } from "./processing/args";
import { QueryActionHelper } from "./util/QueryActionHelper";
type TurnObjWithTime = TurnObj & {
time: string;
year?: number;
month?: number;
tooltip?: string;
style?: Record<string, unknown>;
};
type ReservedCommandResponse = {
result: true;
@@ -406,22 +400,13 @@ setTimeout(() => {
}, 1000 - serverNow.value.getMilliseconds());
const emptyTurn: TurnObjWithTime[] = Array.from<TurnObjWithTime>({
length: staticValues.maxTurn,
}).fill({
arg: {},
brief: "",
action: "",
year: undefined,
month: undefined,
time: "",
});
const editModeKey = `sammo_edit_mode_key`;
const prevTurnList = ref(new Set([0]));
const turnList = ref(new Set<number>());
const reservedCommandList = ref(emptyTurn);
const queryActionHelper = new QueryActionHelper(staticValues.maxTurn);
const reservedCommandList = queryActionHelper.reservedCommandList;
const prevSelectedTurnList = queryActionHelper.prevSelectedTurnList;
const selectedTurnList = queryActionHelper.selectedTurnList;
const isEditMode = ref(localStorage.getItem(editModeKey) === '1');
const flippedMaxTurn = 14;
@@ -456,53 +441,7 @@ function updateNow() {
}, 1000 - serverNow.value.getMilliseconds());
}
function toggleTurn(...reqTurnList: number[] | string[]) {
for (let turnIdx of reqTurnList) {
if (isString(turnIdx)) {
turnIdx = parseInt(turnIdx);
}
if (turnList.value.has(turnIdx)) {
turnList.value.delete(turnIdx);
} else {
turnList.value.add(turnIdx);
}
}
}
function selectTurn(...reqTurnList: number[] | string[]) {
turnList.value.clear();
for (const turnIdx of reqTurnList) {
if (isString(turnIdx)) {
turnList.value.add(parseInt(turnIdx));
} else {
turnList.value.add(turnIdx);
}
}
}
function selectAll(e: Event | true) {
//NOTE: split 구현에 버그가 있어서, 수동으로 구분해야함
if (e !== true && isDropdownChildren(e)) {
return;
}
if (turnList.value.size * 3 > maxTurn) {
turnList.value.clear();
} else {
for (let i = 0; i < maxTurn; i++) {
turnList.value.add(i);
}
}
}
function selectStep(begin: number, step: number) {
turnList.value.clear();
for (const idx of range(0, maxTurn)) {
if ((idx - begin) % step == 0) {
turnList.value.add(idx);
}
}
}
function toggleViewMaxTurn() {
if (viewMaxTurn.value == flippedMaxTurn) {
@@ -620,8 +559,6 @@ async function reloadCommandList() {
}
async function reserveCommandDirect(args: [number[], TurnObj][], reload = true): Promise<boolean> {
const waiterList: Promise<ReserveCommandResponse | InvalidResponse>[] = [];
const query: {
turnList: number[],
action: string,
@@ -637,7 +574,7 @@ async function reserveCommandDirect(args: [number[], TurnObj][], reload = true):
try {
await SammoAPI.Command.ReserveBulkCommand(query);
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
} catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
@@ -650,28 +587,8 @@ async function reserveCommandDirect(args: [number[], TurnObj][], reload = true):
return true;
}
function getSelectedTurnList(): number[] {
if (turnList.value.size) {
return Array.from(turnList.value);
}
if (prevTurnList.value.size) {
return Array.from(prevTurnList.value);
}
return [0];
}
function releaseSelectedTurnList() {
if (turnList.value.size > 0) {
prevTurnList.value.clear();
for (const v of turnList.value) {
prevTurnList.value.add(v);
}
turnList.value.clear();
}
}
async function reserveCommand() {
let reqTurnList: number[] = getSelectedTurnList();
let reqTurnList: number[] = queryActionHelper.getSelectedTurnList();
const commandName = selectedCommand.value.value;
@@ -698,7 +615,7 @@ async function reserveCommand() {
arg: {}
});
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
} catch (e) {
@@ -725,8 +642,8 @@ function chooseQuickReserveCommand(val?: string) {
return;
}
selectedCommand.value = invCommandMap[val];
turnList.value.clear();
turnList.value.add(currentQuickReserveTarget.value);
selectedTurnList.value.clear();
selectedTurnList.value.add(currentQuickReserveTarget.value);
void reserveCommand();
}
function toggleQuickReserveForm(turnIdx: number) {
@@ -759,76 +676,17 @@ const storedActions = storedActionsHelper.storedActions;
async function eraseSelectedTurnList(releaseSelect = true): Promise<boolean> {
const result = await reserveCommandDirect([[
getSelectedTurnList(),
queryActionHelper.getSelectedTurnList(),
emptyTurnObj
]]);
if (releaseSelect) {
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
}
return result;
}
function refineQueryActions(): [number[], TurnObj][] {
const reqTurnList = getSelectedTurnList();
const selectedMinTurnIdx = unwrap(min<number>(reqTurnList));
const buffer: [number[], TurnObj][] = [];
for (const rawTurnIdx of reqTurnList) {
const turnIdx = rawTurnIdx - selectedMinTurnIdx;
const rawAction = reservedCommandList.value[rawTurnIdx]
buffer.push([[turnIdx], {
action: rawAction.action,
arg: clone(rawAction.arg),
brief: rawAction.brief
}]);
}
return buffer;
}
function amplifyQueryActions(rawActions: [number[], TurnObj][], reqTurnList: number[]): [number[], TurnObj][] {
if (reqTurnList.length < 1) {
return [];
}
let minQueryIdx = maxTurn;
let maxQueryIdx = 0;
for (const [turnList] of rawActions) {
for (const turnIdx of turnList) {
minQueryIdx = Math.min(minQueryIdx, turnIdx);
maxQueryIdx = Math.max(maxQueryIdx, turnIdx);
}
}
const queryLength = maxQueryIdx - minQueryIdx + 1;
const queryTurnList: number[] = [reqTurnList[0]];
for (const reqTurnIdx of reqTurnList) {
const last = queryTurnList[queryTurnList.length - 1];
if (reqTurnIdx < last + queryLength) {
continue;
}
queryTurnList.push(reqTurnIdx);
}
const actions: [number[], TurnObj][] = [];
for (const [baseTurnList, action] of rawActions) {
const subTurnList: number[] = [];
for (const baseTurnIdx of baseTurnList) {
for (const queryTurnIdx of queryTurnList) {
const targetTurn = baseTurnIdx + queryTurnIdx;
if (targetTurn >= maxTurn) {
continue;
}
subTurnList.push(baseTurnIdx + queryTurnIdx);
}
}
if (subTurnList.length == 0) {
continue;
}
actions.push([subTurnList, action]);
}
return actions;
}
const clipboard = ref<[number[], TurnObj][] | undefined>(undefined);
@@ -838,9 +696,9 @@ async function clipboardCut(releaseSelect = true) {
}
function clipboardCopy(releaseSelect = true) {
clipboard.value = refineQueryActions();
clipboard.value = queryActionHelper.extractQueryActions();
if (releaseSelect) {
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
}
}
@@ -850,37 +708,37 @@ async function clipboardPaste(releaseSelect = true) {
return;
}
const actions = amplifyQueryActions(rawActions, getSelectedTurnList());
const actions = queryActionHelper.amplifyQueryActions(rawActions, queryActionHelper.getSelectedTurnList());
if (actions.length === 0) {
return;
}
const result = await reserveCommandDirect(actions);
if (releaseSelect) {
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
}
return result;
}
async function subRepeatCommand(releaseSelect = true): Promise<boolean> {
const reqTurnList = getSelectedTurnList().sort((a, b) => (a - b));
const reqTurnList = queryActionHelper.getSelectedTurnList();
const selectedMinTurnIdx = reqTurnList[0];
const selectedMaxTurnIdx = reqTurnList[reqTurnList.length - 1];
const queryLength = selectedMaxTurnIdx - selectedMinTurnIdx + 1;
const rawActions = refineQueryActions();
const actions = amplifyQueryActions(rawActions, range(selectedMinTurnIdx, maxTurn, queryLength));
const rawActions = queryActionHelper.extractQueryActions();
const actions = queryActionHelper.amplifyQueryActions(rawActions, range(selectedMinTurnIdx, maxTurn, queryLength));
const result = await reserveCommandDirect(actions);
if (releaseSelect) {
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
}
return result;
}
async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
const reqTurnList = getSelectedTurnList().sort((a, b) => (a - b));
const reqTurnList = queryActionHelper.getSelectedTurnList();
const selectedMinTurnIdx = reqTurnList[0];
const selectedMaxTurnIdx = reqTurnList[reqTurnList.length - 1];
const queryLength = selectedMaxTurnIdx - selectedMinTurnIdx + 1;
@@ -917,13 +775,13 @@ async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
const result = await reserveCommandDirect(actions);
if (releaseSelect) {
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
}
return result;
}
async function pushEmptyCommand(releaseSelect = true): Promise<boolean> {
const reqTurnList = getSelectedTurnList().sort((a, b) => (a - b));
const reqTurnList = queryActionHelper.getSelectedTurnList();
const selectedMinTurnIdx = reqTurnList[0];
const selectedMaxTurnIdx = reqTurnList[reqTurnList.length - 1];
const queryLength = selectedMaxTurnIdx - selectedMinTurnIdx + 1;
@@ -960,26 +818,34 @@ async function pushEmptyCommand(releaseSelect = true): Promise<boolean> {
const result = await reserveCommandDirect(actions);
if (releaseSelect) {
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
}
return result;
}
function setStoredActions() {
const actions = refineQueryActions();
const turnBrief: string[] = [];
const actions = queryActionHelper.extractQueryActions();
const turnBrief = new Map<number, string>();
for (const [subTurnList, action] of actions) {
const actionName = action.action.split('_');
const actionShortName = actionName.length == 1 ? actionName[0] : actionName[1];
turnBrief.push(repeat(actionShortName[0], subTurnList.length));
for (const turnIdx of subTurnList) {
turnBrief.set(turnIdx, actionShortName[0]);
}
}
const nickName = trim(prompt('선택한 턴들의 별명을 지어주세요', turnBrief.join()) ?? '');
const turnBriefStr = Array.from(turnBrief.entries())
.sort(([turnA], [turnB]) => turnA - turnB)
.map(([, action]) => action)
.join('');
const nickName = trim(prompt('선택한 턴들의 별명을 지어주세요', turnBriefStr) ?? '');
if (nickName == '') {
return;
}
storedActionsHelper.setStoredActions(nickName, actions);
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
}
function deleteStoredActions(actionKey: string) {
@@ -987,10 +853,10 @@ function deleteStoredActions(actionKey: string) {
}
async function useStoredAction(rawActions: [number[], TurnObj][]) {
const reqTurnList = getSelectedTurnList().sort((a, b) => (a - b));
const actions = amplifyQueryActions(rawActions, reqTurnList)
const reqTurnList = queryActionHelper.getSelectedTurnList();
const actions = queryActionHelper.amplifyQueryActions(rawActions, reqTurnList)
const result = await reserveCommandDirect(actions);
releaseSelectedTurnList();
queryActionHelper.releaseSelectedTurnList();
return result;
}
+173
View File
@@ -0,0 +1,173 @@
import type { TurnObj } from "@/defs";
import { clone, isString, range } from "lodash";
import { ref, type Ref } from "vue";
import { unwrap } from "./unwrap";
type TurnObjWithTime = TurnObj & {
time: string;
year?: number;
month?: number;
tooltip?: string;
style?: Record<string, unknown>;
};
export const getEmptyTurn = (maxTurn: number): TurnObjWithTime[] => Array.from<TurnObjWithTime>({
length: maxTurn,
}).fill({
arg: {},
brief: "",
action: "",
year: undefined,
month: undefined,
time: "",
});
export class QueryActionHelper {
public readonly reservedCommandList: Ref<TurnObjWithTime[]>;
public readonly selectedTurnList: Ref<Set<number>>;
public readonly prevSelectedTurnList: Ref<Set<number>>;
constructor(
protected maxTurn: number,
) {
this.reservedCommandList = ref(getEmptyTurn(maxTurn));
this.selectedTurnList = ref(new Set());
this.prevSelectedTurnList = ref(new Set([0]));
}
toggleTurn(...reqTurnList: number[] | string[]) {
for (let turnIdx of reqTurnList) {
if (isString(turnIdx)) {
turnIdx = parseInt(turnIdx);
}
if (this.selectedTurnList.value.has(turnIdx)) {
this.selectedTurnList.value.delete(turnIdx);
} else {
this.selectedTurnList.value.add(turnIdx);
}
}
}
selectTurn(...reqTurnList: number[] | string[]) {
this.selectedTurnList.value.clear();
for (const turnIdx of reqTurnList) {
if (isString(turnIdx)) {
this.selectedTurnList.value.add(parseInt(turnIdx));
} else {
this.selectedTurnList.value.add(turnIdx);
}
}
}
selectStep(begin: number, step: number) {
this.selectedTurnList.value.clear();
for (const idx of range(0, this.maxTurn)) {
if ((idx - begin) % step == 0) {
this.selectedTurnList.value.add(idx);
}
}
}
selectAll() {
for (let i = 0; i < this.maxTurn; i++) {
this.selectedTurnList.value.add(i);
}
}
getSelectedTurnList(useSort = true): number[] {
let result: number[];
if (this.selectedTurnList.value.size) {
result = Array.from(this.selectedTurnList.value);
}
else if (this.prevSelectedTurnList.value.size) {
result = Array.from(this.prevSelectedTurnList.value);
}
else {
return [0];
}
if (useSort) {
return result.sort((a, b) => a - b);
}
return result;
}
releaseSelectedTurnList() {
if (this.selectedTurnList.value.size > 0) {
this.prevSelectedTurnList.value.clear();
for (const v of this.selectedTurnList.value) {
this.prevSelectedTurnList.value.add(v);
}
this.selectedTurnList.value.clear();
}
}
extractQueryActions(): [number[], TurnObj][] {
const reqTurnList = this.getSelectedTurnList();
const selectedMinTurnIdx = unwrap(Math.min(...reqTurnList));
const buffer = new Map<string, [number[], TurnObj]>();
for (const rawTurnIdx of reqTurnList) {
const turnIdx = rawTurnIdx - selectedMinTurnIdx;
const rawAction = this.reservedCommandList.value[rawTurnIdx]
const actionStr = JSON.stringify([rawAction.action, rawAction.arg]);
if (buffer.has(actionStr)) {
const items = unwrap(buffer.get(actionStr));
items[0].push(turnIdx);
}
else {
buffer.set(actionStr, [[turnIdx], {
action: rawAction.action,
arg: clone(rawAction.arg),
brief: rawAction.brief
}])
}
}
return Array.from(buffer.values());
}
amplifyQueryActions(rawActions: [number[], TurnObj][], reqTurnList: number[]): [number[], TurnObj][] {
if (reqTurnList.length < 1) {
return [];
}
let minQueryIdx = this.maxTurn;
let maxQueryIdx = 0;
for (const [turnList] of rawActions) {
for (const turnIdx of turnList) {
minQueryIdx = Math.min(minQueryIdx, turnIdx);
maxQueryIdx = Math.max(maxQueryIdx, turnIdx);
}
}
const queryLength = maxQueryIdx - minQueryIdx + 1;
const queryTurnList: number[] = [reqTurnList[0]];
for (const reqTurnIdx of reqTurnList) {
const last = queryTurnList[queryTurnList.length - 1];
if (reqTurnIdx < last + queryLength) {
continue;
}
queryTurnList.push(reqTurnIdx);
}
const actions: [number[], TurnObj][] = [];
for (const [baseTurnList, action] of rawActions) {
const subTurnList: number[] = [];
for (const baseTurnIdx of baseTurnList) {
for (const queryTurnIdx of queryTurnList) {
const targetTurn = baseTurnIdx + queryTurnIdx;
if (targetTurn >= this.maxTurn) {
continue;
}
subTurnList.push(baseTurnIdx + queryTurnIdx);
}
}
if (subTurnList.length == 0) {
continue;
}
actions.push([subTurnList, action]);
}
return actions;
}
}