feat: 개인 메시지, 외교 메시지 수신 시 toast 알림, 모두 읽음 버튼 추가

This commit is contained in:
2023-03-09 02:20:26 +09:00
parent e404f3145c
commit 1d477a285a
+136 -7
View File
@@ -27,7 +27,7 @@
</BFormSelect> </BFormSelect>
</div> </div>
<div id="msg_input-col" class="col-12 col-md-8 d-grid"> <div id="msg_input-col" class="col-12 col-md-8 d-grid">
<input v-model="newMessageText" type="text" maxlength="99" @keydown.enter="sendMessage" class="form-control" /> <input v-model="newMessageText" type="text" maxlength="99" class="form-control" @keydown.enter="sendMessage" />
</div> </div>
<div id="msg_submit-col" class="col-6 col-md-2 d-grid"> <div id="msg_submit-col" class="col-6 col-md-2 d-grid">
<BButton variant="primary" @click="sendMessage">서신전달&amp;갱신</BButton> <BButton variant="primary" @click="sendMessage">서신전달&amp;갱신</BButton>
@@ -83,7 +83,18 @@
</div> </div>
<div class="PrivateTalk"> <div class="PrivateTalk">
<div class="stickyAnchor"></div> <div class="stickyAnchor"></div>
<div class="BoardHeader bg0">개인 메시지</div> <div class="BoardHeader bg0 d-flex">
<div class="flex-grow-1 align-self-center">개인 메시지</div>
<div>
<BButton
v-if="messagePrivate.length > 0 && latestPrivateMsgToastInfo[2] > latestPrivateMsgToastInfo[1]"
size="sm"
variant="secondary"
@click="readLatestMsg('private')"
>모두 읽음</BButton
>
</div>
</div>
<template v-if="messagePrivate.length == 0"> <template v-if="messagePrivate.length == 0">
<div>메시지가 없습니다.</div> <div>메시지가 없습니다.</div>
</template> </template>
@@ -107,7 +118,18 @@
</div> </div>
<div class="DiplomacyTalk"> <div class="DiplomacyTalk">
<div class="stickyAnchor"></div> <div class="stickyAnchor"></div>
<div class="BoardHeader bg0">외교 메시지</div> <div class="BoardHeader bg0 d-flex">
<div class="flex-grow-1 align-self-center">외교 메시지</div>
<div>
<BButton
v-if="messageDiplomacy.length > 0 && latestDiplomacyMsgToastInfo[2] > latestDiplomacyMsgToastInfo[1]"
ize="sm"
variant="secondary"
@click="readLatestMsg('diplomacy')"
>모두 읽음</BButton
>
</div>
</div>
<template v-if="messageDiplomacy.length == 0"> <template v-if="messageDiplomacy.length == 0">
<div>메시지가 없습니다.</div> <div>메시지가 없습니다.</div>
</template> </template>
@@ -132,17 +154,27 @@
</div> </div>
</template> </template>
<script lang="ts">
declare const staticValues: {
serverName: string;
serverNick: string;
serverID: string;
mapName: string;
unitSet: string;
};
</script>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, toRef, watch, type Ref } from "vue"; import { onMounted, ref, toRef, watch, type Ref } from "vue";
import { delay } from "@/util/delay"; import { delay } from "@/util/delay";
import { SammoAPI } from "@/SammoAPI"; import { SammoAPI } from "@/SammoAPI";
import { isString } from "lodash"; import { isString } from "lodash";
import type { MabilboxListResponse, MailboxItem, MsgItem, MsgResponse, MsgType } from "@/defs/API/Message"; import type { MabilboxListResponse, MsgItem, MsgResponse, MsgType } from "@/defs/API/Message";
import MessagePlate from "@/components/MessagePlate.vue"; import MessagePlate from "@/components/MessagePlate.vue";
import { useToast, BFormSelect, BFormSelectOptionGroup } from "bootstrap-vue-3"; import { useToast, BFormSelect } from "bootstrap-vue-3";
import { unwrap } from "@/util/unwrap"; import { unwrap } from "@/util/unwrap";
import { isBrightColor } from "@/util/isBrightColor"; import { isBrightColor } from "@/util/isBrightColor";
const serverID = staticValues.serverID;
const toasts = unwrap(useToast()); const toasts = unwrap(useToast());
const emit = defineEmits<{ const emit = defineEmits<{
@@ -187,12 +219,107 @@ const messageIndexedList: Record<MsgType, Ref<MsgItem[]>> = {
diplomacy: messageDiplomacy, diplomacy: messageDiplomacy,
}; };
function generateLatestMsgState(msgType: MsgType) {
const storageKey = `state.${serverID}.latestReadMsg.${msgType}`;
const latestReadMsgID = parseInt(localStorage.getItem(storageKey) ?? "0");
const obj = ref<[string | undefined, number, number]>([undefined, latestReadMsgID, latestReadMsgID]);
watch(obj, ([, newMsgID], [, oldMsgID]) => {
if (newMsgID == oldMsgID) {
return;
}
localStorage.setItem(storageKey, newMsgID.toString());
});
return obj;
}
const latestPrivateMsgToastInfo = generateLatestMsgState("private");
const latestDiplomacyMsgToastInfo = generateLatestMsgState("diplomacy");
function readLatestMsg(msgType: MsgType) {
const targetMap = {
private: latestPrivateMsgToastInfo,
diplomacy: latestDiplomacyMsgToastInfo,
public: undefined,
national: undefined,
};
const target = targetMap[msgType];
if (!target) {
return;
}
const [toastID, , lastestReceivedID] = target.value;
if (toastID) {
toasts.remove(toastID);
}
target.value = [undefined, lastestReceivedID, lastestReceivedID];
}
function _updateLatestMsg(msg: MsgItem) {
const msgType = msg.msgType;
//TODO: 메시지함으로 바로 이동하는 기능이 나중에 필요할 것
if (msgType == "private") {
const [toastID, latestMsgID] = latestPrivateMsgToastInfo.value;
if (msg.id <= latestMsgID) {
return;
}
if (toastID) {
toasts.remove(toastID);
}
const newToastID = toasts.show(
{
title: "새로운 개인 메시지",
body: "새로운 개인 메시지가 도착했습니다.",
},
{
delay: 1000 * 60 * 10,
variant: 'warning'
}
).options.id;
latestPrivateMsgToastInfo.value = [newToastID, latestMsgID, msg.id];
return;
}
if (msgType == "diplomacy") {
const [toastID, latestMsgID] = latestDiplomacyMsgToastInfo.value;
if (msg.id <= latestMsgID) {
return;
}
if (toastID) {
toasts.remove(toastID);
}
const newToastID = toasts.show(
{
title: "새로운 외교 메시지",
body: "새로운 외교 메시지가 도착했습니다.",
},
{
delay: 1000 * 60 * 10,
variant: 'warning'
}
).options.id;
latestDiplomacyMsgToastInfo.value = [newToastID, latestMsgID, msg.id];
return;
}
}
function updateLatestMsg(msg: MsgItem[]) {
for (const msgItem of msg) {
if (msgItem.src.id == generalID.value) continue;
_updateLatestMsg(msgItem);
break;
}
}
function processMsg(msg: MsgItem) { function processMsg(msg: MsgItem) {
if (msg.option.delete) { if (msg.option.delete) {
(() => { (() => {
const targetID = msg.option.delete; const targetID = msg.option.delete;
const targetMsg = messageStorage.get(targetID); const targetMsg = messageStorage.get(targetID);
if(!targetMsg){ if (!targetMsg) {
return; return;
} }
targetMsg.option.invalid = true; targetMsg.option.invalid = true;
@@ -232,6 +359,7 @@ function updateMsgResponse(response: MsgResponse) {
if (msgList.value.length == 0) { if (msgList.value.length == 0) {
msgList.value = newMsgList; msgList.value = newMsgList;
updateLatestMsg(newMsgList);
continue; continue;
} }
@@ -256,6 +384,7 @@ function updateMsgResponse(response: MsgResponse) {
const newMsgTailID = filteredMsgList[filteredMsgList.length - 1].id; const newMsgTailID = filteredMsgList[filteredMsgList.length - 1].id;
if (newMsgTailID > msgHeadID) { if (newMsgTailID > msgHeadID) {
msgList.value = [...filteredMsgList, ...msgList.value]; msgList.value = [...filteredMsgList, ...msgList.value];
updateLatestMsg(filteredMsgList);
continue; continue;
} }
@@ -592,7 +721,7 @@ onMounted(async () => {
display: none; display: none;
} }
.MessageList{ .MessageList {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
height: 650px; height: 650px;