generated from VLADIMIR/template_frontend
up
This commit is contained in:
+17
-1
@@ -675,6 +675,7 @@ service EveningDetectiveServer {
|
||||
};
|
||||
}
|
||||
|
||||
// Each request creates a fresh setup package, replacing any unfinished preparation.
|
||||
rpc StartGameExport(StartGameExportReq) returns (GameExportRsp) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/games/{ID}/export"
|
||||
@@ -682,6 +683,14 @@ service EveningDetectiveServer {
|
||||
};
|
||||
}
|
||||
|
||||
// Release the transfer without discarding ready packages or their future results.
|
||||
rpc ResetGameLocalMode(ResetGameLocalModeReq) returns (ResetGameLocalModeRsp) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/games/{ID}/local-mode/reset"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc GetGameExport(GetGameExportReq) returns (GameExportRsp) {
|
||||
option (google.api.http) = {get: "/api/games/{ID}/export"};
|
||||
}
|
||||
@@ -1719,7 +1728,7 @@ message Game {
|
||||
// GET /api/games/{ID}/export/download (not an HttpBody RPC).
|
||||
message GameExport {
|
||||
string ID = 1;
|
||||
string state = 2; // preparing, ready or failed; preparing/ready lock game writes.
|
||||
string state = 2; // preparing, ready or failed; ready indicates a completed package.
|
||||
string packageID = 3;
|
||||
int64 size = 4;
|
||||
string sha256 = 5;
|
||||
@@ -1736,6 +1745,13 @@ message StartGameExportReq { int32 ID = 1; }
|
||||
|
||||
message GetGameExportReq { int32 ID = 1; }
|
||||
|
||||
message ResetGameLocalModeReq {
|
||||
int32 ID = 1;
|
||||
string exportID = 2; // Expected latest export, protecting against stale confirmations.
|
||||
}
|
||||
|
||||
message ResetGameLocalModeRsp {}
|
||||
|
||||
message GameExportRsp { GameExport export = 1; }
|
||||
|
||||
enum ScreenMode {
|
||||
|
||||
@@ -588,6 +588,14 @@ export type GetGameExportReq = {
|
||||
ID: number | undefined;
|
||||
};
|
||||
|
||||
export type ResetGameLocalModeReq = {
|
||||
ID: number | undefined;
|
||||
exportID: string | undefined;
|
||||
};
|
||||
|
||||
export type ResetGameLocalModeRsp = {
|
||||
};
|
||||
|
||||
export type GameExportRsp = {
|
||||
export: GameExport | undefined;
|
||||
};
|
||||
@@ -1419,7 +1427,10 @@ export interface EveningDetectiveServer {
|
||||
// Dedicated HTTP handlers stream ZIPs; gRPC retains its message size limit.
|
||||
DownloadGameResultsPackage(request: GameResultsPackageReq): Promise<googleapi_HttpBody>;
|
||||
ApplyGameResultsPackage(request: ApplyGameResultsPackageReq): Promise<GameResultsPackageRsp>;
|
||||
// Each request creates a fresh setup package, replacing any unfinished preparation.
|
||||
StartGameExport(request: StartGameExportReq): Promise<GameExportRsp>;
|
||||
// Release the transfer without discarding ready packages or their future results.
|
||||
ResetGameLocalMode(request: ResetGameLocalModeReq): Promise<ResetGameLocalModeRsp>;
|
||||
GetGameExport(request: GetGameExportReq): Promise<GameExportRsp>;
|
||||
IssueGameTickets(request: IssueGameTicketsReq): Promise<IssueGameTicketsRsp>;
|
||||
GetTicket(request: GetTicketReq): Promise<GetTicketRsp>;
|
||||
@@ -2682,6 +2693,26 @@ export function createEveningDetectiveServerClient(
|
||||
method: "StartGameExport",
|
||||
}) as Promise<GameExportRsp>;
|
||||
},
|
||||
ResetGameLocalMode(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
if (!request.ID) {
|
||||
throw new Error("missing required field request.ID");
|
||||
}
|
||||
const path = `api/games/${request.ID}/local-mode/reset`; // eslint-disable-line quotes
|
||||
const body = JSON.stringify(request);
|
||||
const queryParams: string[] = [];
|
||||
let uri = path;
|
||||
if (queryParams.length > 0) {
|
||||
uri += `?${queryParams.join("&")}`
|
||||
}
|
||||
return handler({
|
||||
path: uri,
|
||||
method: "POST",
|
||||
body,
|
||||
}, {
|
||||
service: "EveningDetectiveServer",
|
||||
method: "ResetGameLocalMode",
|
||||
}) as Promise<ResetGameLocalModeRsp>;
|
||||
},
|
||||
GetGameExport(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
if (!request.ID) {
|
||||
throw new Error("missing required field request.ID");
|
||||
|
||||
+94
-21
@@ -202,6 +202,14 @@ const canExport = computed(
|
||||
)
|
||||
const isStartingExport = ref(false)
|
||||
const isDownloadingPackage = ref(false)
|
||||
const canResetLocalMode = computed(
|
||||
() =>
|
||||
canExport.value &&
|
||||
(game.value.export?.state === 'preparing' || game.value.export?.state === 'ready'),
|
||||
)
|
||||
const isResettingLocalMode = ref(false)
|
||||
const isResetLocalModeModalOpen = ref(false)
|
||||
const resetLocalModeExportID = ref('')
|
||||
const canDownloadResults = computed(
|
||||
() =>
|
||||
uiStore.environment === 'venue' &&
|
||||
@@ -649,14 +657,7 @@ async function toTeamStory(id: string, password: string, newTab: boolean = false
|
||||
}
|
||||
|
||||
async function preparePackage() {
|
||||
if (
|
||||
!canExport.value ||
|
||||
isStartingExport.value ||
|
||||
game.value.export?.state === 'preparing' ||
|
||||
game.value.export?.state === 'ready' ||
|
||||
game.value.status !== 'draft'
|
||||
)
|
||||
return
|
||||
if (!canExport.value || isStartingExport.value || isResettingLocalMode.value) return
|
||||
isStartingExport.value = true
|
||||
try {
|
||||
const res = await client.StartGameExport({ ID: game.value.ID })
|
||||
@@ -668,6 +669,30 @@ async function preparePackage() {
|
||||
}
|
||||
}
|
||||
|
||||
function openResetLocalMode() {
|
||||
if (!canResetLocalMode.value || isStartingExport.value || isResettingLocalMode.value) return
|
||||
resetLocalModeExportID.value = game.value.export?.ID || ''
|
||||
isResetLocalModeModalOpen.value = true
|
||||
}
|
||||
|
||||
async function resetLocalMode() {
|
||||
if (!canExport.value || !resetLocalModeExportID.value || isResettingLocalMode.value) return
|
||||
isResettingLocalMode.value = true
|
||||
try {
|
||||
await client.ResetGameLocalMode({
|
||||
ID: game.value.ID,
|
||||
exportID: resetLocalModeExportID.value,
|
||||
})
|
||||
isResetLocalModeModalOpen.value = false
|
||||
await getGame(Number(game.value.ID))
|
||||
message.success('Локальный режим сброшен')
|
||||
} catch (err) {
|
||||
message.error(err instanceof Error ? err.message : 'Не удалось сбросить локальный режим')
|
||||
} finally {
|
||||
isResettingLocalMode.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadPackage() {
|
||||
if (!canExport.value || isDownloadingPackage.value || game.value.export?.state !== 'ready') return
|
||||
isDownloadingPackage.value = true
|
||||
@@ -1264,7 +1289,9 @@ useSimplePolling(() => {
|
||||
<n-space vertical>
|
||||
<p>
|
||||
Пакет содержит снимок на момент подготовки. Последующие онлайн-изменения в него не
|
||||
попадут. Готовность пакета не подтверждает импорт на площадку.
|
||||
попадут. На площадке игра начнётся с нуля: ходы, ответы и баллы в пакет не входят.
|
||||
Статус и прогресс игры на сервере сохраняются. Готовность пакета не подтверждает
|
||||
импорт на площадку.
|
||||
</p>
|
||||
<n-alert v-if="game.export?.state === 'preparing'" type="info" role="status"
|
||||
>Подготовка пакета…</n-alert
|
||||
@@ -1283,21 +1310,25 @@ useSimplePolling(() => {
|
||||
>Скачать пакет</n-button
|
||||
>
|
||||
<n-button
|
||||
v-else
|
||||
:loading="isStartingExport || game.export?.state === 'preparing'"
|
||||
:disabled="
|
||||
isStartingExport ||
|
||||
game.export?.state === 'preparing' ||
|
||||
game.status !== 'draft'
|
||||
"
|
||||
:loading="isStartingExport"
|
||||
:disabled="isStartingExport || isResettingLocalMode"
|
||||
@click="preparePackage"
|
||||
>{{
|
||||
game.export?.state === 'failed' ? 'Повторить подготовку' : 'Подготовить пакет'
|
||||
}}</n-button
|
||||
>
|
||||
<p v-if="game.status && game.status !== 'draft'">
|
||||
Подготовка доступна до старта игры.
|
||||
Подготовить новый пакет
|
||||
</n-button>
|
||||
<p>
|
||||
Новый пакет заменяет текущую подготовку. Результаты ранее скачанных пакетов можно
|
||||
загрузить позже.
|
||||
</p>
|
||||
<n-button
|
||||
v-if="canResetLocalMode"
|
||||
type="warning"
|
||||
:loading="isResettingLocalMode"
|
||||
:disabled="isStartingExport || isResettingLocalMode"
|
||||
@click="openResetLocalMode"
|
||||
>
|
||||
Сбросить локальный режим
|
||||
</n-button>
|
||||
</n-space>
|
||||
</UiBlock>
|
||||
<UiBlock>
|
||||
@@ -1559,6 +1590,43 @@ useSimplePolling(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-modal
|
||||
v-if="canExport"
|
||||
v-model:show="isResetLocalModeModalOpen"
|
||||
:mask-closable="!isResettingLocalMode"
|
||||
:close-on-esc="!isResettingLocalMode"
|
||||
>
|
||||
<n-card
|
||||
class="reset-local-mode-modal"
|
||||
:bordered="false"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="reset-local-mode-title"
|
||||
aria-describedby="reset-local-mode-description"
|
||||
>
|
||||
<template #header><span id="reset-local-mode-title">Сбросить локальный режим?</span></template>
|
||||
<p id="reset-local-mode-description">
|
||||
Игра вернётся в обычный режим на сервере. Данные игры сохранятся. Локальная площадка продолжит
|
||||
работать отдельно; её результаты можно будет загрузить позже.
|
||||
</p>
|
||||
<template #footer>
|
||||
<n-flex justify="end">
|
||||
<n-button :disabled="isResettingLocalMode" @click="isResetLocalModeModalOpen = false">
|
||||
Отмена
|
||||
</n-button>
|
||||
<n-button
|
||||
type="warning"
|
||||
:loading="isResettingLocalMode"
|
||||
:disabled="isResettingLocalMode"
|
||||
@click="resetLocalMode"
|
||||
>
|
||||
Вернуть в обычный режим
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
|
||||
<TicketCheckModal v-if="canManageGame" v-model:show="showTicketCheckModal" />
|
||||
<TeamReceiptModal
|
||||
v-if="canManageGame && uiStore.environment === 'venue'"
|
||||
@@ -1805,6 +1873,11 @@ useSimplePolling(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.reset-local-mode-modal {
|
||||
max-width: calc(100vw - 32px);
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
.reviews-summary {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user