feat: 이미 읽은 메시지를 DB에서 처리
This commit is contained in:
@@ -6,8 +6,10 @@ use sammo\Session;
|
|||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use sammo\DB;
|
use sammo\DB;
|
||||||
use sammo\Enums\APIRecoveryType;
|
use sammo\Enums\APIRecoveryType;
|
||||||
|
use sammo\Enums\GeneralStorKey;
|
||||||
use sammo\Enums\MessageType;
|
use sammo\Enums\MessageType;
|
||||||
use sammo\Json;
|
use sammo\Json;
|
||||||
|
use sammo\KVStorage;
|
||||||
use sammo\Message;
|
use sammo\Message;
|
||||||
use sammo\TimeUtil;
|
use sammo\TimeUtil;
|
||||||
use sammo\Util;
|
use sammo\Util;
|
||||||
@@ -140,9 +142,19 @@ class GetRecentMessage extends \sammo\BaseAPI
|
|||||||
array_pop($result[$lastType]);
|
array_pop($result[$lastType]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$generalStor = KVStorage::getStorage($db, "general_{$generalID}");
|
||||||
|
[$latestReadDiplomacyMsg, $latestReadPrivateMsg] = $generalStor->getValuesAsArray([
|
||||||
|
GeneralStorKey::latestReadDiplomacyMsg,
|
||||||
|
GeneralStorKey::latestReadPrivateMsg
|
||||||
|
]);
|
||||||
|
|
||||||
$result['sequence'] = $nextSequence;
|
$result['sequence'] = $nextSequence;
|
||||||
$result['nationID'] = $nationID;
|
$result['nationID'] = $nationID;
|
||||||
$result['generalName'] = $generalName;
|
$result['generalName'] = $generalName;
|
||||||
|
$result['latestRead'] = [
|
||||||
|
'diplomacy' => $latestReadDiplomacyMsg ?? 0,
|
||||||
|
'private' => $latestReadPrivateMsg ?? 0,
|
||||||
|
];
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace sammo\API\Message;
|
||||||
|
|
||||||
|
use sammo\DB;
|
||||||
|
use sammo\Enums\APIRecoveryType;
|
||||||
|
use sammo\Enums\GeneralStorKey;
|
||||||
|
use sammo\Enums\MessageType;
|
||||||
|
use sammo\KVStorage;
|
||||||
|
use sammo\Session;
|
||||||
|
use sammo\Validator;
|
||||||
|
|
||||||
|
class ReadLatestMessage extends \sammo\BaseAPI
|
||||||
|
{
|
||||||
|
public function validateArgs(): ?string
|
||||||
|
{
|
||||||
|
//type이 개인 메시지 혹은 외교 메시지여야함
|
||||||
|
$v = new Validator($this->args);
|
||||||
|
$v->rule('required', ['type', 'msgID'])
|
||||||
|
->rule('in', 'type', [MessageType::private->value, MessageType::diplomacy->value])
|
||||||
|
->rule('int', 'msgID');
|
||||||
|
if (!$v->validate()) {
|
||||||
|
return $v->errorStr();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredSessionMode(): int
|
||||||
|
{
|
||||||
|
return static::REQ_GAME_LOGIN | static::REQ_READ_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function launch(Session $session, ?\DateTimeInterface $modifiedSince, ?string $reqEtag): null | string | array | APIRecoveryType
|
||||||
|
{
|
||||||
|
$db = DB::db();
|
||||||
|
$generalID = $session->generalID;
|
||||||
|
$generalStor = KVStorage::getStorage($db, "general_{$generalID}");
|
||||||
|
$type = MessageType::from($this->args['type']);
|
||||||
|
$msgID = $this->args['msgID'];
|
||||||
|
|
||||||
|
$storKey = GeneralStorKey::latestReadPrivateMsg;
|
||||||
|
if($type == MessageType::diplomacy){
|
||||||
|
$storKey = GeneralStorKey::latestReadDiplomacyMsg;
|
||||||
|
}
|
||||||
|
$oldValue = $generalStor->getValue($storKey);
|
||||||
|
if($oldValue === null || $oldValue < $msgID){
|
||||||
|
$generalStor->setValue($storKey, $msgID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace sammo\Enums;
|
||||||
|
|
||||||
|
enum GeneralStorKey: string{
|
||||||
|
case latestReadPrivateMsg = 'latestReadPrivateMsg';
|
||||||
|
case latestReadDiplomacyMsg = 'latestReadDiplomacyMsg';
|
||||||
|
}
|
||||||
+10
-6
@@ -29,7 +29,7 @@ import type {
|
|||||||
GetFrontInfoResponse,
|
GetFrontInfoResponse,
|
||||||
GetHistoryResponse,
|
GetHistoryResponse,
|
||||||
GetMenuResponse,
|
GetMenuResponse,
|
||||||
GetRecentRecordResponse,
|
GetRecentRecordResponse,
|
||||||
} from "./defs/API/Global";
|
} from "./defs/API/Global";
|
||||||
import type { CachedMapResult, CommandTableResponse, GeneralListResponse, ItemTypeKey, MapResult } from "./defs";
|
import type { CachedMapResult, CommandTableResponse, GeneralListResponse, ItemTypeKey, MapResult } from "./defs";
|
||||||
import type { VoteDetailResult, VoteListResult } from "./defs/API/Vote";
|
import type { VoteDetailResult, VoteListResult } from "./defs/API/Vote";
|
||||||
@@ -71,12 +71,12 @@ const apiRealPath = {
|
|||||||
amount: number;
|
amount: number;
|
||||||
}>,
|
}>,
|
||||||
GetUniqueItemAuctionDetail: GET as APICallT<{
|
GetUniqueItemAuctionDetail: GET as APICallT<{
|
||||||
auctionID: number;
|
auctionID: number;
|
||||||
}, UniqueItemAuctionDetail>,
|
}, UniqueItemAuctionDetail>,
|
||||||
GetUniqueItemAuctionList: GET as APICallT<undefined, UniqueItemAuctionList>,
|
GetUniqueItemAuctionList: GET as APICallT<undefined, UniqueItemAuctionList>,
|
||||||
OpenUniqueAuction: POST as APICallT<{
|
OpenUniqueAuction: POST as APICallT<{
|
||||||
itemID: string,
|
itemID: string,
|
||||||
amount: number,
|
amount: number,
|
||||||
}, OpenAuctionResponse>,
|
}, OpenAuctionResponse>,
|
||||||
},
|
},
|
||||||
Betting: {
|
Betting: {
|
||||||
@@ -199,7 +199,11 @@ const apiRealPath = {
|
|||||||
SendMessage: POST as APICallT<{
|
SendMessage: POST as APICallT<{
|
||||||
mailbox: number;
|
mailbox: number;
|
||||||
text: string;
|
text: string;
|
||||||
}, ValidResponse & {msgID: number, msgType: MsgType}>
|
}, ValidResponse & { msgID: number, msgType: MsgType }>,
|
||||||
|
ReadLatestMessage: PATCH as APICallT<{
|
||||||
|
type: 'diplomacy' | 'private';
|
||||||
|
msgID: number;
|
||||||
|
}>
|
||||||
},
|
},
|
||||||
Misc: {
|
Misc: {
|
||||||
UploadImage: POST as APICallT<
|
UploadImage: POST as APICallT<
|
||||||
@@ -273,7 +277,7 @@ const apiRealPath = {
|
|||||||
troopID: number;
|
troopID: number;
|
||||||
troopName: string;
|
troopName: string;
|
||||||
}>,
|
}>,
|
||||||
GetNationInfo: GET as APICallT<{full?: boolean}, NationInfoResponse>,
|
GetNationInfo: GET as APICallT<{ full?: boolean }, NationInfoResponse>,
|
||||||
},
|
},
|
||||||
Troop: {
|
Troop: {
|
||||||
NewTroop: POST as APICallT<{
|
NewTroop: POST as APICallT<{
|
||||||
|
|||||||
@@ -255,21 +255,8 @@ const messageIndexedList: Record<MsgType, Ref<MsgItem[]>> = {
|
|||||||
diplomacy: messageDiplomacy,
|
diplomacy: messageDiplomacy,
|
||||||
};
|
};
|
||||||
|
|
||||||
function generateLatestMsgState(msgType: MsgType) {
|
const latestPrivateMsgToastInfo = ref<[string | undefined, number, number]>([undefined, 0, 0]);
|
||||||
const storageKey = `state.${serverID}.latestReadMsg.${msgType}`;
|
const latestDiplomacyMsgToastInfo = ref<[string | undefined, number, number]>([undefined, 0, 0]);
|
||||||
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) {
|
function readLatestMsg(msgType: MsgType) {
|
||||||
const targetMap = {
|
const targetMap = {
|
||||||
@@ -288,6 +275,13 @@ function readLatestMsg(msgType: MsgType) {
|
|||||||
if (toastID) {
|
if (toastID) {
|
||||||
toasts.remove(toastID);
|
toasts.remove(toastID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(msgType == "private" || msgType == "diplomacy"){
|
||||||
|
void SammoAPI.Message.ReadLatestMessage({
|
||||||
|
type: msgType,
|
||||||
|
msgID: lastestReceivedID,
|
||||||
|
});
|
||||||
|
}
|
||||||
target.value = [undefined, lastestReceivedID, lastestReceivedID];
|
target.value = [undefined, lastestReceivedID, lastestReceivedID];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,6 +397,27 @@ function updateMsgResponse(response: MsgResponse) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(response.latestRead){
|
||||||
|
const newDiplomacyIdx = response.latestRead.diplomacy;
|
||||||
|
const [diplomacyToastID, diplomacyLatestMsgID] = latestDiplomacyMsgToastInfo.value;
|
||||||
|
if(diplomacyLatestMsgID < newDiplomacyIdx){
|
||||||
|
if(diplomacyToastID){
|
||||||
|
toasts.remove(diplomacyToastID);
|
||||||
|
}
|
||||||
|
latestDiplomacyMsgToastInfo.value = [undefined, newDiplomacyIdx, newDiplomacyIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPrivateIdx = response.latestRead.private;
|
||||||
|
const [privateToastID, privateLatestMsgID] = latestPrivateMsgToastInfo.value;
|
||||||
|
if(privateLatestMsgID < newPrivateIdx){
|
||||||
|
if(privateToastID){
|
||||||
|
toasts.remove(privateToastID);
|
||||||
|
}
|
||||||
|
latestPrivateMsgToastInfo.value = [undefined, newPrivateIdx, newPrivateIdx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
lastSequence.value = Math.max(lastSequence.value, response.sequence);
|
lastSequence.value = Math.max(lastSequence.value, response.sequence);
|
||||||
|
|
||||||
for (const msgType of Object.keys(messageIndexedList) as (keyof typeof messageIndexedList)[]) {
|
for (const msgType of Object.keys(messageIndexedList) as (keyof typeof messageIndexedList)[]) {
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ export type MsgResponse = {
|
|||||||
nationID: number;
|
nationID: number;
|
||||||
generalName: string;
|
generalName: string;
|
||||||
sequence: number;
|
sequence: number;
|
||||||
|
latestRead?: {
|
||||||
|
private: number;
|
||||||
|
diplomacy: number;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MailboxItem = {
|
export type MailboxItem = {
|
||||||
|
|||||||
Reference in New Issue
Block a user