feat: 개인 메시지, 외교 메시지 수신 시 toast 알림, 모두 읽음 버튼 추가
This commit is contained in:
@@ -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">서신전달&갱신</BButton>
|
<BButton variant="primary" @click="sendMessage">서신전달&갱신</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;
|
||||||
|
|||||||
Reference in New Issue
Block a user