Skip to content

Commit 595ff67

Browse files
committed
Merge Refine URL confirmation modal (mr-835)
a3dc46a - tweak(core): refine URL confirmation modal
2 parents a6f2980 + a3dc46a commit 595ff67

File tree

4 files changed

+250
-41
lines changed

4 files changed

+250
-41
lines changed

code/components/conhost-v2/component.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ return function()
88
"rage:graphics",
99
"rage:nutsnbolts",
1010
"nui:core",
11+
"vendor:cpp-url"
1112
}
1213
end
1314

Lines changed: 163 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
#include <StdInc.h>
2+
23
#include <imgui.h>
34
#include <imgui_internal.h>
4-
#include <ConsoleHost.h>
55

6+
#include <ConsoleHost.h>
67
#include <CoreConsole.h>
78

89
#include <shellapi.h>
910

11+
#include <skyr/url.hpp>
12+
#include <fmt/format.h>
13+
1014
#include <UrlConfirmationExport.h>
1115

1216
#if defined(GTA_FIVE)
@@ -15,6 +19,82 @@
1519
#define PRODUCT_NAME "RedM"
1620
#endif
1721

22+
extern ImFont* consoleFontSmall;
23+
24+
namespace
25+
{
26+
struct LocalizedStrings
27+
{
28+
std::string heading;
29+
std::string warning;
30+
std::string visitSite;
31+
std::string cancel;
32+
};
33+
34+
const std::unordered_map<std::string, LocalizedStrings> g_translations = {
35+
{ "en-US", { "This link will take you to the website below", "You are now leaving {0} and visiting an unaffiliated external site. This link may not be secure. Only click on links from trusted senders.", "Visit Site", "Cancel" } },
36+
{ "de-DE", { "Dieser Link führt dich zur folgenden Website", "Du verlässt jetzt {0} und begibst dich auf eine externe Seite eines Drittanbieters. Die Sicherheit des Links kann nicht gewährleistet werden. Klicke nur auf Links, wenn du dem Absender vertraust.", "Website besuchen", "Abbrechen" } },
37+
{ "es-ES", { "Este enlace te llevará al sitio web a continuación", "Estás abandonando {0} y serás redirigido a un sitio web externo no afiliado con Rockstar Games. Puede que este enlace no sea seguro. Haz clic solamente en enlaces procedentes de fuentes de fiar.", "Visitar sitio", "Cancelar" } },
38+
{ "es-MX", { "Este enlace te llevará al sitio web a continuación", "Estás por salir de {0} y se te dirigirá a una página externa no afiliada. Este enlace podría no ser seguro. Haz clic solo en enlaces de fuentes seguras.", "Visitar sitio", "Cancelar" } },
39+
{ "fr-FR", { "Ce lien vous dirigera vers le site ci-dessous", "Vous allez quitter {0} et accéder à un site externe non affilié. Ce lien n'est peut-être pas sûr. Cliquez uniquement sur les liens d'expéditeurs de confiance.", "Visiter le site", "Annuler" } },
40+
{ "it-IT", { "Questo link ti porterà al sito web qui sotto", "Stai uscendo da {0} per visitare un sito esterno non affiliato. Questo link potrebbe non essere sicuro. Clicca solo su link forniti da mittenti attendibili.", "Visita il sito", "Annulla" } },
41+
{ "pl-PL", { "Ten link przeniesie Cię na poniższą stronę", "Zamierzasz opuścić {0} i udać się na zewnętrzną stronę sieciową. Ten odnośnik może nie być bezpieczny. Otwieraj odnośniki jedynie z zaufanych źródeł.", "Odwiedź stronę", "Anuluj" } },
42+
{ "pt-BR", { "Este link levará você ao site abaixo", "Você está saindo do {0} e acessando um site externo não afiliado. O link por não ser seguro. Clique apenas em links de fontes confiáveis.", "Visitar site", "Cancelar" } },
43+
{ "ru-RU", { "Эта ссылка приведёт вас на сайт ниже", "Вы покидаете {0} и переходите на сторонний сайт. Ссылка может быть ненадежной. Переходите только по ссылкам от доверенных отправителей.", "Перейти на сайт", "Отмена" } }
44+
};
45+
46+
const LocalizedStrings& GetLocalizedStrings()
47+
{
48+
// Read the user's preferred languages from registry
49+
HKEY hKey;
50+
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Control Panel\\International\\User Profile", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
51+
{
52+
DWORD bufferSize = 0;
53+
if (RegGetValueW(hKey, nullptr, L"Languages", RRF_RT_REG_MULTI_SZ, nullptr, nullptr, &bufferSize) == ERROR_SUCCESS && bufferSize > 0)
54+
{
55+
std::vector<wchar_t> buffer(bufferSize / sizeof(wchar_t));
56+
if (RegGetValueW(hKey, nullptr, L"Languages", RRF_RT_REG_MULTI_SZ, nullptr, buffer.data(), &bufferSize) == ERROR_SUCCESS)
57+
{
58+
RegCloseKey(hKey);
59+
60+
// Buffer contains null-separated list of languages in preference order
61+
const wchar_t* current = buffer.data();
62+
while (*current)
63+
{
64+
std::string locale = ToNarrow(current);
65+
66+
auto it = g_translations.find(locale);
67+
68+
// Check for exact match first
69+
if (it != g_translations.end())
70+
{
71+
return it->second;
72+
}
73+
74+
// Try language-only fallback (e.g., "en -> "en-US")
75+
auto dashPos = locale.find('-');
76+
std::string langOnly = (dashPos != std::string::npos) ? locale.substr(0, dashPos) : locale;
77+
78+
for (const auto& [key, value] : g_translations)
79+
{
80+
if (key.substr(0, key.find('-')) == langOnly)
81+
{
82+
return value;
83+
}
84+
}
85+
86+
current += wcslen(current) + 1;
87+
}
88+
}
89+
}
90+
RegCloseKey(hKey);
91+
}
92+
93+
// Fallback to English if no match found
94+
return g_translations.at("en-US");
95+
}
96+
}
97+
1898
void DrawUrlConfirmationModal()
1999
{
20100
if (!nui::g_showUrlConfirmModal.load())
@@ -26,67 +106,108 @@ void DrawUrlConfirmationModal()
26106

27107
std::lock_guard<std::mutex> lock(nui::g_urlModalMutex);
28108

29-
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.11f, 0.11f, 0.13f, 1.0f));
30-
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.20f, 0.20f, 0.22f, 1.0f));
31-
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f);
32-
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(32.0f, 24.0f));
33-
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
34-
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0f, 8.0f));
109+
const auto& strings = GetLocalizedStrings();
110+
111+
ImGui::PushStyleColor(ImGuiCol_PopupBg, IM_COL32(26, 26, 26, 255));
112+
ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(26, 26, 26, 255));
113+
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.0f);
114+
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(45.0f, 50.0f));
115+
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
35116

