wip: MessagePanel

This commit is contained in:
2023-03-09 02:20:15 +09:00
parent 951143120b
commit c4f8e551a5
4 changed files with 245 additions and 9 deletions
+9 -7
View File
@@ -27,7 +27,7 @@
<div class="subOnlineUserCnt">전체 접속자 : {{globalInfo.onlineUserCnt.toLocaleString()}}</div>
<div class="subAPILimit">턴당 갱신횟수: {{globalInfo.apiLimit.toLocaleString()}}</div>
<div class="subGeneralCnt">
등록 장수: 유저
등록 장수: 유저
{{globalInfo.genCount.toLocaleString()}} / {{globalInfo.generalCntLimit.toLocaleString()}}
+ NPC TODO:NPC Cnt
</div>
@@ -86,11 +86,7 @@
</template>
</div>
<div class="commonToolbar">툴바?</div>
<div class="MessageInputForm">메시지 입력</div>
<div class="PublicTalk">전체 메시지</div>
<div class="NationalTalk">국가 메시지</div>
<div class="PrivateTalk">개인 메시지</div>
<div class="DiplomacyTalk">외교 메시지</div>
<MessagePanel v-if="generalInfo" :generalID="generalInfo.no" :generalName="generalInfo.name" :nationID="generalInfo.nation" :permissionLevel="generalInfo.permission" ref="msgPanel" />
<div class="commonToolbar">툴바?</div>
</main>
</BContainer>
@@ -126,6 +122,7 @@ import { delay } from "./util/delay";
import CityBasicCard from "./components/CityBasicCard.vue";
import NationBasicCard from "./components/NationBasicCard.vue";
import GeneralBasicCard from "./components/GeneralBasicCard.vue";
import MessagePanel from "./components/MessagePanel.vue";
import type { GeneralListItemP1 } from "./defs/API/Nation";
import type { NationStaticItem } from "./defs";
import { formatTime } from "./util/formatTime";
@@ -150,7 +147,12 @@ const storeP = getGameConstStore().then((store) => {
let responseLock = false;
const msgPanel = ref<InstanceType<typeof MessagePanel>>();
async function tryRefresh() {
msgPanel.value?.tryRefresh();
if (responseLock) {
return;
}
@@ -293,7 +295,7 @@ watch(refreshCounter, async () => {
const frontInfo = ref<GetFrontInfoResponse>();
const globalInfo = ref<GetFrontInfoResponse['global']>({} as GetFrontInfoResponse['global'])
const generalInfo = ref<GeneralListItemP1>();
const generalInfo = ref<GetFrontInfoResponse['general']>();
const nationStaticInfo = ref<NationStaticItem>();
let generalInfoLock = false;
+236
View File
@@ -0,0 +1,236 @@
<template>
<div class="MessageInputForm">메시지 입력</div>
<div class="PublicTalk">
<div>전체 메시지</div>
<template v-if="messagePublic.length == 0">
<div>메시지가 없습니다.</div>
</template>
<MessagePlate
v-for="msg of messagePublic"
:key="msg.id"
:modelValue="msg"
:generalID="generalID"
:generalName="generalName"
:nationID="nationID"
:permissionLevel="permissionLevel"
></MessagePlate>
</div>
<div class="NationalTalk">
<div>국가 메시지</div>
<template v-if="messageNational.length == 0">
<div>메시지가 없습니다.</div>
</template>
</div>
<div class="PrivateTalk">
<div>개인 메시지</div>
<template v-if="messagePrivate.length == 0">
<div>메시지가 없습니다.</div>
</template>
</div>
<div class="DiplomacyTalk">
<div>외교 메시지</div>
<template v-if="messageDiplomacy.length == 0">
<div>메시지가 없습니다.</div>
</template>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, toRef, type Ref } from "vue";
import { delay } from "@/util/delay";
import { SammoAPI } from "@/SammoAPI";
import { isString } from "lodash";
import type { MsgItem, MsgResponse, MsgType } from "@/defs/API/Message";
import MessagePlate from "@/components/MessagePlate.vue";
const emit = defineEmits<{
(event: "request-refresh"): void;
}>();
const props = defineProps<{
generalID: number;
generalName: string;
nationID: number;
permissionLevel: number;
}>();
const generalID = toRef(props, "generalID");
const generalName = toRef(props, "generalName");
const nationID = toRef(props, "nationID");
const permissionLevel = toRef(props, "permissionLevel");
onMounted(() => {
console.log("MessagePanel mounted");
console.log(props);
});
const lastSequence = ref(-1);
const initRefreshLimit = 5;
let refreshLimit = initRefreshLimit;
const refreshP: Set<Promise<boolean>> = new Set();
let lastRefreshDone = 0;
let refreshTimer: null | number = null;
const messageStorage = new Map<number, MsgItem>();
const messagePublic = ref<MsgItem[]>([]);
const messageNational = ref<MsgItem[]>([]);
const messagePrivate = ref<MsgItem[]>([]);
const messageDiplomacy = ref<MsgItem[]>([]);
const messageIndexedList: Record<MsgType, Ref<MsgItem[]>> = {
public: messagePublic,
national: messageNational,
private: messagePrivate,
diplomacy: messageDiplomacy,
};
function updateMsgResponse(response: MsgResponse) {
if (!response.keepRecent) {
messageStorage.clear();
for (const msgList of Object.values(messageIndexedList)) {
msgList.value.length = 0;
}
}
if(response.generalName != generalName.value){
emit("request-refresh");
return;
}
if(response.nationID != nationID.value){
emit("request-refresh");
return;
}
lastSequence.value = Math.max(lastSequence.value, response.sequence);
for (const msgType of Object.keys(messageIndexedList) as (keyof typeof messageIndexedList)[]) {
const msgList = messageIndexedList[msgType];
const newMsgList = response[msgType];
console.log(msgType, msgList, newMsgList);
if (newMsgList.length == 0) {
continue;
}
//순서가 어떤 순서인지 모르니, 여기서 내림차순으로 맞춘다.
newMsgList.sort((a, b) => b.id - a.id);
if (msgList.value.length == 0) {
msgList.value = newMsgList;
continue;
}
//head test
const filteredMsgList: MsgItem[] = [];
for (const msg of newMsgList) {
const oldMsg = messageStorage.get(msg.id);
if (oldMsg !== undefined) {
continue;
}
messageStorage.set(msg.id, msg);
filteredMsgList.push(msg);
}
if (filteredMsgList.length == 0) {
continue;
}
const msgHeadID = msgList.value[0].id;
const newMsgTailID = filteredMsgList[filteredMsgList.length - 1].id;
if (newMsgTailID > msgHeadID) {
msgList.value = [...filteredMsgList, ...msgList.value];
continue;
}
const msgTailID = msgList.value[msgList.value.length - 1].id;
const newMsgHeadID = filteredMsgList[0].id;
if (msgTailID > newMsgHeadID) {
msgList.value.push(...filteredMsgList);
continue;
}
//중간에 삽입되는 경우는 에러이다.
console.error("중간 삽입 있음", msgType, newMsgList);
}
}
function beginRefreshTimer() {
if (refreshTimer) {
clearInterval(refreshTimer);
refreshTimer = null;
}
refreshTimer = window.setInterval(function () {
const now = Date.now();
if (lastRefreshDone + 5000 < now) {
//만약 서버 응답이 없다면?
if (refreshP.size > refreshLimit && refreshTimer) {
clearInterval(refreshTimer);
refreshTimer = null;
alert("서버 응답이 없습니다. 새로고침을 해주세요.");
return;
}
refreshP.add(tryRefresh());
}
}, 2500);
}
async function tryRefresh() {
if (refreshP.size > 0) {
await Promise.race([...refreshP, delay(500)]);
}
const waiterP = (async () => {
let response: MsgResponse | undefined = undefined;
try {
response = await SammoAPI.Message.GetRecentMessage({
sequence: lastSequence.value,
});
const now = Date.now();
if (lastRefreshDone < now) {
lastRefreshDone = now;
}
} catch (e) {
if (isString(e)) {
alert(e);
}
console.error(e);
}
if(!response){
return false;
}
updateMsgResponse(response);
return true;
})();
void waiterP.then((result) => {
if (!result) {
refreshLimit--;
} else {
refreshLimit = initRefreshLimit;
}
refreshP.delete(waiterP);
});
refreshP.add(waiterP);
return waiterP;
}
defineExpose({
tryRefresh,
});
onMounted(async () => {
beginRefreshTimer();
await tryRefresh();
});
</script>
-1
View File
@@ -143,7 +143,6 @@ watch(
icon: defaultIcon,
};
},
{ immediate: true }
);
const deletableTimer: Ref<number | undefined> = ref();
-1
View File
@@ -7,7 +7,6 @@ include "func.php";
//로그인 검사
Session::requireGameLogin()->setReadOnly();
//턴 실행 대기 및 갱신 증가는 API에서
//TODO: 내 정보도 API에서, 사망 여부도