feat: 2.3.1
This commit is contained in:
@@ -1,3 +1,18 @@
|
||||
## [2.3.2] — 2026-05-12
|
||||
- Integrated Mailbox UI into lobby with CanvasLayer overlay (renders above 3D viewport).
|
||||
- Redesigned `mailbox_panel.tscn` to 3-column layout: scrollable mail list | content area | reward slots.
|
||||
- Auto read-all and auto claim-all triggered on mailbox open — no "NEW" tags remain after viewing.
|
||||
- Fixed `MailboxBtn` text glitch — removed text override, replaced with `MailBadge` Label overlay in top-right corner of button.
|
||||
- Fixed `mark_as_read()` in `MailManager` — now persists `read_ids` to server via new `save_mail_state` RPC.
|
||||
- Admin Panel: Announcement tab `Target User` field now accepts username, display name, or user_id with **Find** button for resolution.
|
||||
- Admin Panel: New **Mail Manager** tab listing all global and personal sent mails with Type, Title, Sender, Start, Expires, Status columns.
|
||||
- Admin Panel: Edit dialog for mails uses `date_picker.tscn` for end date and supports reassigning recipient (username lookup + move).
|
||||
- Admin Panel: **End Now** button sets `end_date` to current timestamp; **Delete** permanently removes mail from server storage.
|
||||
- Server: Added `admin_list_mail` RPC — paginates all `inbox/personal` storage objects across all users via `storageList`.
|
||||
- Server: Added `admin_update_mail` RPC — extract-then-reinsert pattern supports field edits and cross-user mail movement.
|
||||
- Server: Added `admin_delete_mail_server` RPC — hard-removes mail from global config or personal inbox storage.
|
||||
- Server: Added `save_mail_state` RPC — merges client `read_ids` into server `inbox/state` without clobbering claimed/deleted IDs.
|
||||
|
||||
## [2.3.1] — 2026-05-11
|
||||
- Integrated DM tab system directly into the lobby chatbox — DMs now open as closeable tabs inside ChatPanel instead of a fullscreen overlay.
|
||||
- Removed static DMTabBtn; DM tabs are dynamically created per friend using a scene-local DMTabTemplate with (X) close button.
|
||||
|
||||
@@ -1,7 +1,27 @@
|
||||
{
|
||||
"latest_version": "2.3.1",
|
||||
"latest_version": "2.3.2",
|
||||
"minimum_app_version": "2.1.0",
|
||||
"releases": [
|
||||
{
|
||||
"version": "2.3.2",
|
||||
"date": "2026-05-12",
|
||||
"pck_url": "https://raw.githubusercontent.com/adtpdn/tekton-updates/main/latest/patch.pck",
|
||||
"pck_size": 0,
|
||||
"changelog": [
|
||||
"Integrated Mailbox UI into lobby with CanvasLayer overlay",
|
||||
"Redesigned mailbox_panel.tscn to 3-column layout (mail list, content, rewards)",
|
||||
"Added auto read-all and auto claim-all on mailbox open",
|
||||
"Fixed MailboxBtn text glitch — replaced text override with MailBadge Label overlay",
|
||||
"Admin Panel: Target user field now accepts username or user_id with Find button",
|
||||
"Admin Panel: New Mail Manager tab to view, edit, end, and delete sent announcements",
|
||||
"Admin Panel: Edit dialog uses DatePicker for end date and supports recipient reassignment",
|
||||
"Server: Added admin_list_mail, admin_update_mail, admin_delete_mail_server RPCs",
|
||||
"Server: admin_list_mail scans all users personal inboxes via storageList",
|
||||
"Server: admin_update_mail supports moving mail between users and global",
|
||||
"Server: Added save_mail_state RPC to persist read_ids to server",
|
||||
"Fixed mark_as_read — now persists to Nakama inbox state instead of local-only pass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.3.1",
|
||||
"date": "2026-05-11",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://mw0hjqg4u58a"
|
||||
path="res://.godot/imported/btn_back.png-766f18f6c608c9d18d187505f5ac889b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gui/mailbox/btn_back.png"
|
||||
dest_files=["res://.godot/imported/btn_back.png-766f18f6c608c9d18d187505f5ac889b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://byvqbe1m2dr41"
|
||||
path="res://.godot/imported/btn_delete.png-cf3f17cefdd7f374d94d8379a1c9f478.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gui/mailbox/btn_delete.png"
|
||||
dest_files=["res://.godot/imported/btn_delete.png-cf3f17cefdd7f374d94d8379a1c9f478.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cs5lke40qve7y"
|
||||
path="res://.godot/imported/mailbox_icon.png-c85e0603097425f2befc209c5030293f.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gui/mailbox/mailbox_icon.png"
|
||||
dest_files=["res://.godot/imported/mailbox_icon.png-c85e0603097425f2befc209c5030293f.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bc14cddtp0gvq"
|
||||
path="res://.godot/imported/mailbox_node_01.png-a4c2ade2737ef5ed9de0d64d83793bb5.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gui/mailbox/mailbox_node_01.png"
|
||||
dest_files=["res://.godot/imported/mailbox_node_01.png-a4c2ade2737ef5ed9de0d64d83793bb5.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cosjvp4msuv34"
|
||||
path="res://.godot/imported/mailbox_node_02.png-c71d25c932b515f6569f65ba769a206c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gui/mailbox/mailbox_node_02.png"
|
||||
dest_files=["res://.godot/imported/mailbox_node_02.png-c71d25c932b515f6569f65ba769a206c.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://exkx1hado3wi"
|
||||
path="res://.godot/imported/read_all.png-3faf33804509dc0f18651b7ee0cbaf4b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gui/mailbox/read_all.png"
|
||||
dest_files=["res://.godot/imported/read_all.png-3faf33804509dc0f18651b7ee0cbaf4b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -1,286 +0,0 @@
|
||||
[gd_scene format=3 uid="uid://xpalyawdgnyl"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b5q6yekyk0tld" path="res://scenes/lobby.gd" id="1_lp6xi"]
|
||||
[ext_resource type="Theme" uid="uid://da337sh5qxi0s" path="res://assets/themes/ui_theme.tres" id="2_theme"]
|
||||
|
||||
[node name="Lobby" type="Control" unique_id=1681755368]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("2_theme")
|
||||
script = ExtResource("1_lp6xi")
|
||||
|
||||
[node name="Background" type="ColorRect" parent="." unique_id=610186638]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.12, 0.1, 0.08, 1)
|
||||
|
||||
[node name="MainMenuPanel" type="PanelContainer" parent="." unique_id=749972967]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -220.0
|
||||
offset_top = -240.0
|
||||
offset_right = 220.0
|
||||
offset_bottom = 240.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MainMenuPanel" unique_id=172832102]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 18
|
||||
|
||||
[node name="TitleContainer" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=1254704944]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="Title" type="Label" parent="MainMenuPanel/VBoxContainer/TitleContainer" unique_id=1313646849]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1)
|
||||
theme_override_font_sizes/font_size = 44
|
||||
text = "TEKTON DASH"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Subtitle" type="Label" parent="MainMenuPanel/VBoxContainer/TitleContainer" unique_id=981494139]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "ARMAGEDDON VERSION"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Separator" type="HSeparator" parent="MainMenuPanel/VBoxContainer" unique_id=1861761683]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="InputSection" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=113453689]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="PlayerNameLabel" type="Label" parent="MainMenuPanel/VBoxContainer/InputSection" unique_id=2007198770]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1)
|
||||
theme_override_font_sizes/font_size = 13
|
||||
text = "YOUR NAME"
|
||||
|
||||
[node name="PlayerNameInput" type="LineEdit" parent="MainMenuPanel/VBoxContainer/InputSection" unique_id=543986981]
|
||||
custom_minimum_size = Vector2(0, 44)
|
||||
layout_mode = 2
|
||||
text = "Player"
|
||||
placeholder_text = "Enter your name..."
|
||||
|
||||
[node name="ButtonSection" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=357245135]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="CreateRoomBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=1297504653]
|
||||
custom_minimum_size = Vector2(0, 48)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "CREATE ROOM"
|
||||
|
||||
[node name="BrowseRoomsBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=1825906901]
|
||||
custom_minimum_size = Vector2(0, 48)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "BROWSE ROOMS"
|
||||
|
||||
[node name="RoomListPanel" type="PanelContainer" parent="." unique_id=1969054574]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -300.0
|
||||
offset_top = -260.0
|
||||
offset_right = 300.0
|
||||
offset_bottom = 260.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="RoomListPanel" unique_id=726092140]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
|
||||
[node name="Header" type="Label" parent="RoomListPanel/VBoxContainer" unique_id=1272860526]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1)
|
||||
theme_override_font_sizes/font_size = 28
|
||||
text = "SERVER BROWSER"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="RoomListPanel/VBoxContainer" unique_id=38843595]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MatchIdLabel" type="Label" parent="RoomListPanel/VBoxContainer" unique_id=316758674]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "DIRECT CONNECT (MATCH ID)"
|
||||
|
||||
[node name="MatchIdInput" type="LineEdit" parent="RoomListPanel/VBoxContainer" unique_id=159753310]
|
||||
custom_minimum_size = Vector2(0, 44)
|
||||
layout_mode = 2
|
||||
placeholder_text = "Paste match ID here..."
|
||||
|
||||
[node name="RoomListLabel" type="Label" parent="RoomListPanel/VBoxContainer" unique_id=867057060]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "AVAILABLE SERVERS"
|
||||
|
||||
[node name="RoomList" type="ItemList" parent="RoomListPanel/VBoxContainer" unique_id=203590990]
|
||||
custom_minimum_size = Vector2(0, 200)
|
||||
layout_mode = 2
|
||||
allow_reselect = true
|
||||
|
||||
[node name="ButtonContainer" type="HBoxContainer" parent="RoomListPanel/VBoxContainer" unique_id=178401080]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
alignment = 1
|
||||
|
||||
[node name="RefreshBtn" type="Button" parent="RoomListPanel/VBoxContainer/ButtonContainer" unique_id=1068680772]
|
||||
custom_minimum_size = Vector2(110, 44)
|
||||
layout_mode = 2
|
||||
text = "REFRESH"
|
||||
|
||||
[node name="JoinBtn" type="Button" parent="RoomListPanel/VBoxContainer/ButtonContainer" unique_id=822083530]
|
||||
custom_minimum_size = Vector2(130, 44)
|
||||
layout_mode = 2
|
||||
text = "JOIN SERVER"
|
||||
|
||||
[node name="BackBtn" type="Button" parent="RoomListPanel/VBoxContainer/ButtonContainer" unique_id=1873303918]
|
||||
custom_minimum_size = Vector2(110, 44)
|
||||
layout_mode = 2
|
||||
text = "BACK"
|
||||
|
||||
[node name="LobbyPanel" type="PanelContainer" parent="." unique_id=1955208668]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -300.0
|
||||
offset_top = -260.0
|
||||
offset_right = 300.0
|
||||
offset_bottom = 260.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="LobbyPanel" unique_id=1443407103]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
|
||||
[node name="RoomNameHeader" type="Label" parent="LobbyPanel/VBoxContainer" unique_id=2019479226]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1)
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "ROOM: "
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="MatchIdContainer" type="HBoxContainer" parent="LobbyPanel/VBoxContainer" unique_id=280307543]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="MatchIdDisplay" type="Label" parent="LobbyPanel/VBoxContainer/MatchIdContainer" unique_id=1883524532]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.5, 0.5, 0.55, 1)
|
||||
theme_override_font_sizes/font_size = 10
|
||||
text = "Match ID: "
|
||||
|
||||
[node name="CopyIdBtn" type="Button" parent="LobbyPanel/VBoxContainer/MatchIdContainer" unique_id=1770583860]
|
||||
custom_minimum_size = Vector2(80, 32)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 10
|
||||
text = "COPY"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="LobbyPanel/VBoxContainer" unique_id=346787128]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PlayersLabel" type="Label" parent="LobbyPanel/VBoxContainer" unique_id=1266635921]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1)
|
||||
theme_override_font_sizes/font_size = 13
|
||||
text = "PLAYERS"
|
||||
|
||||
[node name="PlayerList" type="ItemList" parent="LobbyPanel/VBoxContainer" unique_id=1481128408]
|
||||
custom_minimum_size = Vector2(0, 160)
|
||||
layout_mode = 2
|
||||
allow_reselect = true
|
||||
|
||||
[node name="StatusLabel" type="Label" parent="LobbyPanel/VBoxContainer" unique_id=1750308910]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
text = "Waiting for players..."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="ButtonContainer" type="HBoxContainer" parent="LobbyPanel/VBoxContainer" unique_id=941148439]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
alignment = 1
|
||||
|
||||
[node name="ReadyBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer" unique_id=386730307]
|
||||
custom_minimum_size = Vector2(110, 48)
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "READY"
|
||||
|
||||
[node name="StartGameBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer" unique_id=1510805318]
|
||||
custom_minimum_size = Vector2(140, 48)
|
||||
layout_mode = 2
|
||||
disabled = true
|
||||
text = "START GAME"
|
||||
|
||||
[node name="LeaveBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer" unique_id=1773320572]
|
||||
custom_minimum_size = Vector2(110, 48)
|
||||
layout_mode = 2
|
||||
text = "LEAVE"
|
||||
|
||||
[node name="StatusBar" type="PanelContainer" parent="." unique_id=547557114]
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 24.0
|
||||
offset_top = -72.0
|
||||
offset_right = -24.0
|
||||
offset_bottom = -24.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
|
||||
[node name="ConnectionStatus" type="Label" parent="StatusBar" unique_id=1282790307]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "NOT CONNECTED"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="VersionLabel" type="Label" parent="." unique_id=1619858099]
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -130.0
|
||||
offset_top = 20.0
|
||||
offset_right = -20.0
|
||||
offset_bottom = 40.0
|
||||
grow_horizontal = 0
|
||||
theme_override_colors/font_color = Color(0.6, 0.6, 0.6, 0.6)
|
||||
theme_override_font_sizes/font_size = 11
|
||||
text = "v0.1.0 ALPHA"
|
||||
horizontal_alignment = 2
|
||||
+4
-4
@@ -42,8 +42,8 @@ application/modify_resources=false
|
||||
application/icon=""
|
||||
application/console_wrapper_icon=""
|
||||
application/icon_interpolation=4
|
||||
application/file_version="2.3"
|
||||
application/product_version="2.3"
|
||||
application/file_version="2.3.1"
|
||||
application/product_version="2.3.1"
|
||||
application/company_name="DanchieGo"
|
||||
application/product_name="Tekton Armageddon"
|
||||
application/file_description=""
|
||||
@@ -565,8 +565,8 @@ codesign/digest_algorithm=1
|
||||
codesign/identity_type=0
|
||||
application/modify_resources=false
|
||||
application/console_wrapper_icon=""
|
||||
application/file_version="2.3"
|
||||
application/product_version="2.3"
|
||||
application/file_version="2.3.1"
|
||||
application/product_version="2.3.1"
|
||||
application/company_name="DanchieGo"
|
||||
application/product_name="Tekton Armageddon"
|
||||
application/file_description=""
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true
|
||||
[application]
|
||||
|
||||
config/name="Tekton Dash Armageddon"
|
||||
config/version="2.3.1"
|
||||
config/version="2.3.2"
|
||||
run/main_scene="res://scenes/ui/boot_screen.tscn"
|
||||
config/features=PackedStringArray("4.6", "Forward Plus")
|
||||
boot_splash/bg_color=Color(0.16470589, 0.6745098, 0.9372549, 1)
|
||||
|
||||
+27
-17
@@ -30,6 +30,7 @@ extends Control
|
||||
@onready var shop_btn = %CartBtn
|
||||
@onready var top_right_profile_btn = %ProfileBtn
|
||||
@onready var mailbox_btn = get_node_or_null("%MailboxBtn")
|
||||
@onready var mail_badge = get_node_or_null("%MailBadge")
|
||||
@onready var banner1_btn = get_node_or_null("%Banner1")
|
||||
@onready var ticket_btn = get_node_or_null("%TicketBtn")
|
||||
@onready var mailbox_panel = get_node_or_null("MailboxPanel")
|
||||
@@ -869,28 +870,37 @@ func _on_profile_btn_pressed() -> void:
|
||||
main_menu_panel.hide()
|
||||
profile_panel_instance.show_panel()
|
||||
|
||||
func _on_mailbox_pressed() -> void:
|
||||
if mailbox_panel:
|
||||
mailbox_panel.show_panel()
|
||||
if main_menu_panel:
|
||||
main_menu_panel.hide()
|
||||
|
||||
# Connect the closed signal to reshow main menu if not connected
|
||||
if not mailbox_panel.closed.is_connected(_on_mailbox_closed):
|
||||
mailbox_panel.closed.connect(_on_mailbox_closed)
|
||||
var _mailbox_panel_instance: Control
|
||||
|
||||
func _on_mailbox_closed() -> void:
|
||||
if main_menu_panel:
|
||||
main_menu_panel.show()
|
||||
func _on_mailbox_pressed() -> void:
|
||||
if not _mailbox_panel_instance:
|
||||
var scene = load("res://scenes/ui/mailbox_panel.tscn")
|
||||
if scene:
|
||||
_mailbox_panel_instance = scene.instantiate()
|
||||
_mailbox_panel_instance.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
|
||||
var cl := CanvasLayer.new()
|
||||
cl.layer = 100
|
||||
cl.name = "MailboxCanvasLayer"
|
||||
add_child(cl)
|
||||
cl.add_child(_mailbox_panel_instance)
|
||||
|
||||
if _mailbox_panel_instance.has_signal("closed"):
|
||||
_mailbox_panel_instance.closed.connect(func():
|
||||
_mailbox_panel_instance.get_parent().queue_free()
|
||||
_mailbox_panel_instance = null
|
||||
)
|
||||
|
||||
if _mailbox_panel_instance:
|
||||
_mailbox_panel_instance.show_panel()
|
||||
|
||||
func _on_mail_unread_count_changed(count: int) -> void:
|
||||
if mailbox_btn:
|
||||
if mail_badge:
|
||||
if count > 0:
|
||||
mailbox_btn.text = "MAIL (%d)" % count
|
||||
mailbox_btn.add_theme_color_override("font_color", Color.YELLOW)
|
||||
mail_badge.text = str(count) if count < 100 else "99+"
|
||||
mail_badge.visible = true
|
||||
else:
|
||||
mailbox_btn.text = "MAIL"
|
||||
mailbox_btn.remove_theme_color_override("font_color")
|
||||
mail_badge.visible = false
|
||||
|
||||
func _on_logout_pressed() -> void:
|
||||
AuthManager.logout()
|
||||
|
||||
+48
-70
@@ -31,6 +31,7 @@
|
||||
[ext_resource type="Texture2D" uid="uid://dv782w5t0xlcc" path="res://assets/graphics/gui/lobby/friends.png" id="22_1x1aw"]
|
||||
[ext_resource type="Texture2D" uid="uid://cpy5lppf3ro02" path="res://assets/graphics/gui/play/selection_play1.png" id="22_kn4i6"]
|
||||
[ext_resource type="Texture2D" uid="uid://b0ovmvcm8rt2n" path="res://assets/graphics/gui/play/selection_room0.png" id="23_3jc85"]
|
||||
[ext_resource type="Texture2D" uid="uid://cs5lke40qve7y" path="res://assets/graphics/gui/mailbox/mailbox_icon.png" id="23_835bk"]
|
||||
[ext_resource type="Texture2D" uid="uid://bqcxrfu2jlplr" path="res://assets/graphics/gui/lobby/settings.png" id="23_twy5w"]
|
||||
[ext_resource type="Texture2D" uid="uid://3p0sabd1og31" path="res://assets/graphics/gui/play/selection_room1.png" id="24_jhtcy"]
|
||||
[ext_resource type="Texture2D" uid="uid://bpco6lch7homj" path="res://assets/graphics/gui/play/bg.png" id="25_iwv4c"]
|
||||
@@ -113,6 +114,12 @@ bg_color = Color(0.6, 0.6, 0.6, 0)
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_dfnwm"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_835bk"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ucbax"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_1x1aw"]
|
||||
|
||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_50md7"]
|
||||
texture = ExtResource("25_iwv4c")
|
||||
|
||||
@@ -480,73 +487,6 @@ layout_mode = 2
|
||||
custom_minimum_size = Vector2(0, 8)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CurrencyRow" type="VBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol" unique_id=59384589]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow" unique_id=1807032398]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="StarPanel" type="PanelContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer" unique_id=509438]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme = ExtResource("2_theme")
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer/StarPanel" unique_id=609485]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 12
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 12
|
||||
theme_override_constants/margin_bottom = 4
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer/StarPanel/Margin" unique_id=69485094]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="Icon" type="Label" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer/StarPanel/Margin/HBoxContainer" unique_id=958438]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.9, 0.7, 0.3, 1)
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "✦"
|
||||
|
||||
[node name="Control" type="Control" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer" unique_id=255421565]
|
||||
custom_minimum_size = Vector2(180, 0)
|
||||
layout_direction = 3
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow" unique_id=1399423386]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="GoldPanel" type="PanelContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2" unique_id=9458940]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme = ExtResource("2_theme")
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2/GoldPanel" unique_id=94589]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 12
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 12
|
||||
theme_override_constants/margin_bottom = 4
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2/GoldPanel/Margin" unique_id=9458094]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="Icon" type="Label" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2/GoldPanel/Margin/HBoxContainer" unique_id=9485]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.8, 0.6, 0.2, 1)
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "▤"
|
||||
|
||||
[node name="Control" type="Control" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2" unique_id=320833893]
|
||||
custom_minimum_size = Vector2(180, 0)
|
||||
layout_direction = 3
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SpacerMiddle" type="Control" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol" unique_id=984509]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
@@ -764,6 +704,44 @@ grow_vertical = 2
|
||||
texture = ExtResource("21_ucbax")
|
||||
expand_mode = 2
|
||||
|
||||
[node name="MailboxBtn" type="Button" parent="MainMenuPanel/MainMargin/MainHBox/RightCol/TopRightPanel" unique_id=64042311]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(61, 61)
|
||||
layout_mode = 2
|
||||
theme_override_fonts/font = ExtResource("5_pc087")
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_835bk")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxEmpty_ucbax")
|
||||
theme_override_styles/hover = SubResource("StyleBoxEmpty_1x1aw")
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="MainMenuPanel/MainMargin/MainHBox/RightCol/TopRightPanel/MailboxBtn" unique_id=109558991]
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = ExtResource("23_835bk")
|
||||
expand_mode = 3
|
||||
|
||||
[node name="MailBadge" type="Label" parent="MainMenuPanel/MainMargin/MainHBox/RightCol/TopRightPanel/MailboxBtn"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -18.0
|
||||
offset_top = -2.0
|
||||
offset_right = 2.0
|
||||
offset_bottom = 16.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 1
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_font_sizes/font_size = 10
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
text = ""
|
||||
|
||||
[node name="SocialBtn" type="Button" parent="MainMenuPanel/MainMargin/MainHBox/RightCol/TopRightPanel" unique_id=82719328]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(61, 61)
|
||||
@@ -824,7 +802,7 @@ unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 120)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme = ExtResource("2_theme")
|
||||
theme = ExtResource("14_2630d")
|
||||
theme_override_fonts/font = ExtResource("5_pc087")
|
||||
theme_override_font_sizes/font_size = 30
|
||||
disabled = true
|
||||
@@ -845,7 +823,7 @@ unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 120)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme = ExtResource("2_theme")
|
||||
theme = ExtResource("14_2630d")
|
||||
theme_override_fonts/font = ExtResource("5_pc087")
|
||||
theme_override_font_sizes/font_size = 30
|
||||
disabled = true
|
||||
@@ -866,7 +844,7 @@ unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 120)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme = ExtResource("2_theme")
|
||||
theme = ExtResource("14_2630d")
|
||||
theme_override_fonts/font = ExtResource("5_pc087")
|
||||
theme_override_font_sizes/font_size = 30
|
||||
disabled = true
|
||||
|
||||
@@ -271,17 +271,29 @@ metadata/_tab_index = 3
|
||||
|
||||
[node name="TargetHBox" type="HBoxContainer" parent="Margin/VBox/Tabs/Announcements"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="Label" type="Label" parent="Margin/VBox/Tabs/Announcements/TargetHBox"]
|
||||
layout_mode = 2
|
||||
text = "Target User ID:"
|
||||
text = "Target:"
|
||||
|
||||
[node name="TargetUserEdit" type="LineEdit" parent="Margin/VBox/Tabs/Announcements/TargetHBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Leave empty to send to ALL users (Global)"
|
||||
placeholder_text = "Username or User ID (empty = ALL)"
|
||||
|
||||
[node name="FindUserBtn" type="Button" parent="Margin/VBox/Tabs/Announcements/TargetHBox"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
text = "Find"
|
||||
|
||||
[node name="ResolvedIdLabel" type="Label" parent="Margin/VBox/Tabs/Announcements/TargetHBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.4, 0.9, 0.4, 1)
|
||||
text = ""
|
||||
|
||||
[node name="TitleEdit" type="LineEdit" parent="Margin/VBox/Tabs/Announcements"]
|
||||
unique_name_in_owner = true
|
||||
@@ -376,6 +388,54 @@ custom_minimum_size = Vector2(0, 40)
|
||||
layout_mode = 2
|
||||
text = "SEND ANNOUNCEMENT"
|
||||
|
||||
[node name="Mail Manager" type="VBoxContainer" parent="Margin/VBox/Tabs"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
metadata/_tab_index = 4
|
||||
|
||||
[node name="MailTree" type="Tree" parent="Margin/VBox/Tabs/Mail Manager"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
columns = 6
|
||||
column_titles_visible = true
|
||||
allow_reselect = true
|
||||
hide_root = true
|
||||
select_mode = 1
|
||||
|
||||
[node name="MailActionBar" type="HBoxContainer" parent="Margin/VBox/Tabs/Mail Manager"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="RefreshMailBtn" type="Button" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 36)
|
||||
layout_mode = 2
|
||||
text = "Refresh"
|
||||
|
||||
[node name="Spacer" type="Control" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="EditMailBtn" type="Button" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 36)
|
||||
layout_mode = 2
|
||||
text = "Edit"
|
||||
|
||||
[node name="EndMailBtn" type="Button" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(120, 36)
|
||||
layout_mode = 2
|
||||
text = "End Now"
|
||||
|
||||
[node name="DeleteMailServerBtn" type="Button" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(120, 36)
|
||||
layout_mode = 2
|
||||
text = "Delete"
|
||||
|
||||
[node name="StatusLabel" type="Label" parent="Margin/VBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
+196
-88
@@ -1,6 +1,39 @@
|
||||
[gd_scene format=3 uid="uid://cb5cbbxyxx"]
|
||||
[gd_scene format=3 uid="uid://5e1bfpagcpps"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://df7xxyyzz" path="res://scripts/ui/mailbox_panel.gd" id="1_a"]
|
||||
[ext_resource type="Script" uid="uid://b5fema68m6b2s" path="res://scripts/ui/mailbox_panel.gd" id="1_a"]
|
||||
[ext_resource type="Theme" uid="uid://cxab3xxy00" path="res://assets/themes/GUI_Tekton.tres" id="1_wi8mn"]
|
||||
[ext_resource type="Texture2D" uid="uid://dfmailbox" path="res://assets/graphics/gui/mainmenu/mailbox.png" id="tex_mailbox"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_middle"]
|
||||
bg_color = Color(0.02, 0.04, 0.08, 1)
|
||||
border_width_left = 3
|
||||
border_width_top = 3
|
||||
border_width_right = 3
|
||||
border_width_bottom = 3
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
corner_radius_top_left = 12
|
||||
corner_radius_top_right = 12
|
||||
corner_radius_bottom_right = 12
|
||||
corner_radius_bottom_left = 12
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_slot"]
|
||||
bg_color = Color(0.1, 0.3, 0.6, 0.6)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mailbtn"]
|
||||
bg_color = Color(0.2, 0.4, 0.6, 1)
|
||||
border_width_left = 2
|
||||
border_width_top = 2
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[node name="MailboxPanel" type="Panel"]
|
||||
anchors_preset = 15
|
||||
@@ -8,6 +41,7 @@ anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme = ExtResource("1_wi8mn")
|
||||
script = ExtResource("1_a")
|
||||
|
||||
[node name="BG" type="ColorRect" parent="."]
|
||||
@@ -17,7 +51,7 @@ anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.08, 0.15, 0.22, 1)
|
||||
color = Color(0.12, 0.4, 0.9, 1)
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="."]
|
||||
layout_mode = 1
|
||||
@@ -26,45 +60,51 @@ anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 24
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 24
|
||||
theme_override_constants/margin_left = 40
|
||||
theme_override_constants/margin_top = 40
|
||||
theme_override_constants/margin_right = 40
|
||||
theme_override_constants/margin_bottom = 40
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="Margin"]
|
||||
[node name="VBox" type="VBoxContainer" parent="Margin"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="LeftCol" type="VBoxContainer" parent="Margin/HBox"]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="HeaderHBox" type="HBoxContainer" parent="Margin/HBox/LeftCol"]
|
||||
[node name="HeaderHBox" type="HBoxContainer" parent="Margin/VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Icon" type="TextureRect" parent="Margin/HBox/LeftCol/HeaderHBox"]
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
[node name="Icon" type="TextureRect" parent="Margin/VBox/HeaderHBox"]
|
||||
custom_minimum_size = Vector2(48, 48)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("tex_mailbox")
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="Label" type="Label" parent="Margin/HBox/LeftCol/HeaderHBox"]
|
||||
[node name="Label" type="Label" parent="Margin/VBox/HeaderHBox"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 42
|
||||
text = "MAILBOX"
|
||||
theme_override_font_sizes/font_size = 24
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Margin/HBox/LeftCol"]
|
||||
[node name="HBox" type="HBoxContainer" parent="Margin/VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="LeftCol" type="VBoxContainer" parent="Margin/VBox/HBox"]
|
||||
custom_minimum_size = Vector2(350, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Margin/VBox/HBox/LeftCol"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="MailListVBox" type="VBoxContainer" parent="Margin/HBox/LeftCol/Scroll"]
|
||||
[node name="MailListVBox" type="VBoxContainer" parent="Margin/VBox/HBox/LeftCol/Scroll"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 8
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="EmptyStateLbl" type="Label" parent="Margin/HBox/LeftCol/Scroll"]
|
||||
[node name="EmptyStateLbl" type="Label" parent="Margin/VBox/HBox/LeftCol/Scroll"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
@@ -73,102 +113,170 @@ size_flags_vertical = 6
|
||||
text = "No mails found."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="CloseBtn" type="Button" parent="Margin/HBox/LeftCol"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
text = "BACK"
|
||||
|
||||
[node name="MailBtnTemplate" type="Button" parent="Margin/HBox/LeftCol"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 80)
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Margin/HBox/LeftCol/MailBtnTemplate"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 8
|
||||
theme_override_constants/margin_right = 8
|
||||
|
||||
[node name="Title" type="Label" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 18
|
||||
text = "No Title"
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DateLbl" type="Label" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox/HBox"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.745098, 0.745098, 0.745098, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
|
||||
[node name="Spacer" type="Control" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox/HBox"]
|
||||
[node name="MiddleCol" type="PanelContainer" parent="Margin/VBox/HBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_middle")
|
||||
|
||||
[node name="StatusLbl" type="Label" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox/HBox"]
|
||||
[node name="Margin" type="MarginContainer" parent="Margin/VBox/HBox/MiddleCol"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 12
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 24
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 24
|
||||
|
||||
[node name="RightCol" type="VBoxContainer" parent="Margin/HBox"]
|
||||
[node name="VBox" type="VBoxContainer" parent="Margin/VBox/HBox/MiddleCol/Margin"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="MailTitleLbl" type="Label" parent="Margin/HBox/RightCol"]
|
||||
[node name="MailTitleLbl" type="Label" parent="Margin/VBox/HBox/MiddleCol/Margin/VBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 28
|
||||
text = "WELCOME TO TEKTONIA"
|
||||
theme_override_font_sizes/font_size = 32
|
||||
text = "Message Title"
|
||||
|
||||
[node name="Sep" type="HSeparator" parent="Margin/HBox/RightCol"]
|
||||
[node name="Sep" type="HSeparator" parent="Margin/VBox/HBox/MiddleCol/Margin/VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MailContentText" type="RichTextLabel" parent="Margin/HBox/RightCol"]
|
||||
[node name="MailContentText" type="RichTextLabel" parent="Margin/VBox/HBox/MiddleCol/Margin/VBox"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/normal_font_size = 18
|
||||
text = "Lorem ipsum..."
|
||||
|
||||
[node name="FooterHBox" type="HBoxContainer" parent="Margin/HBox/RightCol"]
|
||||
[node name="RightCol" type="VBoxContainer" parent="Margin/VBox/HBox"]
|
||||
custom_minimum_size = Vector2(250, 0)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="DynamicRewardsContainer" type="VBoxContainer" parent="Margin/VBox/HBox/RightCol"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="FooterHBox" type="HBoxContainer" parent="Margin/VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SenderLbl" type="Label" parent="Margin/HBox/RightCol/FooterHBox"]
|
||||
[node name="CloseBtn" type="Button" parent="Margin/VBox/FooterHBox"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(120, 50)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
text = "< BACK"
|
||||
|
||||
[node name="ReadAllBtn" type="Button" parent="Margin/VBox/FooterHBox"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(160, 50)
|
||||
layout_mode = 2
|
||||
text = "READ ALL"
|
||||
|
||||
[node name="Spacer" type="Control" parent="Margin/VBox/FooterHBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "SENDER:
|
||||
TEKTON DEV TEAM"
|
||||
|
||||
[node name="DynamicRewardsContainer" type="HBoxContainer" parent="Margin/HBox/RightCol/FooterHBox"]
|
||||
[node name="ActionBtn" type="Button" parent="Margin/VBox/FooterHBox"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(180, 50)
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
text = "DELETE"
|
||||
|
||||
[node name="Templates" type="Control" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 0
|
||||
|
||||
[node name="MailBtnTemplate" type="Button" parent="Templates"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 100)
|
||||
layout_mode = 0
|
||||
toggle_mode = true
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_mailbtn")
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="Templates/MailBtnTemplate"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 12
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 12
|
||||
theme_override_constants/margin_bottom = 12
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Templates/MailBtnTemplate/Margin"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Title" type="Label" parent="Templates/MailBtnTemplate/Margin/VBox"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 22
|
||||
text = "Message Title"
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[node name="Spacer" type="Control" parent="Templates/MailBtnTemplate/Margin/VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="Templates/MailBtnTemplate/Margin/VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DateLbl" type="Label" parent="Templates/MailBtnTemplate/Margin/VBox/HBox"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0.9, 0.9, 0.9, 1)
|
||||
theme_override_font_sizes/font_size = 14
|
||||
|
||||
[node name="Spacer" type="Control" parent="Templates/MailBtnTemplate/Margin/VBox/HBox"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="StatusLbl" type="Label" parent="Templates/MailBtnTemplate/Margin/VBox/HBox"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 14
|
||||
|
||||
[node name="RewardHBoxTemplate" type="PanelContainer" parent="Templates"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 80)
|
||||
layout_mode = 0
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_slot")
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="Templates/RewardHBoxTemplate"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 12
|
||||
theme_override_constants/margin_top = 12
|
||||
theme_override_constants/margin_right = 12
|
||||
theme_override_constants/margin_bottom = 12
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="Templates/RewardHBoxTemplate/Margin"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 12
|
||||
|
||||
[node name="RewardHBoxTemplate" type="HBoxContainer" parent="Margin/HBox/RightCol/FooterHBox"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
[node name="IconBg" type="ColorRect" parent="Templates/RewardHBoxTemplate/Margin/HBox"]
|
||||
custom_minimum_size = Vector2(56, 56)
|
||||
layout_mode = 2
|
||||
color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Icon" type="TextureRect" parent="Margin/HBox/RightCol/FooterHBox/RewardHBoxTemplate"]
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
layout_mode = 2
|
||||
[node name="Icon" type="TextureRect" parent="Templates/RewardHBoxTemplate/Margin/HBox/IconBg"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="AmountLbl" type="Label" parent="Margin/HBox/RightCol/FooterHBox/RewardHBoxTemplate"]
|
||||
[node name="VBox" type="VBoxContainer" parent="Templates/RewardHBoxTemplate/Margin/HBox"]
|
||||
layout_mode = 2
|
||||
text = "100"
|
||||
alignment = 1
|
||||
|
||||
[node name="ActionBtn" type="Button" parent="Margin/HBox/RightCol/FooterHBox"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(120, 48)
|
||||
[node name="TypeLbl" type="Label" parent="Templates/RewardHBoxTemplate/Margin/HBox/VBox"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(1, 0.2, 0.2, 1)
|
||||
text = "DELETE"
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "ITEM NAME"
|
||||
|
||||
[node name="AmountLbl" type="Label" parent="Templates/RewardHBoxTemplate/Margin/HBox/VBox"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = "x00000"
|
||||
|
||||
@@ -1,53 +1,16 @@
|
||||
[gd_scene format=3 uid="uid://b1two2tvv5prx"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cdege6m8u5cp" path="res://scripts/ui/settings_menu.gd" id="1_script"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bg"]
|
||||
bg_color = Color(0.05, 0.05, 0.08, 0.85)
|
||||
border_width_left = 2
|
||||
border_width_top = 2
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0.2, 0.8, 1, 0.5)
|
||||
corner_radius_top_left = 20
|
||||
corner_radius_top_right = 20
|
||||
corner_radius_bottom_right = 20
|
||||
corner_radius_bottom_left = 20
|
||||
shadow_color = Color(0, 0, 0, 0.5)
|
||||
shadow_size = 20
|
||||
[ext_resource type="Theme" uid="uid://cxab3xxy00" path="res://assets/themes/GUI_Tekton.tres" id="2_theme"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_title"]
|
||||
font_size = 32
|
||||
font_color = Color(0.2, 0.9, 1, 1)
|
||||
outline_size = 8
|
||||
outline_color = Color(0, 0, 0, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tab_bg"]
|
||||
content_margin_left = 20.0
|
||||
content_margin_top = 20.0
|
||||
content_margin_right = 20.0
|
||||
content_margin_bottom = 20.0
|
||||
bg_color = Color(0.1, 0.1, 0.15, 0.6)
|
||||
corner_radius_top_left = 10
|
||||
corner_radius_top_right = 10
|
||||
corner_radius_bottom_right = 10
|
||||
corner_radius_bottom_left = 10
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_heading"]
|
||||
font_size = 24
|
||||
font_color = Color(0.9, 0.9, 0.9, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_section_header"]
|
||||
content_margin_left = 15.0
|
||||
content_margin_top = 5.0
|
||||
content_margin_right = 15.0
|
||||
content_margin_bottom = 5.0
|
||||
bg_color = Color(0.2, 0.5, 0.8, 0.3)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_section"]
|
||||
font_size = 18
|
||||
font_color = Color(0.4, 0.9, 1, 1)
|
||||
@@ -79,47 +42,41 @@ offset_right = 400.0
|
||||
offset_bottom = 300.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_bg")
|
||||
theme = ExtResource("2_theme")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer" unique_id=1608766484]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="Header" type="HBoxContainer" parent="PanelContainer/VBoxContainer" unique_id=1476625293]
|
||||
custom_minimum_size = Vector2(0, 80)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="PanelContainer" type="Control" parent="PanelContainer/VBoxContainer/Header" unique_id=1523545144]
|
||||
custom_minimum_size = Vector2(30, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer/Header" unique_id=22122987]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/margin_left = 30
|
||||
|
||||
[node name="Title" type="Label" parent="PanelContainer/VBoxContainer/Header/MarginContainer" unique_id=1831070364]
|
||||
layout_mode = 2
|
||||
text = "SYSTEM SETTINGS"
|
||||
text = "SETTINGS"
|
||||
label_settings = SubResource("LabelSettings_title")
|
||||
|
||||
[node name="CloseButton" type="Button" parent="PanelContainer/VBoxContainer/Header" unique_id=1345773906]
|
||||
custom_minimum_size = Vector2(60, 60)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
theme_override_font_sizes/font_size = 32
|
||||
text = "X"
|
||||
flat = true
|
||||
|
||||
[node name="ContentSection" type="MarginContainer" parent="PanelContainer/VBoxContainer" unique_id=1702280876]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 20
|
||||
theme_override_constants/margin_top = 0
|
||||
theme_override_constants/margin_right = 20
|
||||
theme_override_constants/margin_bottom = 30
|
||||
|
||||
[node name="TabContainer" type="TabContainer" parent="PanelContainer/VBoxContainer/ContentSection" unique_id=1795678842]
|
||||
layout_mode = 2
|
||||
theme_override_constants/side_margin = 20
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_tab_bg")
|
||||
current_tab = 0
|
||||
|
||||
[node name="Video" type="ScrollContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer" unique_id=964480166]
|
||||
@@ -129,7 +86,6 @@ metadata/_tab_index = 0
|
||||
[node name="VBox" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Video" unique_id=1271648419]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 15
|
||||
|
||||
[node name="Fullscreen" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Video/VBox" unique_id=683482011]
|
||||
layout_mode = 2
|
||||
@@ -216,7 +172,6 @@ layout_mode = 2
|
||||
[node name="Audio" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer" unique_id=1585166620]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
metadata/_tab_index = 1
|
||||
|
||||
[node name="Master" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Audio" unique_id=2083739941]
|
||||
@@ -272,7 +227,6 @@ metadata/_tab_index = 2
|
||||
[node name="VBox" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls" unique_id=1592525164]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 15
|
||||
|
||||
[node name="ControllerToggleContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000001]
|
||||
layout_mode = 2
|
||||
@@ -289,7 +243,6 @@ layout_mode = 2
|
||||
|
||||
[node name="MovementSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000004]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_section_header")
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/MovementSection" unique_id=100000005]
|
||||
layout_mode = 2
|
||||
@@ -298,8 +251,6 @@ label_settings = SubResource("LabelSettings_section")
|
||||
|
||||
[node name="MoveGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000006]
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 20
|
||||
theme_override_constants/v_separation = 10
|
||||
columns = 3
|
||||
|
||||
[node name="ColH1" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/MoveGrid" unique_id=393496035]
|
||||
@@ -391,7 +342,6 @@ text = "Right"
|
||||
|
||||
[node name="PowerUpSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000015]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_section_header")
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/PowerUpSection" unique_id=100000016]
|
||||
layout_mode = 2
|
||||
@@ -400,8 +350,6 @@ label_settings = SubResource("LabelSettings_section")
|
||||
|
||||
[node name="PowerUpGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000017]
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 20
|
||||
theme_override_constants/v_separation = 10
|
||||
columns = 3
|
||||
|
||||
[node name="UsePowerupLabel" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/PowerUpGrid" unique_id=100000118]
|
||||
@@ -424,7 +372,6 @@ text = "N/A"
|
||||
|
||||
[node name="PowerBarSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000026]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_section_header")
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/PowerBarSection" unique_id=100000027]
|
||||
layout_mode = 2
|
||||
@@ -433,8 +380,6 @@ label_settings = SubResource("LabelSettings_section")
|
||||
|
||||
[node name="PowerBarGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000028]
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 20
|
||||
theme_override_constants/v_separation = 10
|
||||
columns = 3
|
||||
|
||||
[node name="AttackLabel" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/PowerBarGrid" unique_id=100000029]
|
||||
@@ -475,7 +420,6 @@ text = "N/A"
|
||||
|
||||
[node name="OtherSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000033]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_section_header")
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/OtherSection" unique_id=100000034]
|
||||
layout_mode = 2
|
||||
@@ -484,8 +428,6 @@ label_settings = SubResource("LabelSettings_section")
|
||||
|
||||
[node name="OtherGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000035]
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 20
|
||||
theme_override_constants/v_separation = 10
|
||||
columns = 3
|
||||
|
||||
[node name="GrabLabel" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/OtherGrid" unique_id=100000036]
|
||||
@@ -514,11 +456,9 @@ metadata/_tab_index = 3
|
||||
[node name="VBox" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller" unique_id=300000002]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 15
|
||||
|
||||
[node name="CtrlInfoSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller/VBox" unique_id=300000003]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_section_header")
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller/VBox/CtrlInfoSection" unique_id=300000004]
|
||||
layout_mode = 2
|
||||
@@ -527,8 +467,6 @@ label_settings = SubResource("LabelSettings_section")
|
||||
|
||||
[node name="CtrlGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller/VBox" unique_id=300000005]
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 20
|
||||
theme_override_constants/v_separation = 10
|
||||
columns = 2
|
||||
|
||||
[node name="ColH1" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller/VBox/CtrlGrid" unique_id=300000006]
|
||||
@@ -606,11 +544,9 @@ metadata/_tab_index = 4
|
||||
[node name="VBox" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput" unique_id=200000002]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="JoystickSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox" unique_id=200000003]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_section_header")
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox/JoystickSection" unique_id=200000004]
|
||||
layout_mode = 2
|
||||
@@ -619,7 +555,6 @@ label_settings = SubResource("LabelSettings_section")
|
||||
|
||||
[node name="JoystickSizeContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox" unique_id=200000005]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="JoystickSizeHeader" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox/JoystickSizeContainer" unique_id=200000006]
|
||||
layout_mode = 2
|
||||
@@ -662,7 +597,6 @@ button_pressed = true
|
||||
|
||||
[node name="ButtonsSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox" unique_id=200000013]
|
||||
layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_section_header")
|
||||
|
||||
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox/ButtonsSection" unique_id=200000014]
|
||||
layout_mode = 2
|
||||
@@ -671,7 +605,6 @@ label_settings = SubResource("LabelSettings_section")
|
||||
|
||||
[node name="ButtonOpacityContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox" unique_id=200000015]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="ButtonOpacityHeader" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox/ButtonOpacityContainer" unique_id=200000016]
|
||||
layout_mode = 2
|
||||
|
||||
@@ -104,6 +104,8 @@ size_flags_horizontal = 3
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_tab_inactive")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_tab_inactive")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxFlat_tab_active")
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
text = "FIND FRIEND"
|
||||
|
||||
[node name="FriendsTabBtn" type="Button" parent="Panel/Margin/VBox/TabBar" unique_id=1723617099]
|
||||
@@ -111,9 +113,10 @@ unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_tab_active")
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_tab_inactive")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_tab_inactive")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxFlat_tab_active")
|
||||
toggle_mode = true
|
||||
text = "FRIENDS"
|
||||
|
||||
[node name="HSep1" type="HSeparator" parent="Panel/Margin/VBox" unique_id=549111536]
|
||||
|
||||
@@ -90,7 +90,52 @@ func delete_mail(mail_id: String) -> bool:
|
||||
|
||||
func mark_as_read(mail_id: String) -> void:
|
||||
if mail_id in read_ids: return
|
||||
# Since there's no specific RPC for just marking as read, we can just do a claim with 0 rewards if needed,
|
||||
# or add an RPC for it. For now we just let claim or delete handle the server-side state.
|
||||
# Let's add an empty claim or just handle it purely local until claimed/deleted if it has no rewards.
|
||||
pass
|
||||
read_ids.append(mail_id)
|
||||
_update_unread_count()
|
||||
# Persist read state to server via delete_mail RPC pattern (just saves state)
|
||||
_save_inbox_state()
|
||||
|
||||
func _save_inbox_state() -> void:
|
||||
if not NakamaManager.session: return
|
||||
var state_payload = {
|
||||
"claimed_ids": claimed_ids,
|
||||
"deleted_ids": [],
|
||||
"read_ids": read_ids
|
||||
}
|
||||
# We use storage write via a lightweight RPC or direct storage
|
||||
var r = await NakamaManager.client.rpc_async(
|
||||
NakamaManager.session, "save_mail_state",
|
||||
JSON.stringify(state_payload)
|
||||
)
|
||||
if r.is_exception():
|
||||
push_warning("[MailManager] Could not save mail state: " + r.get_exception().message)
|
||||
|
||||
func read_all_and_claim_all() -> void:
|
||||
"""Mark all mails as read and claim all unclaimed rewards."""
|
||||
if mails.is_empty(): return
|
||||
|
||||
# Mark all as read
|
||||
for mail in mails:
|
||||
var mid = mail.get("id", "")
|
||||
if mid not in read_ids:
|
||||
read_ids.append(mid)
|
||||
|
||||
# Claim all unclaimed rewards
|
||||
var to_claim: Array = []
|
||||
for mail in mails:
|
||||
var mid = mail.get("id", "")
|
||||
if mid in claimed_ids: continue
|
||||
var rewards = mail.get("rewards", [])
|
||||
var has_rewards = false
|
||||
if typeof(rewards) == TYPE_DICTIONARY:
|
||||
has_rewards = rewards.get("star", 0) > 0 or rewards.get("gold", 0) > 0
|
||||
elif typeof(rewards) == TYPE_ARRAY:
|
||||
has_rewards = rewards.size() > 0
|
||||
if has_rewards:
|
||||
to_claim.append(mid)
|
||||
|
||||
for mid in to_claim:
|
||||
await claim_reward(mid)
|
||||
|
||||
_update_unread_count()
|
||||
mail_updated.emit()
|
||||
|
||||
+365
-2
@@ -38,6 +38,8 @@ var _current_dr_month: String = ""
|
||||
|
||||
# Tab: Announcements
|
||||
@onready var target_user_edit := %TargetUserEdit as LineEdit
|
||||
@onready var find_user_btn := %FindUserBtn as Button
|
||||
@onready var resolved_id_label := %ResolvedIdLabel as Label
|
||||
@onready var title_edit := %TitleEdit as LineEdit
|
||||
@onready var content_edit := %ContentEdit as TextEdit
|
||||
@onready var start_date_edit := %StartDatePicker as Button
|
||||
@@ -47,6 +49,18 @@ var _current_dr_month: String = ""
|
||||
@onready var reward_row_template := %RewardRowTemplate as HBoxContainer
|
||||
@onready var send_mail_btn := %SendMailBtn as Button
|
||||
|
||||
var _resolved_user_id: String = ""
|
||||
|
||||
# Tab: Mail Manager
|
||||
@onready var mail_tree := %MailTree as Tree
|
||||
@onready var refresh_mail_btn := %RefreshMailBtn as Button
|
||||
@onready var edit_mail_btn := %EditMailBtn as Button
|
||||
@onready var end_mail_btn := %EndMailBtn as Button
|
||||
@onready var delete_mail_server_btn := %DeleteMailServerBtn as Button
|
||||
|
||||
var _mail_root: TreeItem
|
||||
var _all_server_mails: Array = []
|
||||
|
||||
const MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
||||
|
||||
# -- Data --
|
||||
@@ -140,6 +154,19 @@ func _setup_columns() -> void:
|
||||
lb_tree.set_column_custom_minimum_width(5, 60)
|
||||
_lb_root = lb_tree.create_item()
|
||||
|
||||
# Mail Manager
|
||||
mail_tree.set_column_title(0, "Type")
|
||||
mail_tree.set_column_title(1, "Title")
|
||||
mail_tree.set_column_title(2, "Sender")
|
||||
mail_tree.set_column_title(3, "Start")
|
||||
mail_tree.set_column_title(4, "Expires")
|
||||
mail_tree.set_column_title(5, "Status")
|
||||
mail_tree.set_column_custom_minimum_width(0, 100)
|
||||
mail_tree.set_column_expand(0, false)
|
||||
mail_tree.set_column_custom_minimum_width(5, 80)
|
||||
mail_tree.set_column_expand(5, false)
|
||||
_mail_root = mail_tree.create_item()
|
||||
|
||||
func _connect_signals() -> void:
|
||||
close_btn.pressed.connect(_on_close)
|
||||
refresh_btn.pressed.connect(_on_refresh)
|
||||
@@ -166,6 +193,16 @@ func _connect_signals() -> void:
|
||||
# Announcement actions
|
||||
send_mail_btn.pressed.connect(_on_send_mail)
|
||||
add_reward_btn.pressed.connect(_on_add_reward_pressed)
|
||||
find_user_btn.pressed.connect(_on_find_user)
|
||||
target_user_edit.text_changed.connect(func(_t): _resolved_user_id = ""; resolved_id_label.text = "")
|
||||
|
||||
# Mail Manager actions
|
||||
refresh_mail_btn.pressed.connect(func(): await _load_mail())
|
||||
mail_tree.item_selected.connect(_on_mail_item_selected)
|
||||
edit_mail_btn.pressed.connect(_on_edit_mail_pressed)
|
||||
end_mail_btn.pressed.connect(_on_end_mail_pressed)
|
||||
delete_mail_server_btn.pressed.connect(_on_delete_mail_server_pressed)
|
||||
_update_mail_action_btns(null)
|
||||
|
||||
# =============================================================================
|
||||
# Core Panel Logic
|
||||
@@ -189,6 +226,8 @@ func _on_tab_changed(tab_index: int) -> void:
|
||||
await _load_leaderboard()
|
||||
elif tab_index == 2:
|
||||
await _load_daily_rewards_config()
|
||||
elif tab_index == 4:
|
||||
await _load_mail()
|
||||
|
||||
# =============================================================================
|
||||
# RPC Helper
|
||||
@@ -649,8 +688,60 @@ func _on_add_reward_pressed() -> void:
|
||||
var remove_btn = row.get_node("RemoveBtn") as Button
|
||||
remove_btn.pressed.connect(func(): row.queue_free())
|
||||
|
||||
func _on_find_user() -> void:
|
||||
var input = target_user_edit.text.strip_edges()
|
||||
if input.is_empty():
|
||||
_resolved_user_id = ""
|
||||
resolved_id_label.text = "(Global — all users)"
|
||||
return
|
||||
|
||||
_set_status("Looking up user...")
|
||||
var uid = await _resolve_target_user_id(input)
|
||||
if uid.is_empty():
|
||||
resolved_id_label.text = "NOT FOUND"
|
||||
resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_ERR)
|
||||
_set_status("User not found: " + input, CLR_STATUS_ERR)
|
||||
else:
|
||||
_resolved_user_id = uid
|
||||
resolved_id_label.text = "ID: " + uid.substr(0, 12) + "..."
|
||||
resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
_set_status("Found user: " + uid, CLR_STATUS_OK)
|
||||
|
||||
func _resolve_target_user_id(input: String) -> String:
|
||||
"""Resolve a username, display_name, or user_id to a user_id.
|
||||
Returns empty string if not found."""
|
||||
if input.is_empty():
|
||||
return ""
|
||||
|
||||
# If it looks like a UUID already, return as-is
|
||||
if input.length() >= 32 and "-" in input:
|
||||
return input
|
||||
|
||||
# Search in cached all_users first
|
||||
for u in all_users:
|
||||
var uname: String = u.get("username", "")
|
||||
var dname: String = u.get("display_name", "")
|
||||
var uid: String = u.get("user_id", "")
|
||||
if uname.to_lower() == input.to_lower() or dname.to_lower() == input.to_lower():
|
||||
return uid
|
||||
|
||||
# Cache miss — fetch from server
|
||||
var res := await _rpc("admin_list_users", {})
|
||||
if res.has("error"):
|
||||
return ""
|
||||
all_users = res.get("users", [])
|
||||
|
||||
for u in all_users:
|
||||
var uname: String = u.get("username", "")
|
||||
var dname: String = u.get("display_name", "")
|
||||
var uid: String = u.get("user_id", "")
|
||||
if uname.to_lower() == input.to_lower() or dname.to_lower() == input.to_lower():
|
||||
return uid
|
||||
|
||||
return ""
|
||||
|
||||
func _on_send_mail() -> void:
|
||||
var target = target_user_edit.text.strip_edges()
|
||||
var input = target_user_edit.text.strip_edges()
|
||||
var title = title_edit.text.strip_edges()
|
||||
var content = content_edit.text.strip_edges()
|
||||
var start_date = start_date_edit.get_date_iso()
|
||||
@@ -659,6 +750,21 @@ func _on_send_mail() -> void:
|
||||
if title.is_empty() or content.is_empty():
|
||||
_set_status("Title and content cannot be empty", CLR_STATUS_ERR)
|
||||
return
|
||||
|
||||
# Resolve target
|
||||
var target_uid := ""
|
||||
if not input.is_empty():
|
||||
if not _resolved_user_id.is_empty():
|
||||
target_uid = _resolved_user_id
|
||||
else:
|
||||
_set_status("Resolving user...")
|
||||
target_uid = await _resolve_target_user_id(input)
|
||||
if target_uid.is_empty():
|
||||
_set_status("User not found: " + input + ". Use Find button.", CLR_STATUS_ERR)
|
||||
return
|
||||
_resolved_user_id = target_uid
|
||||
resolved_id_label.text = "ID: " + target_uid.substr(0, 12) + "..."
|
||||
resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
|
||||
var rewards_arr = []
|
||||
for child in rewards_list.get_children():
|
||||
@@ -679,7 +785,7 @@ func _on_send_mail() -> void:
|
||||
_set_status("Sending mail...")
|
||||
|
||||
var payload = {
|
||||
"target_user_id": target,
|
||||
"target_user_id": target_uid,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"start_date": start_date,
|
||||
@@ -694,6 +800,9 @@ func _on_send_mail() -> void:
|
||||
_set_status("Failed to send mail: " + res.get("error", "Unknown"), CLR_STATUS_ERR)
|
||||
else:
|
||||
_set_status("Mail sent successfully!", CLR_STATUS_OK)
|
||||
target_user_edit.text = ""
|
||||
_resolved_user_id = ""
|
||||
resolved_id_label.text = ""
|
||||
title_edit.text = ""
|
||||
content_edit.text = ""
|
||||
if start_date_edit.has_method("clear_date"):
|
||||
@@ -702,3 +811,257 @@ func _on_send_mail() -> void:
|
||||
end_date_edit.clear_date()
|
||||
for child in rewards_list.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
|
||||
# =============================================================================
|
||||
# TAB 5: MAIL MANAGER
|
||||
# =============================================================================
|
||||
func _load_mail() -> void:
|
||||
_clear_tree(mail_tree, _mail_root)
|
||||
_set_status("Loading mails...")
|
||||
|
||||
var res := await _rpc("admin_list_mail", {})
|
||||
if res.has("error"):
|
||||
_set_status("Failed: " + str(res.error), CLR_STATUS_ERR)
|
||||
return
|
||||
|
||||
_all_server_mails = res.get("mails", [])
|
||||
count_label.text = "%d mails" % _all_server_mails.size()
|
||||
|
||||
var now_str = Time.get_datetime_string_from_system(true)
|
||||
|
||||
for mail in _all_server_mails:
|
||||
var item := _mail_root.create_child()
|
||||
var mail_type: String = mail.get("type", "global")
|
||||
var mail_title: String = mail.get("title", "No Title")
|
||||
var sender: String = mail.get("sender", "SYSTEM")
|
||||
var start_date: String = mail.get("start_date", "")
|
||||
var expiry: String = mail.get("expiry_date", "")
|
||||
|
||||
# Type column
|
||||
item.set_text(0, mail_type.to_upper())
|
||||
if mail_type == "personal":
|
||||
item.set_custom_color(0, CLR_ADMIN)
|
||||
else:
|
||||
item.set_custom_color(0, CLR_MOD)
|
||||
|
||||
# Title
|
||||
item.set_text(1, mail_title)
|
||||
|
||||
# Sender
|
||||
item.set_text(2, sender)
|
||||
|
||||
# Start date
|
||||
item.set_text(3, start_date.substr(0, 10) if start_date.length() >= 10 else start_date)
|
||||
|
||||
# Expiry
|
||||
var expiry_short = expiry.substr(0, 10) if expiry.length() >= 10 else expiry
|
||||
item.set_text(4, expiry_short)
|
||||
|
||||
# Status
|
||||
var end_date: String = mail.get("end_date", "")
|
||||
var status := "ACTIVE"
|
||||
if not end_date.is_empty() and now_str > end_date:
|
||||
status = "ENDED"
|
||||
item.set_custom_color(5, CLR_BTN_DEL)
|
||||
elif not expiry.is_empty() and now_str > expiry:
|
||||
status = "EXPIRED"
|
||||
item.set_custom_color(5, CLR_BTN_DEL)
|
||||
else:
|
||||
item.set_custom_color(5, CLR_STATUS_OK)
|
||||
item.set_text(5, status)
|
||||
|
||||
item.set_metadata(0, mail)
|
||||
|
||||
_update_mail_action_btns(null)
|
||||
_set_status("")
|
||||
|
||||
func _on_mail_item_selected() -> void:
|
||||
var item = mail_tree.get_selected()
|
||||
_update_mail_action_btns(item)
|
||||
|
||||
func _update_mail_action_btns(item) -> void:
|
||||
var has_sel = item != null
|
||||
edit_mail_btn.disabled = not has_sel
|
||||
end_mail_btn.disabled = not has_sel
|
||||
delete_mail_server_btn.disabled = not has_sel
|
||||
|
||||
func _get_selected_mail() -> Dictionary:
|
||||
var item = mail_tree.get_selected()
|
||||
if item:
|
||||
return item.get_metadata(0)
|
||||
return {}
|
||||
|
||||
func _on_edit_mail_pressed() -> void:
|
||||
var mail = _get_selected_mail()
|
||||
if mail.is_empty(): return
|
||||
_show_edit_mail_dialog(mail)
|
||||
|
||||
func _show_edit_mail_dialog(mail: Dictionary) -> void:
|
||||
var mail_id: String = mail.get("id", "")
|
||||
var mail_type: String = mail.get("type", "global")
|
||||
var target_uid: String = mail.get("target_user_id", "")
|
||||
|
||||
var dialog := AcceptDialog.new()
|
||||
dialog.title = "Edit Mail: " + mail.get("title", "")
|
||||
dialog.min_size = Vector2i(480, 360)
|
||||
var vbox := VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 10)
|
||||
|
||||
var id_lbl := Label.new()
|
||||
id_lbl.text = "ID: " + mail_id + " | Type: " + mail_type
|
||||
id_lbl.add_theme_color_override("font_color", CLR_DIM)
|
||||
vbox.add_child(id_lbl)
|
||||
|
||||
var grid := GridContainer.new()
|
||||
grid.columns = 2
|
||||
grid.add_theme_constant_override("h_separation", 8)
|
||||
grid.add_theme_constant_override("v_separation", 8)
|
||||
|
||||
var title_lbl := Label.new(); title_lbl.text = "Title:"; grid.add_child(title_lbl)
|
||||
var title_input := LineEdit.new(); title_input.text = mail.get("title", ""); title_input.custom_minimum_size.x = 300; grid.add_child(title_input)
|
||||
|
||||
var content_lbl := Label.new(); content_lbl.text = "Content:"; grid.add_child(content_lbl)
|
||||
var content_input := TextEdit.new(); content_input.text = mail.get("content", ""); content_input.custom_minimum_size = Vector2(300, 100); grid.add_child(content_input)
|
||||
|
||||
var end_lbl := Label.new(); end_lbl.text = "End Date:"; grid.add_child(end_lbl)
|
||||
var end_picker: Button = load("res://scenes/ui/date_picker.tscn").instantiate()
|
||||
end_picker.custom_minimum_size.x = 200
|
||||
grid.add_child(end_picker)
|
||||
# Pre-populate if existing end_date
|
||||
var existing_end: String = mail.get("end_date", "")
|
||||
if not existing_end.is_empty():
|
||||
var parts = existing_end.substr(0, 10).split("-")
|
||||
if parts.size() == 3:
|
||||
end_picker.current_date = {"year": int(parts[0]), "month": int(parts[1]), "day": int(parts[2])}
|
||||
end_picker.view_date = end_picker.current_date.duplicate()
|
||||
end_picker.text = existing_end.substr(0, 10)
|
||||
|
||||
# Recipient row
|
||||
var recip_lbl := Label.new(); recip_lbl.text = "Recipient:"; grid.add_child(recip_lbl)
|
||||
var recip_hbox := HBoxContainer.new()
|
||||
recip_hbox.add_theme_constant_override("separation", 6)
|
||||
var recip_input := LineEdit.new()
|
||||
recip_input.custom_minimum_size.x = 180
|
||||
recip_input.placeholder_text = "Username or ID (empty = global)"
|
||||
if mail_type == "personal" and not target_uid.is_empty():
|
||||
recip_input.text = target_uid.substr(0, 12) + "..."
|
||||
recip_input.tooltip_text = target_uid
|
||||
recip_hbox.add_child(recip_input)
|
||||
var recip_find_btn := Button.new()
|
||||
recip_find_btn.text = "Find"
|
||||
recip_find_btn.custom_minimum_size.x = 60
|
||||
recip_hbox.add_child(recip_find_btn)
|
||||
var recip_resolved_lbl := Label.new()
|
||||
recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
recip_hbox.add_child(recip_resolved_lbl)
|
||||
grid.add_child(recip_hbox)
|
||||
|
||||
var _edit_resolved_uid := target_uid
|
||||
recip_find_btn.pressed.connect(func():
|
||||
var inp = recip_input.text.strip_edges()
|
||||
if inp.is_empty():
|
||||
_edit_resolved_uid = ""
|
||||
recip_resolved_lbl.text = "(Global)"
|
||||
recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
return
|
||||
var uid = await _resolve_target_user_id(inp)
|
||||
if uid.is_empty():
|
||||
recip_resolved_lbl.text = "NOT FOUND"
|
||||
recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_ERR)
|
||||
else:
|
||||
_edit_resolved_uid = uid
|
||||
recip_resolved_lbl.text = uid.substr(0, 12) + "..."
|
||||
recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK)
|
||||
)
|
||||
|
||||
vbox.add_child(grid)
|
||||
|
||||
var save_btn := Button.new()
|
||||
save_btn.text = "Save Changes"
|
||||
save_btn.custom_minimum_size.y = 40
|
||||
vbox.add_child(save_btn)
|
||||
|
||||
dialog.add_child(vbox)
|
||||
add_child(dialog)
|
||||
dialog.popup_centered()
|
||||
|
||||
save_btn.pressed.connect(func():
|
||||
# If user typed something but didn't click Find, auto-resolve
|
||||
var recip_text = recip_input.text.strip_edges()
|
||||
var final_target = _edit_resolved_uid
|
||||
if not recip_text.is_empty() and _edit_resolved_uid == target_uid:
|
||||
# Text changed but not resolved yet
|
||||
var resolved = await _resolve_target_user_id(recip_text)
|
||||
if not resolved.is_empty():
|
||||
final_target = resolved
|
||||
elif recip_text.is_empty():
|
||||
final_target = ""
|
||||
|
||||
_set_status("Updating mail...")
|
||||
var payload = {
|
||||
"mail_id": mail_id,
|
||||
"type": mail_type,
|
||||
"target_user_id": target_uid,
|
||||
"new_target_user_id": final_target,
|
||||
"title": title_input.text,
|
||||
"content": content_input.text,
|
||||
"end_date": end_picker.get_date_iso()
|
||||
}
|
||||
var res = await _rpc("admin_update_mail", payload)
|
||||
if res.has("success"):
|
||||
_set_status("Mail updated!", CLR_STATUS_OK)
|
||||
await _load_mail()
|
||||
dialog.queue_free()
|
||||
)
|
||||
|
||||
func _on_end_mail_pressed() -> void:
|
||||
var mail = _get_selected_mail()
|
||||
if mail.is_empty(): return
|
||||
|
||||
var mail_id: String = mail.get("id", "")
|
||||
var mail_type: String = mail.get("type", "global")
|
||||
var target_uid: String = mail.get("target_user_id", "")
|
||||
|
||||
var confirm := ConfirmationDialog.new()
|
||||
confirm.title = "End Mail Now?"
|
||||
confirm.dialog_text = "Set end_date to NOW for:\n" + mail.get("title", "Unknown")
|
||||
add_child(confirm)
|
||||
confirm.popup_centered()
|
||||
confirm.confirmed.connect(func():
|
||||
var now_iso = Time.get_datetime_string_from_system(true)
|
||||
var res = await _rpc("admin_update_mail", {
|
||||
"mail_id": mail_id,
|
||||
"type": mail_type,
|
||||
"target_user_id": target_uid,
|
||||
"end_date": now_iso
|
||||
})
|
||||
if res.has("success"):
|
||||
_set_status("Mail ended", CLR_STATUS_OK)
|
||||
await _load_mail()
|
||||
confirm.queue_free()
|
||||
)
|
||||
|
||||
func _on_delete_mail_server_pressed() -> void:
|
||||
var mail = _get_selected_mail()
|
||||
if mail.is_empty(): return
|
||||
|
||||
var mail_id: String = mail.get("id", "")
|
||||
var mail_type: String = mail.get("type", "global")
|
||||
var target_uid: String = mail.get("target_user_id", "")
|
||||
|
||||
var confirm := ConfirmationDialog.new()
|
||||
confirm.title = "PERMANENTLY Delete Mail?"
|
||||
confirm.dialog_text = "Remove from server storage:\n" + mail.get("title", "Unknown") + "\n\nThis cannot be undone!"
|
||||
add_child(confirm)
|
||||
confirm.popup_centered()
|
||||
confirm.confirmed.connect(func():
|
||||
var res = await _rpc("admin_delete_mail_server", {
|
||||
"mail_id": mail_id,
|
||||
"type": mail_type,
|
||||
"target_user_id": target_uid
|
||||
})
|
||||
if res.has("success"):
|
||||
_set_status("Mail deleted from server", CLR_STATUS_OK)
|
||||
await _load_mail()
|
||||
confirm.queue_free()
|
||||
)
|
||||
|
||||
+53
-27
@@ -6,11 +6,13 @@ signal closed
|
||||
@onready var mail_list_vbox := %MailListVBox as VBoxContainer
|
||||
@onready var mail_title_lbl := %MailTitleLbl as Label
|
||||
@onready var mail_content_text := %MailContentText as RichTextLabel
|
||||
@onready var sender_lbl := %SenderLbl as Label
|
||||
@onready var dynamic_rewards_container := %DynamicRewardsContainer as HBoxContainer
|
||||
@onready var reward_hbox_template := %RewardHBoxTemplate as HBoxContainer
|
||||
@onready var sender_lbl = get_node_or_null("%SenderLbl")
|
||||
@onready var dynamic_rewards_container := %DynamicRewardsContainer as VBoxContainer
|
||||
@onready var reward_hbox_template = %RewardHBoxTemplate
|
||||
@onready var action_btn := %ActionBtn as Button
|
||||
@onready var read_all_btn := %ReadAllBtn as Button
|
||||
@onready var empty_state_lbl := %EmptyStateLbl as Label
|
||||
@onready var mail_btn_template := %MailBtnTemplate as Button
|
||||
|
||||
var _current_mail: Dictionary = {}
|
||||
|
||||
@@ -18,6 +20,7 @@ func _ready() -> void:
|
||||
visible = false
|
||||
close_btn.pressed.connect(hide_panel)
|
||||
action_btn.pressed.connect(_on_action_pressed)
|
||||
read_all_btn.pressed.connect(_on_read_all_pressed)
|
||||
if MailManager:
|
||||
MailManager.mail_updated.connect(_refresh_ui)
|
||||
|
||||
@@ -25,6 +28,7 @@ func show_panel() -> void:
|
||||
visible = true
|
||||
_clear_details()
|
||||
if MailManager:
|
||||
await MailManager.read_all_and_claim_all()
|
||||
MailManager.fetch_mails()
|
||||
_refresh_ui()
|
||||
|
||||
@@ -57,16 +61,15 @@ func _refresh_ui() -> void:
|
||||
if _current_mail.is_empty() and mails.size() > 0:
|
||||
_on_mail_selected(mails[0])
|
||||
|
||||
@onready var mail_btn_template := %MailBtnTemplate as Button
|
||||
|
||||
func _create_mail_button(mail: Dictionary) -> Button:
|
||||
var btn = mail_btn_template.duplicate()
|
||||
btn.visible = true
|
||||
|
||||
var title = btn.get_node("VBox/Title") as Label
|
||||
title.text = mail.get("title", "No Title")
|
||||
var title_lbl = btn.get_node("Margin/VBox/Title") as Label
|
||||
if title_lbl:
|
||||
title_lbl.text = mail.get("title", "No Title")
|
||||
|
||||
var date_lbl = btn.get_node("VBox/HBox/DateLbl") as Label
|
||||
var date_lbl = btn.get_node("Margin/VBox/HBox/DateLbl") as Label
|
||||
var date_str = mail.get("date", "")
|
||||
var expiry_str = mail.get("expiry_date", "")
|
||||
|
||||
@@ -85,19 +88,21 @@ func _create_mail_button(mail: Dictionary) -> Button:
|
||||
else:
|
||||
label_text += " (Expired)"
|
||||
|
||||
date_lbl.text = label_text
|
||||
if date_lbl:
|
||||
date_lbl.text = label_text
|
||||
|
||||
var status_lbl = btn.get_node("VBox/HBox/StatusLbl") as Label
|
||||
var status_lbl = btn.get_node("Margin/VBox/HBox/StatusLbl") as Label
|
||||
var mail_id = mail.get("id", "")
|
||||
if mail_id in MailManager.claimed_ids:
|
||||
status_lbl.text = "CLAIMED"
|
||||
status_lbl.add_theme_color_override("font_color", Color.GREEN)
|
||||
elif mail_id in MailManager.read_ids:
|
||||
status_lbl.text = "READ"
|
||||
status_lbl.add_theme_color_override("font_color", Color.GRAY)
|
||||
else:
|
||||
status_lbl.text = "NEW"
|
||||
status_lbl.add_theme_color_override("font_color", Color.YELLOW)
|
||||
if status_lbl:
|
||||
if mail_id in MailManager.claimed_ids:
|
||||
status_lbl.text = "CLAIMED"
|
||||
status_lbl.add_theme_color_override("font_color", Color.GREEN)
|
||||
elif mail_id in MailManager.read_ids:
|
||||
status_lbl.text = "READ"
|
||||
status_lbl.add_theme_color_override("font_color", Color.GRAY)
|
||||
else:
|
||||
status_lbl.text = "NEW"
|
||||
status_lbl.add_theme_color_override("font_color", Color.YELLOW)
|
||||
|
||||
return btn
|
||||
|
||||
@@ -113,7 +118,7 @@ func _on_mail_selected(mail: Dictionary) -> void:
|
||||
func _clear_details() -> void:
|
||||
mail_title_lbl.text = ""
|
||||
mail_content_text.text = ""
|
||||
sender_lbl.text = ""
|
||||
if sender_lbl: sender_lbl.text = ""
|
||||
for child in dynamic_rewards_container.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
action_btn.hide()
|
||||
@@ -121,7 +126,7 @@ func _clear_details() -> void:
|
||||
func _update_details(mail: Dictionary) -> void:
|
||||
mail_title_lbl.text = mail.get("title", "No Title")
|
||||
mail_content_text.text = mail.get("content", "")
|
||||
sender_lbl.text = "SENDER:\n" + mail.get("sender", "SYSTEM")
|
||||
if sender_lbl: sender_lbl.text = "SENDER:\n" + mail.get("sender", "SYSTEM")
|
||||
|
||||
for child in dynamic_rewards_container.get_children():
|
||||
if child.visible: child.queue_free()
|
||||
@@ -141,16 +146,29 @@ func _update_details(mail: Dictionary) -> void:
|
||||
var row = reward_hbox_template.duplicate()
|
||||
row.visible = true
|
||||
dynamic_rewards_container.add_child(row)
|
||||
var amt_lbl = row.get_node("AmountLbl") as Label
|
||||
var amt_lbl = row.get_node("Margin/HBox/VBox/AmountLbl") as Label
|
||||
var type_lbl = row.get_node("Margin/HBox/VBox/TypeLbl") as Label
|
||||
|
||||
var t = r.get("type", "star")
|
||||
var amt = r.get("amount", 0)
|
||||
var id = r.get("id", "")
|
||||
var rid = r.get("id", "")
|
||||
|
||||
if t == "star" or t == "gold":
|
||||
amt_lbl.text = str(amt) + " " + t.to_upper()
|
||||
else:
|
||||
amt_lbl.text = str(amt) + " " + id
|
||||
if amt_lbl: amt_lbl.text = "x" + str(amt)
|
||||
if type_lbl: type_lbl.text = t.to_upper() if (t == "star" or t == "gold") else rid.to_upper()
|
||||
|
||||
# Fill empty slots up to 4
|
||||
var added = rewards.size()
|
||||
while added < 4:
|
||||
added += 1
|
||||
var empty_row = reward_hbox_template.duplicate()
|
||||
empty_row.visible = true
|
||||
dynamic_rewards_container.add_child(empty_row)
|
||||
var icon_bg = empty_row.get_node_or_null("Margin/HBox/IconBg")
|
||||
if icon_bg: icon_bg.color = Color(0, 0, 0, 0)
|
||||
var t_lbl = empty_row.get_node_or_null("Margin/HBox/VBox/TypeLbl")
|
||||
if t_lbl: t_lbl.text = ""
|
||||
var a_lbl = empty_row.get_node_or_null("Margin/HBox/VBox/AmountLbl")
|
||||
if a_lbl: a_lbl.text = ""
|
||||
|
||||
action_btn.show()
|
||||
var mail_id = mail.get("id", "")
|
||||
@@ -178,3 +196,11 @@ func _on_action_pressed() -> void:
|
||||
if ok:
|
||||
_current_mail = {}
|
||||
_clear_details()
|
||||
|
||||
func _on_read_all_pressed() -> void:
|
||||
if not MailManager: return
|
||||
for mail in MailManager.mails:
|
||||
var mid = mail.get("id", "")
|
||||
if mid not in MailManager.read_ids:
|
||||
MailManager.mark_as_read(mid)
|
||||
_refresh_ui()
|
||||
|
||||
@@ -83,6 +83,11 @@ func _show_tab(tab: String) -> void:
|
||||
_friends_view.visible = tab == "friends"
|
||||
_dm_view.visible = tab == "dm"
|
||||
|
||||
_search_tab_btn.set_pressed_no_signal(tab == "search")
|
||||
_friends_tab_btn.set_pressed_no_signal(tab == "friends")
|
||||
if _dm_tab_btn:
|
||||
_dm_tab_btn.set_pressed_no_signal(tab == "dm")
|
||||
|
||||
# Auto-load search results on first open
|
||||
if tab == "search" and _search_results_list.get_child_count() == 0:
|
||||
_on_search_pressed()
|
||||
|
||||
@@ -52,9 +52,13 @@ function InitModule(ctx, logger, nk, initializer) {
|
||||
|
||||
// Inbox System RPCs
|
||||
initializer.registerRpc("admin_send_mail", rpcAdminSendMail);
|
||||
initializer.registerRpc("admin_list_mail", rpcAdminListMail);
|
||||
initializer.registerRpc("admin_update_mail", rpcAdminUpdateMail);
|
||||
initializer.registerRpc("admin_delete_mail_server", rpcAdminDeleteMailServer);
|
||||
initializer.registerRpc("get_mail", rpcGetMail);
|
||||
initializer.registerRpc("claim_mail_reward", rpcClaimMailReward);
|
||||
initializer.registerRpc("delete_mail", rpcDeleteMail);
|
||||
initializer.registerRpc("save_mail_state", rpcSaveMailState);
|
||||
|
||||
// Steam auth hooks
|
||||
initializer.registerAfterAuthenticateSteam(afterAuthenticateSteam);
|
||||
@@ -1712,3 +1716,229 @@ function rpcDeleteMail(ctx, logger, nk, payload) {
|
||||
|
||||
return JSON.stringify({ success: true, deleted_ids: state.deleted_ids });
|
||||
}
|
||||
|
||||
function rpcSaveMailState(ctx, logger, nk, payload) {
|
||||
if (!ctx.userId) throw new Error("Not authenticated");
|
||||
var request = JSON.parse(payload || "{}");
|
||||
|
||||
// Load existing state to merge (don't clobber claimed_ids from client)
|
||||
var stateObjs = nk.storageRead([{ collection: "inbox", key: "state", userId: ctx.userId }]);
|
||||
var state = { claimed_ids: [], deleted_ids: [], read_ids: [] };
|
||||
if (stateObjs && stateObjs.length > 0) {
|
||||
var val = stateObjs[0].value;
|
||||
state.claimed_ids = val.claimed_ids || [];
|
||||
state.deleted_ids = val.deleted_ids || [];
|
||||
state.read_ids = val.read_ids || [];
|
||||
}
|
||||
|
||||
// Merge read_ids from client (add any new ones)
|
||||
var newReadIds = request.read_ids || [];
|
||||
for (var i = 0; i < newReadIds.length; i++) {
|
||||
if (state.read_ids.indexOf(newReadIds[i]) === -1) {
|
||||
state.read_ids.push(newReadIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
nk.storageWrite([{
|
||||
collection: "inbox",
|
||||
key: "state",
|
||||
userId: ctx.userId,
|
||||
value: state,
|
||||
permissionRead: 1,
|
||||
permissionWrite: 0
|
||||
}]);
|
||||
|
||||
return JSON.stringify({ success: true });
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Admin Mail Management RPCs
|
||||
// =============================================================================
|
||||
|
||||
function rpcAdminListMail(ctx, logger, nk, payload) {
|
||||
requireAdmin(ctx, nk);
|
||||
|
||||
// --- Global mails ---
|
||||
var globalObjs = nk.storageRead([{ collection: "config", key: "global_mail", userId: "00000000-0000-0000-0000-000000000000" }]);
|
||||
var globalMails = (globalObjs && globalObjs.length > 0) ? (globalObjs[0].value.mails || []) : [];
|
||||
for (var i = 0; i < globalMails.length; i++) {
|
||||
globalMails[i].type = "global";
|
||||
}
|
||||
|
||||
// --- Personal mails: scan ALL users' inbox/personal objects ---
|
||||
var personalMails = [];
|
||||
var cursor = null;
|
||||
try {
|
||||
do {
|
||||
// storageList(userId, collection, limit, cursor)
|
||||
// Using empty string userId means list across all users for this collection
|
||||
var listResult = nk.storageList("", "inbox", 100, cursor);
|
||||
var objects = listResult.objects || [];
|
||||
for (var j = 0; j < objects.length; j++) {
|
||||
var obj = objects[j];
|
||||
if (obj.key !== "personal") continue;
|
||||
var ownerUserId = obj.userId;
|
||||
var mails = obj.value.mails || [];
|
||||
for (var k = 0; k < mails.length; k++) {
|
||||
var m = mails[k];
|
||||
m.type = "personal";
|
||||
m.target_user_id = ownerUserId;
|
||||
personalMails.push(m);
|
||||
}
|
||||
}
|
||||
cursor = listResult.cursor || null;
|
||||
} while (cursor);
|
||||
} catch (e) {
|
||||
logger.warn("admin_list_mail: could not list personal inboxes: " + e);
|
||||
}
|
||||
|
||||
var allMails = globalMails.concat(personalMails);
|
||||
// Sort newest first
|
||||
allMails.sort(function(a, b) {
|
||||
return (b.date || "").localeCompare(a.date || "");
|
||||
});
|
||||
|
||||
return JSON.stringify({ mails: allMails });
|
||||
}
|
||||
|
||||
function rpcAdminUpdateMail(ctx, logger, nk, payload) {
|
||||
requireAdmin(ctx, nk);
|
||||
var request = JSON.parse(payload || "{}");
|
||||
var mailId = request.mail_id;
|
||||
if (!mailId) throw new Error("mail_id required");
|
||||
|
||||
var isGlobal = request.type !== "personal";
|
||||
var targetUserId = request.target_user_id || "";
|
||||
var newTargetUserId = request.new_target_user_id;
|
||||
var hasNewTarget = (newTargetUserId !== undefined && newTargetUserId !== null);
|
||||
|
||||
// Step 1: Find and extract the mail object from its current location
|
||||
var mailObj = null;
|
||||
|
||||
if (isGlobal) {
|
||||
var globalObjs = nk.storageRead([{ collection: "config", key: "global_mail", userId: "00000000-0000-0000-0000-000000000000" }]);
|
||||
var globalMails = (globalObjs && globalObjs.length > 0) ? (globalObjs[0].value.mails || []) : [];
|
||||
for (var i = 0; i < globalMails.length; i++) {
|
||||
if (globalMails[i].id === mailId) {
|
||||
mailObj = globalMails.splice(i, 1)[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mailObj) throw new Error("Mail not found in global");
|
||||
// Write back without this mail (it may move)
|
||||
nk.storageWrite([{
|
||||
collection: "config",
|
||||
key: "global_mail",
|
||||
userId: "00000000-0000-0000-0000-000000000000",
|
||||
value: { mails: globalMails },
|
||||
permissionRead: 2,
|
||||
permissionWrite: 0
|
||||
}]);
|
||||
} else {
|
||||
if (!targetUserId) throw new Error("target_user_id required for personal mail");
|
||||
var pObjs = nk.storageRead([{ collection: "inbox", key: "personal", userId: targetUserId }]);
|
||||
var personalMails = (pObjs && pObjs.length > 0) ? (pObjs[0].value.mails || []) : [];
|
||||
for (var j = 0; j < personalMails.length; j++) {
|
||||
if (personalMails[j].id === mailId) {
|
||||
mailObj = personalMails.splice(j, 1)[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mailObj) throw new Error("Mail not found in personal inbox");
|
||||
// Write back without this mail
|
||||
nk.storageWrite([{
|
||||
collection: "inbox",
|
||||
key: "personal",
|
||||
userId: targetUserId,
|
||||
value: { mails: personalMails },
|
||||
permissionRead: 1,
|
||||
permissionWrite: 0
|
||||
}]);
|
||||
}
|
||||
|
||||
// Step 2: Apply field updates to the mail object
|
||||
if (request.title !== undefined) mailObj.title = request.title;
|
||||
if (request.content !== undefined) mailObj.content = request.content;
|
||||
if (request.end_date !== undefined) mailObj.end_date = request.end_date;
|
||||
if (request.expiry_date !== undefined) mailObj.expiry_date = request.expiry_date;
|
||||
|
||||
// Step 3: Determine destination
|
||||
var destUserId = hasNewTarget ? newTargetUserId : (isGlobal ? "" : targetUserId);
|
||||
|
||||
if (destUserId === "") {
|
||||
// Write to global
|
||||
mailObj.type = "global";
|
||||
var gObjs = nk.storageRead([{ collection: "config", key: "global_mail", userId: "00000000-0000-0000-0000-000000000000" }]);
|
||||
var gMails = (gObjs && gObjs.length > 0) ? (gObjs[0].value.mails || []) : [];
|
||||
gMails.push(mailObj);
|
||||
nk.storageWrite([{
|
||||
collection: "config",
|
||||
key: "global_mail",
|
||||
userId: "00000000-0000-0000-0000-000000000000",
|
||||
value: { mails: gMails },
|
||||
permissionRead: 2,
|
||||
permissionWrite: 0
|
||||
}]);
|
||||
} else {
|
||||
// Write to personal inbox of destUserId
|
||||
mailObj.type = "personal";
|
||||
var dObjs = nk.storageRead([{ collection: "inbox", key: "personal", userId: destUserId }]);
|
||||
var dMails = (dObjs && dObjs.length > 0) ? (dObjs[0].value.mails || []) : [];
|
||||
dMails.push(mailObj);
|
||||
nk.storageWrite([{
|
||||
collection: "inbox",
|
||||
key: "personal",
|
||||
userId: destUserId,
|
||||
value: { mails: dMails },
|
||||
permissionRead: 1,
|
||||
permissionWrite: 0
|
||||
}]);
|
||||
}
|
||||
|
||||
logger.info("Admin updated mail " + mailId + " by " + ctx.userId + (hasNewTarget ? " (moved to " + destUserId + ")" : ""));
|
||||
return JSON.stringify({ success: true });
|
||||
}
|
||||
|
||||
function rpcAdminDeleteMailServer(ctx, logger, nk, payload) {
|
||||
requireAdmin(ctx, nk);
|
||||
var request = JSON.parse(payload || "{}");
|
||||
var mailId = request.mail_id;
|
||||
if (!mailId) throw new Error("mail_id required");
|
||||
|
||||
var isGlobal = request.type !== "personal";
|
||||
var targetUserId = request.target_user_id || "";
|
||||
|
||||
if (isGlobal) {
|
||||
var globalObjs = nk.storageRead([{ collection: "config", key: "global_mail", userId: "00000000-0000-0000-0000-000000000000" }]);
|
||||
var globalMails = (globalObjs && globalObjs.length > 0) ? (globalObjs[0].value.mails || []) : [];
|
||||
var before = globalMails.length;
|
||||
globalMails = globalMails.filter(function(m) { return m.id !== mailId; });
|
||||
if (globalMails.length === before) throw new Error("Mail not found");
|
||||
nk.storageWrite([{
|
||||
collection: "config",
|
||||
key: "global_mail",
|
||||
userId: "00000000-0000-0000-0000-000000000000",
|
||||
value: { mails: globalMails },
|
||||
permissionRead: 2,
|
||||
permissionWrite: 0
|
||||
}]);
|
||||
} else {
|
||||
if (!targetUserId) throw new Error("target_user_id required for personal mail");
|
||||
var pObjs = nk.storageRead([{ collection: "inbox", key: "personal", userId: targetUserId }]);
|
||||
var personalMails = (pObjs && pObjs.length > 0) ? (pObjs[0].value.mails || []) : [];
|
||||
var pBefore = personalMails.length;
|
||||
personalMails = personalMails.filter(function(m) { return m.id !== mailId; });
|
||||
if (personalMails.length === pBefore) throw new Error("Mail not found");
|
||||
nk.storageWrite([{
|
||||
collection: "inbox",
|
||||
key: "personal",
|
||||
userId: targetUserId,
|
||||
value: { mails: personalMails },
|
||||
permissionRead: 1,
|
||||
permissionWrite: 0
|
||||
}]);
|
||||
}
|
||||
|
||||
logger.info("Admin deleted mail " + mailId + " from server by " + ctx.userId);
|
||||
return JSON.stringify({ success: true });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user