36117
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
37118
ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
38-
ImGui::SetNextWindowSize(ImVec2(850, 0), ImGuiCond_Always);
119+
ImGui::SetNextWindowSize(ImVec2(960, 0), ImGuiCond_Always);
39120

40121
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse;
41122

42123
if (ImGui::BeginPopupModal("##UrlConfirmModal", nullptr, flags))
43124
{
44-
ImVec2 windowSize = ImGui::GetWindowSize();
45-
ImVec2 windowPadding = ImGui::GetStyle().WindowPadding;
46-
47-
// Content area
48-
ImGui::SetCursorPos(ImVec2(windowPadding.x, windowPadding.y + 16.0f));
49-
50125
// Main heading
51-
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.85f, 0.85f, 0.87f, 1.0f));
52-
ImGui::Text("This link will take you to the website below");
126+
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(235, 235, 235, 255));
127+
ImGui::Text("%s", strings.heading.c_str());
53128
ImGui::PopStyleColor();
54129

55130
ImGui::Dummy(ImVec2(0, 8.0f));
56131

57-
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.95f, 0.75f, 0.25f, 1.0f));
58-
ImGui::Text("%s", nui::g_pendingUrl.c_str());
59-
ImGui::PopStyleColor();
132+
std::string hostDisplay;
133+
134+
auto parsed = skyr::make_url(nui::g_pendingUrl);
135+
if (parsed && !parsed->host().empty())
136+
{
137+
hostDisplay = parsed->host();
138+
}
139+
else
140+
{
141+
hostDisplay = nui::g_pendingUrl;
142+
}
143+
144+
size_t hostPos = nui::g_pendingUrl.find(hostDisplay);
145+
if (hostPos != std::string::npos && hostDisplay != nui::g_pendingUrl)
146+
{
147+
std::string beforeHost = nui::g_pendingUrl.substr(0, hostPos);
148+
std::string afterHost = nui::g_pendingUrl.substr(hostPos + hostDisplay.length());
149+
150+
// Display scheme
151+
if (!beforeHost.empty())
152+
{
153+
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(235, 235, 235, 255));
154+
ImGui::Text("%s", beforeHost.c_str());
155+
ImGui::PopStyleColor();
156+
ImGui::SameLine(0, 0);
157+
}
158+
159+
// Display host in accent color
160+
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(248, 171, 19, 255));
161+
ImGui::Text("%s", hostDisplay.c_str());
162+
ImGui::PopStyleColor();
163+
164+
// Display path/query
165+
if (!afterHost.empty())
166+
{
167+
ImGui::SameLine(0, 0);
168+
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(235, 235, 235, 255));
169+
ImGui::Text("%s", afterHost.c_str());
170+
ImGui::PopStyleColor();
171+
}
172+
}
173+
else
174+
{
175+
// Fallback: display entire URL in accent color if host not found
176+
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(248, 171, 19, 255));
177+
ImGui::Text("%s", nui::g_pendingUrl.c_str());
178+
ImGui::PopStyleColor();
179+
}
60180

