feat: 개인 메시지, 외교 메시지 수신 시 toast 알림, 모두 읽음 버튼 추가
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
</BFormSelect>
|
||||
</div>
|
||||
<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 id="msg_submit-col" class="col-6 col-md-2 d-grid">
|
||||
<BButton variant="primary" @click="sendMessage">서신전달&갱신</BButton>
|
||||
@@ -83,7 +83,18 @@
|
||||
</div>
|
||||
<div class="PrivateTalk">
|
||||
<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">
|
||||
<div>메시지가 없습니다.</div>
|
||||
</template>
|
||||
@@ -107,7 +118,18 @@
|
||||
</div>
|
||||
<div class="DiplomacyTalk">
|
||||
<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">
|
||||
<div>메시지가 없습니다.</div>
|
||||
</template>
|
||||
@@ -132,17 +154,27 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
declare const staticValues: {
|
||||
serverName: string;
|
||||
serverNick: string;
|
||||
serverID: string;
|
||||
mapName: string;
|
||||
unitSet: string;
|
||||
};
|
||||
</script>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, toRef, watch, type Ref } from "vue";
|
||||
import { delay } from "@/util/delay";
|
||||
import { SammoAPI } from "@/SammoAPI";
|
||||
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 { useToast, BFormSelect, BFormSelectOptionGroup } from "bootstrap-vue-3";
|
||||
import { useToast, BFormSelect } from "bootstrap-vue-3";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import { isBrightColor } from "@/util/isBrightColor";
|
||||
|
||||
const serverID = staticValues.serverID;
|
||||
const toasts = unwrap(useToast());
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -187,12 +219,107 @@ const messageIndexedList: Record<MsgType, Ref<MsgItem[]>> = {
|
||||
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) {
|
||||
if (msg.option.delete) {
|
||||
(() => {
|
||||
const targetID = msg.option.delete;
|
||||
const targetMsg = messageStorage.get(targetID);
|
||||
if(!targetMsg){
|
||||
if (!targetMsg) {
|
||||
return;
|
||||
}
|
||||
targetMsg.option.invalid = true;
|
||||
@@ -232,6 +359,7 @@ function updateMsgResponse(response: MsgResponse) {
|
||||
|
||||
if (msgList.value.length == 0) {
|
||||
msgList.value = newMsgList;
|
||||
updateLatestMsg(newMsgList);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -256,6 +384,7 @@ function updateMsgResponse(response: MsgResponse) {
|
||||
const newMsgTailID = filteredMsgList[filteredMsgList.length - 1].id;
|
||||
if (newMsgTailID > msgHeadID) {
|
||||
msgList.value = [...filteredMsgList, ...msgList.value];
|
||||
updateLatestMsg(filteredMsgList);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -592,7 +721,7 @@ onMounted(async () => {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.MessageList{
|
||||
.MessageList {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: 650px;
|
||||
|
||||
Reference in New Issue
Block a user