返回提交历史
Modified
src/controllers/custom/setBoosterController.ts
+7
-32
Modified
static/webui/index.html
+2
-2
Modified
static/webui/script.js
+67
-69
Modified
static/webui/translations/de.js
+1
-1
Modified
static/webui/translations/en.js
+1
-1
Modified
static/webui/translations/es.js
+1
-1
Modified
static/webui/translations/fr.js
+1
-1
Modified
static/webui/translations/ru.js
+1
-1
Modified
static/webui/translations/uk.js
+1
-1
Modified
static/webui/translations/zh.js
+1
-1
XFEstudio/SpaceNinjaServer
chore(webui): unify Boosters code (#2808)
Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2808 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
71c4835a
代码差异
10 个文件
+83
-110
@@ -1,44 +1,19 @@
1
1
import { getAccountIdForRequest } from "../../services/loginService.ts";
2
2
import { getInventory } from "../../services/inventoryService.ts";
3
3
import type { RequestHandler } from "express";
4
import { ExportBoosters } from "warframe-public-export-plus";
4
import type { IBooster } from "../../types/inventoryTypes/inventoryTypes.ts";
5
5
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
6
6
7
const I32_MAX = 0x7fffffff;
8
9
7
export const setBoosterController: RequestHandler = async (req, res) => {
10
8
const accountId = await getAccountIdForRequest(req);
11
const requests = req.body as { ItemType: string; ExpiryDate: number }[];
9
const requests = req.body as IBooster[];
12
10
const inventory = await getInventory(accountId, "Boosters");
13
const boosters = inventory.Boosters;
14
if (
15
requests.some(request => {
16
if (typeof request.ItemType !== "string") return true;
17
if (Object.entries(ExportBoosters).find(([_, item]) => item.typeName === request.ItemType) === undefined)
18
return true;
19
if (typeof request.ExpiryDate !== "number") return true;
20
if (request.ExpiryDate < 0 || request.ExpiryDate > I32_MAX) return true;
21
return false;
22
})
23
) {
24
res.status(400).send("Invalid ItemType provided.");
25
return;
26
}
27
const now = Math.trunc(Date.now() / 1000);
28
for (const { ItemType, ExpiryDate } of requests) {
29
if (ExpiryDate <= now) {
30
// remove expired boosters
31
const index = boosters.findIndex(item => item.ItemType === ItemType);
32
if (index !== -1) {
33
boosters.splice(index, 1);
34
}
11
for (const request of requests) {
12
const index = inventory.Boosters.findIndex(item => item.ItemType === request.ItemType);
13
if (index !== -1) {
14
inventory.Boosters[index].ExpiryDate = request.ExpiryDate;
35
15
} else {
36
const boosterItem = boosters.find(item => item.ItemType === ItemType);
37
if (boosterItem) {
38
boosterItem.ExpiryDate = ExpiryDate;
39
} else {
40
boosters.push({ ItemType, ExpiryDate });
41
}
16
inventory.Boosters.push(request);
42
17
}
43
18
}
44
19
await inventory.save();
@@ -476,9 +476,9 @@
476
476
</div>
477
477
<div class="col-lg-6">
478
478
<div class="card" style="height: 400px;">
479
<h5 class="card-header" data-loc="inventory_Boosters"></h5>
479
<h5 class="card-header" data-loc="inventory_boosters"></h5>
480
480
<div class="card-body d-flex flex-column">
481
<form class="input-group mb-3" onsubmit="doAcquireBoosters();return false;">
481
<form class="input-group mb-3" onsubmit="doAcquireBooster();return false;">
482
482
<input class="form-control" id="acquire-type-Boosters" list="datalist-Boosters" />
483
483
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
484
484
</form>
@@ -1007,6 +1007,67 @@ function updateInventory() {
1007
1007
document.getElementById("EvolutionProgress-list").appendChild(tr);
1008
1008
});
1009
1009
1010
document.getElementById("Boosters-list").innerHTML = "";
1011
data.Boosters.forEach(item => {
1012
if (item.ExpiryDate < Math.floor(Date.now() / 1000)) {
1013
// Booster has expired, skip it
1014
return;
1015
}
1016
const tr = document.createElement("tr");
1017
{
1018
const td = document.createElement("td");
1019
td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
1020
tr.appendChild(td);
1021
}
1022
{
1023
const td = document.createElement("td");
1024
td.classList = "text-end text-nowrap";
1025
{
1026
const form = document.createElement("form");
1027
form.style.display = "inline-block";
1028
form.onsubmit = function (event) {
1029
event.preventDefault();
1030
const maxDate = new Date(input.max);
1031
const selectedDate = new Date(input.value);
1032
if (selectedDate > maxDate) {
1033
input.value = maxDate.toISOString().slice(0, 16);
1034
}
1035
doChangeBoosterExpiry(item.ItemType, input);
1036
};
1037
1038
const input = document.createElement("input");
1039
input.type = "datetime-local";
1040
input.classList = "form-control form-control-sm";
1041
input.value = formatDatetime("%Y-%m-%d %H:%M:%s", item.ExpiryDate * 1000);
1042
input.max = "2038-01-19T03:14";
1043
input.onblur = function () {
1044
const maxDate = new Date(input.max);
1045
const selectedDate = new Date(input.value);
1046
if (selectedDate > maxDate) {
1047
input.value = maxDate.toISOString().slice(0, 16);
1048
}
1049
doChangeBoosterExpiry(item.ItemType, input);
1050
};
1051
1052
form.appendChild(input);
1053
td.appendChild(form);
1054
}
1055
{
1056
const a = document.createElement("a");
1057
a.href = "#";
1058
a.onclick = function (event) {
1059
event.preventDefault();
1060
setBooster(item.ItemType, 0);
1061
};
1062
a.title = loc("code_remove");
1063
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>`;
1064
td.appendChild(a);
1065
}
1066
tr.appendChild(td);
1067
}
1068
document.getElementById("Boosters-list").appendChild(tr);
1069
});
1070
1010
1071
document.getElementById("FlavourItems-list").innerHTML = "";
1011
1072
data.FlavourItems.forEach(item => {
1012
1073
const datalist = document.getElementById("datalist-FlavourItems");
@@ -1619,63 +1680,6 @@ function updateInventory() {
1619
1680
}
1620
1681
document.getElementById("changeSyndicate").value = data.SupportedSyndicate ?? "";
1621
1682
1622
document.getElementById("Boosters-list").innerHTML = "";
1623
const now = Math.floor(Date.now() / 1000);
1624
data.Boosters.forEach(({ ItemType, ExpiryDate }) => {
1625
if (ExpiryDate < now) {
1626
// Booster has expired, skip it
1627
return;
1628
}
1629
const tr = document.createElement("tr");
1630
{
1631
const td = document.createElement("td");
1632
td.textContent = itemMap[ItemType]?.name ?? ItemType;
1633
tr.appendChild(td);
1634
}
1635
{
1636
const td = document.createElement("td");
1637
td.classList = "text-end text-nowrap";
1638
const timeString = formatDatetime("%Y-%m-%d %H:%M:%s", ExpiryDate * 1000);
1639
const inlineForm = document.createElement("form");
1640
const input = document.createElement("input");
1641
1642
inlineForm.style.display = "inline-block";
1643
inlineForm.onsubmit = function (event) {
1644
event.preventDefault();
1645
doChangeBoosterExpiry(ItemType, input);
1646
};
1647
input.type = "datetime-local";
1648
input.classList.add("form-control");
1649
input.classList.add("form-control-sm");
1650
input.value = timeString;
1651
let changed = false;
1652
input.onchange = function () {
1653
changed = true;
1654
};
1655
input.onblur = function () {
1656
if (changed) {
1657
doChangeBoosterExpiry(ItemType, input);
1658
}
1659
};
1660
inlineForm.appendChild(input);
1661
1662
td.appendChild(inlineForm);
1663
1664
const removeButton = document.createElement("a");
1665
removeButton.title = loc("code_remove");
1666
removeButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>`;
1667
removeButton.href = "#";
1668
removeButton.onclick = function (event) {
1669
event.preventDefault();
1670
setBooster(ItemType, 0);
1671
};
1672
td.appendChild(removeButton);
1673
1674
tr.appendChild(td);
1675
}
1676
document.getElementById("Boosters-list").appendChild(tr);
1677
});
1678
1679
1683
if (single.getCurrentPath().startsWith("/webui/guildView")) {
1680
1684
const guildReq = $.get("/custom/getGuild?guildId=" + window.guildId);
1681
1685
guildReq.done(guildData => {
@@ -3516,7 +3520,7 @@ function handleModularSelection(category) {
3516
3520
});
3517
3521
}
3518
3522
3519
function setBooster(ItemType, ExpiryDate, callback) {
3523
function setBooster(ItemType, ExpiryDate) {
3520
3524
revalidateAuthz().then(() => {
3521
3525
$.post({
3522
3526
url: "/custom/setBooster?" + window.authz,
@@ -3529,33 +3533,27 @@ function setBooster(ItemType, ExpiryDate, callback) {
3529
3533
])
3530
3534
}).done(function () {
3531
3535
updateInventory();
3532
if (callback) callback();
3533
3536
});
3534
3537
});
3535
3538
}
3536
3539
3537
function doAcquireBoosters() {
3540
function doAcquireBooster() {
3538
3541
const uniqueName = getKey(document.getElementById("acquire-type-Boosters"));
3539
3542
if (!uniqueName) {
3540
3543
$("#acquire-type-Boosters").addClass("is-invalid").focus();
3541
3544
return;
3542
3545
}
3543
const ExpiryDate = Date.now() / 1000 + 3 * 24 * 60 * 60; // default 3 days
3544
setBooster(uniqueName, ExpiryDate, () => {
3545
$("#acquire-type-Boosters").val("");
3546
});
3546
setBooster(uniqueName, Math.floor(Date.now() / 1000 + 3 * 24 * 60 * 60));
3547
document.getElementById("acquire-type-Boosters").value = "";
3547
3548
}
3548
3549
3549
3550
function doChangeBoosterExpiry(ItemType, ExpiryDateInput) {
3550
console.log("Changing booster expiry for", ItemType, "to", ExpiryDateInput.value);
3551
// cast local datetime string to unix timestamp
3552
const ExpiryDate = Math.trunc(new Date(ExpiryDateInput.value).getTime() / 1000);
3551
const ExpiryDate = Math.floor(new Date(ExpiryDateInput.value).getTime() / 1000);
3553
3552
if (isNaN(ExpiryDate)) {
3554
3553
ExpiryDateInput.addClass("is-invalid").focus();
3555
return false;
3554
return;
3556
3555
}
3557
3556
setBooster(ItemType, ExpiryDate);
3558
return true;
3559
3557
}
3560
3558
3561
3559
function formatDatetime(fmt, date) {
@@ -109,7 +109,7 @@ dict = {
109
109
inventory_moaPets: `Moas`,
110
110
inventory_kubrowPets: `Bestien`,
111
111
inventory_evolutionProgress: `Incarnon-Entwicklungsfortschritte`,
112
inventory_Boosters: `Booster`,
112
inventory_boosters: `Booster`,
113
113
inventory_flavourItems: `<abbr title="Animationssets, Glyphen, Farbpaletten usw.">Sammlerstücke</abbr>`,
114
114
inventory_shipDecorations: `Schiffsdekorationen`,
115
115
inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`,
@@ -108,7 +108,7 @@ dict = {
108
108
inventory_moaPets: `Moas`,
109
109
inventory_kubrowPets: `Beasts`,
110
110
inventory_evolutionProgress: `Incarnon Evolution Progress`,
111
inventory_Boosters: `Boosters`,
111
inventory_boosters: `Boosters`,
112
112
inventory_flavourItems: `<abbr title="Animation Sets, Glyphs, Palettes, etc.">Flavour Items</abbr>`,
113
113
inventory_shipDecorations: `Ship Decorations`,
114
114
inventory_bulkAddSuits: `Add Missing Warframes`,
@@ -109,7 +109,7 @@ dict = {
109
109
inventory_moaPets: `Moas`,
110
110
inventory_kubrowPets: `Bestias`,
111
111
inventory_evolutionProgress: `Progreso de evolución Incarnon`,
112
inventory_Boosters: `Potenciadores`,
112
inventory_boosters: `Potenciadores`,
113
113
inventory_flavourItems: `<abbr title="Conjuntos de animaciones, glifos, paletas, etc.">Ítems estéticos</abbr>`,
114
114
inventory_shipDecorations: `Decoraciones de nave`,
115
115
inventory_bulkAddSuits: `Agregar Warframes faltantes`,
@@ -109,7 +109,7 @@ dict = {
109
109
inventory_moaPets: `Moas`,
110
110
inventory_kubrowPets: `Bêtes`,
111
111
inventory_evolutionProgress: `Progrès de l'évolution Incarnon`,
112
inventory_Boosters: `Boosters`,
112
inventory_boosters: `Boosters`,
113
113
inventory_flavourItems: `[UNTRANSLATED] <abbr title="Animation Sets, Glyphs, Palettes, etc.">Flavour Items</abbr>`,
114
114
inventory_shipDecorations: `Décorations du vaisseau`,
115
115
inventory_bulkAddSuits: `Ajouter les Warframes manquantes`,
@@ -109,7 +109,7 @@ dict = {
109
109
inventory_moaPets: `МОА`,
110
110
inventory_kubrowPets: `Звери`,
111
111
inventory_evolutionProgress: `Прогресс эволюции Инкарнонов`,
112
inventory_Boosters: `Бустеры`,
112
inventory_boosters: `Бустеры`,
113
113
inventory_flavourItems: `<abbr title="Наборы анимаций, глифы, палитры и т. д.">Уникальные предметы</abbr>`,
114
114
inventory_shipDecorations: `Украшения корабля`,
115
115
inventory_bulkAddSuits: `Добавить отсутствующие Варфреймы`,
@@ -109,7 +109,7 @@ dict = {
109
109
inventory_moaPets: `МОА`,
110
110
inventory_kubrowPets: `Тварини`,
111
111
inventory_evolutionProgress: `Прогрес еволюції Інкарнонів`,
112
inventory_Boosters: `Посилення`,
112
inventory_boosters: `Посилення`,
113
113
inventory_flavourItems: `<abbr title="Набори анімацій, гліфи, палітри і т. д.">Унікальні предмети</abbr>`,
114
114
inventory_shipDecorations: `Прикраси судна`,
115
115
inventory_bulkAddSuits: `Додати відсутні Ворфрейми`,
@@ -109,7 +109,7 @@ dict = {
109
109
inventory_moaPets: `恐鸟`,
110
110
inventory_kubrowPets: `动物同伴`,
111
111
inventory_evolutionProgress: `灵化之源进度`,
112
inventory_Boosters: `加成器`,
112
inventory_boosters: `加成器`,
113
113
inventory_flavourItems: `<abbr title="动作表情、浮印、调色板等">装饰物品</abbr>`,
114
114
inventory_shipDecorations: `飞船装饰`,
115
115
inventory_bulkAddSuits: `添加缺失战甲`,