61181
ImGui::Dummy(ImVec2(0, 12.0f));
62182

63183
// Separator
64-
ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(0.25f, 0.25f, 0.28f, 1.0f));
184+
ImGui::PushStyleColor(ImGuiCol_Separator, IM_COL32(44, 44, 44, 255));
65185
ImGui::Separator();
66186
ImGui::PopStyleColor();
67187

68188
ImGui::Dummy(ImVec2(0, 12.0f));
69189

70190
// Warning text
71-
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.55f, 0.55f, 0.57f, 1.0f));
72-
ImGui::TextWrapped("You are now leaving " PRODUCT_NAME " and visiting an unaffiliated external site.");
73-
ImGui::TextWrapped("This link may not be secure. Only click on links from trusted senders.");
191+
ImGui::PushFont(consoleFontSmall);
192+
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(152, 152, 152, 255));
193+
auto warningText = fmt::format(fmt::runtime(strings.warning), PRODUCT_NAME);
194+
ImGui::TextWrapped("%s", warningText.c_str());
74195
ImGui::PopStyleColor();
196+
ImGui::PopFont();
75197

76198
ImGui::Dummy(ImVec2(0, 20.0f));
77199

78200
float buttonWidth = ImGui::GetContentRegionAvail().x;
79-
float buttonHeight = 44.0f;
80201

81202
// Visit Site button
82-
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.10f, 0.30f, 0.55f, 1.0f));
83-
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.15f, 0.38f, 0.65f, 1.0f));
84-
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.08f, 0.25f, 0.48f, 1.0f));
85-
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
86-
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
203+
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(235, 235, 235, 255));
204+
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(23, 73, 138, 255));
205+
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(35, 95, 170, 255));
206+
ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32(18, 58, 110, 255));
207+
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6.0f);
87208
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
88209

89-
if (ImGui::Button("Visit Site", ImVec2(buttonWidth, buttonHeight)))
210+
if (ImGui::Button(strings.visitSite.c_str(), ImVec2(buttonWidth, 90.0f)))
90211
{
91212
ShellExecuteW(nullptr, L"open", ToWide(nui::g_pendingUrl).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
92213
nui::g_pendingUrl.clear();
@@ -97,17 +218,17 @@ void DrawUrlConfirmationModal()
97218
ImGui::PopStyleVar(2);
98219
ImGui::PopStyleColor(3);
99220

100-
ImGui::Dummy(ImVec2(0, 8.0f));
221+
ImGui::Dummy(ImVec2(0, 30.0f));
101222

102223
// Cancel button
103-
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.15f, 0.17f, 1.0f));
104-
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.20f, 0.20f, 0.23f, 1.0f));
105-
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.12f, 0.12f, 0.14f, 1.0f));
106-
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.35f, 0.35f, 0.38f, 1.0f));
107-
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
108-
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
109-
110-
if (ImGui::Button("Cancel", ImVec2(buttonWidth, buttonHeight)))
224+
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(26, 26, 26, 255));
225+
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(50, 50, 53, 255));
226+
ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32(35, 35, 38, 255));
227+
ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(63, 63, 63, 255));
228+
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6.0f);
229+
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
230+
231+
if (ImGui::Button(strings.cancel.c_str(), ImVec2(buttonWidth, 80.0f)))
111232
{
112233
nui::g_pendingUrl.clear();
113234
nui::g_showUrlConfirmModal.store(false);
@@ -117,9 +238,11 @@ void DrawUrlConfirmationModal()
117238
ImGui::PopStyleVar(2);
118239
ImGui::PopStyleColor(5);
119240

241+
ImGui::Dummy(ImVec2(0, 5.0f));
242+
120243
ImGui::EndPopup();
121244
}
122245

123-
ImGui::PopStyleVar(4);
246+
ImGui::PopStyleVar(3);
124247
ImGui::PopStyleColor(2);
125248
}

code/components/nui-core/component.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"net:base",
77
"vfs:core",
88
"vendor:minhook",
9-
"legitimacy"
9+
"legitimacy",
10+
"vendor:cpp-url"
1011
],
1112
"provides": []
1213
}

0 commit comments

Comments
 (0)