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) {
|
rpc StartGameExport(StartGameExportReq) returns (GameExportRsp) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
post: "/api/games/{ID}/export"
|
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) {
|
rpc GetGameExport(GetGameExportReq) returns (GameExportRsp) {
|
||||||
option (google.api.http) = {get: "/api/games/{ID}/export"};
|
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).
|
// GET /api/games/{ID}/export/download (not an HttpBody RPC).
|
||||||
message GameExport {
|
message GameExport {
|
||||||
string ID = 1;
|
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;
|
string packageID = 3;
|
||||||
int64 size = 4;
|
int64 size = 4;
|
||||||
string sha256 = 5;
|
string sha256 = 5;
|
||||||
@@ -1736,6 +1745,13 @@ message StartGameExportReq { int32 ID = 1; }
|
|||||||
|
|
||||||
message GetGameExportReq { 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; }
|
message GameExportRsp { GameExport export = 1; }
|
||||||
|
|
||||||
enum ScreenMode {
|
enum ScreenMode {
|
||||||
|
|||||||
@@ -588,6 +588,14 @@ export type GetGameExportReq = {
|
|||||||
ID: number | undefined;
|
ID: number | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ResetGameLocalModeReq = {
|
||||||
|
ID: number | undefined;
|
||||||
|
exportID: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ResetGameLocalModeRsp = {
|
||||||
|
};
|
||||||
|
|
||||||
export type GameExportRsp = {
|
export type GameExportRsp = {
|
||||||
export: GameExport | undefined;
|
export: GameExport | undefined;
|
||||||
};
|
};
|
||||||
@@ -1419,7 +1427,10 @@ export interface EveningDetectiveServer {
|
|||||||
// Dedicated HTTP handlers stream ZIPs; gRPC retains its message size limit.
|
// Dedicated HTTP handlers stream ZIPs; gRPC retains its message size limit.
|
||||||
DownloadGameResultsPackage(request: GameResultsPackageReq): Promise<googleapi_HttpBody>;
|
DownloadGameResultsPackage(request: GameResultsPackageReq): Promise<googleapi_HttpBody>;
|
||||||
ApplyGameResultsPackage(request: ApplyGameResultsPackageReq): Promise<GameResultsPackageRsp>;
|
ApplyGameResultsPackage(request: ApplyGameResultsPackageReq): Promise<GameResultsPackageRsp>;
|
||||||
|
// Each request creates a fresh setup package, replacing any unfinished preparation.
|
||||||
StartGameExport(request: StartGameExportReq): Promise<GameExportRsp>;
|
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>;
|
GetGameExport(request: GetGameExportReq): Promise<GameExportRsp>;
|
||||||
IssueGameTickets(request: IssueGameTicketsReq): Promise<IssueGameTicketsRsp>;
|
IssueGameTickets(request: IssueGameTicketsReq): Promise<IssueGameTicketsRsp>;
|
||||||
GetTicket(request: GetTicketReq): Promise<GetTicketRsp>;
|
GetTicket(request: GetTicketReq): Promise<GetTicketRsp>;
|
||||||
@@ -2682,6 +2693,26 @@ export function createEveningDetectiveServerClient(
|
|||||||
method: "StartGameExport",
|
method: "StartGameExport",
|
||||||
}) as Promise<GameExportRsp>;
|
}) 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
|
GetGameExport(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
if (!request.ID) {
|
if (!request.ID) {
|
||||||
throw new Error("missing required field request.ID");
|
throw new Error("missing required field request.ID");
|
||||||
|
|||||||
+94
-21
@@ -202,6 +202,14 @@ const canExport = computed(
|
|||||||
)
|
)
|
||||||
const isStartingExport = ref(false)
|
const isStartingExport = ref(false)
|
||||||
const isDownloadingPackage = 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(
|
const canDownloadResults = computed(
|
||||||
() =>
|
() =>
|
||||||
uiStore.environment === 'venue' &&
|
uiStore.environment === 'venue' &&
|
||||||
@@ -649,14 +657,7 @@ async function toTeamStory(id: string, password: string, newTab: boolean = false
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function preparePackage() {
|
async function preparePackage() {
|
||||||
if (
|
if (!canExport.value || isStartingExport.value || isResettingLocalMode.value) return
|
||||||
!canExport.value ||
|
|
||||||
isStartingExport.value ||
|
|
||||||
game.value.export?.state === 'preparing' ||
|
|
||||||
game.value.export?.state === 'ready' ||
|
|
||||||
game.value.status !== 'draft'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
isStartingExport.value = true
|
isStartingExport.value = true
|
||||||
try {
|
try {
|
||||||
const res = await client.StartGameExport({ ID: game.value.ID })
|
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() {
|
async function downloadPackage() {
|
||||||
if (!canExport.value || isDownloadingPackage.value || game.value.export?.state !== 'ready') return
|
if (!canExport.value || isDownloadingPackage.value || game.value.export?.state !== 'ready') return
|
||||||
isDownloadingPackage.value = true
|
isDownloadingPackage.value = true
|
||||||
@@ -1264,7 +1289,9 @@ useSimplePolling(() => {
|
|||||||
<n-space vertical>
|
<n-space vertical>
|
||||||
<p>
|
<p>
|
||||||
Пакет содержит снимок на момент подготовки. Последующие онлайн-изменения в него не
|
Пакет содержит снимок на момент подготовки. Последующие онлайн-изменения в него не
|
||||||
попадут. Готовность пакета не подтверждает импорт на площадку.
|
попадут. На площадке игра начнётся с нуля: ходы, ответы и баллы в пакет не входят.
|
||||||
|
Статус и прогресс игры на сервере сохраняются. Готовность пакета не подтверждает
|
||||||
|
импорт на площадку.
|
||||||
</p>
|
</p>
|
||||||
<n-alert v-if="game.export?.state === 'preparing'" type="info" role="status"
|
<n-alert v-if="game.export?.state === 'preparing'" type="info" role="status"
|
||||||
>Подготовка пакета…</n-alert
|
>Подготовка пакета…</n-alert
|
||||||
@@ -1283,21 +1310,25 @@ useSimplePolling(() => {
|
|||||||
>Скачать пакет</n-button
|
>Скачать пакет</n-button
|
||||||
>
|
>
|
||||||
<n-button
|
<n-button
|
||||||
v-else
|
:loading="isStartingExport"
|
||||||
:loading="isStartingExport || game.export?.state === 'preparing'"
|
:disabled="isStartingExport || isResettingLocalMode"
|
||||||
:disabled="
|
|
||||||
isStartingExport ||
|
|
||||||
game.export?.state === 'preparing' ||
|
|
||||||
game.status !== 'draft'
|
|
||||||
"
|
|
||||||
@click="preparePackage"
|
@click="preparePackage"
|
||||||
>{{
|
|
||||||
game.export?.state === 'failed' ? 'Повторить подготовку' : 'Подготовить пакет'
|
|
||||||
}}</n-button
|
|
||||||
>
|
>
|
||||||
<p v-if="game.status && game.status !== 'draft'">
|
Подготовить новый пакет
|
||||||
Подготовка доступна до старта игры.
|
</n-button>
|
||||||
|
<p>
|
||||||
|
Новый пакет заменяет текущую подготовку. Результаты ранее скачанных пакетов можно
|
||||||
|
загрузить позже.
|
||||||
</p>
|
</p>
|
||||||
|
<n-button
|
||||||
|
v-if="canResetLocalMode"
|
||||||
|
type="warning"
|
||||||
|
:loading="isResettingLocalMode"
|
||||||
|
:disabled="isStartingExport || isResettingLocalMode"
|
||||||
|
@click="openResetLocalMode"
|
||||||
|
>
|
||||||
|
Сбросить локальный режим
|
||||||
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</UiBlock>
|
</UiBlock>
|
||||||
<UiBlock>
|
<UiBlock>
|
||||||
@@ -1559,6 +1590,43 @@ useSimplePolling(() => {
|
|||||||
</div>
|
</div>
|
||||||
</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" />
|
<TicketCheckModal v-if="canManageGame" v-model:show="showTicketCheckModal" />
|
||||||
<TeamReceiptModal
|
<TeamReceiptModal
|
||||||
v-if="canManageGame && uiStore.environment === 'venue'"
|
v-if="canManageGame && uiStore.environment === 'venue'"
|
||||||
@@ -1805,6 +1873,11 @@ useSimplePolling(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.reset-local-mode-modal {
|
||||||
|
max-width: calc(100vw - 32px);
|
||||||
|
width: 560px;
|
||||||
|
}
|
||||||
|
|
||||||
.reviews-summary {
|
.reviews-summary {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user