add support for country flags

This commit is contained in:
Elijah R 2024-06-23 02:18:35 -04:00
parent 67413079b0
commit 03be418d97
10 changed files with 92 additions and 13 deletions

View File

@ -33,5 +33,6 @@
"run-script-os": "^1.1.6",
"ts-jest": "^29.1.2",
"typescript": "^5.3.3"
}
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View File

@ -87,40 +87,40 @@
display: none;
}
tr.user-admin > td, .chat-username-admin, .username-admin {
tr.user-admin .userlist-username, .chat-username-admin, .username-admin {
color: #FF0000 !important;
}
tr.user-moderator > td, .chat-username-moderator, .username-moderator {
tr.user-moderator .userlist-username, .chat-username-moderator, .username-moderator {
color: #00FF00 !important;
}
html[data-bs-theme="dark"] {
tr.user-unregistered > td, .chat-username-unregistered, .username-unregistered {
tr.user-unregistered .userlist-username, .chat-username-unregistered, .username-unregistered {
color: #b1b1b1 !important;
}
tr.user-registered > td, .chat-username-registered, .username-registered {
tr.user-registered .userlist-username, .chat-username-registered, .username-registered {
color: #FFFFFF !important;
}
tr.user-registered.user-turn > td, tr.user-registered.user-waiting > td {
tr.user-registered.user-turn .userlist-username, tr.user-registered.user-waiting .userlist-username {
color: #000000 !important;
--bs-table-color: #000000 !important;
}
tr.user-unregistered.user-turn > td, tr.user-unregistered.user-waiting > td {
tr.user-unregistered.user-turn .userlist-username, tr.user-unregistered.user-waiting .userlist-username {
color: #585858 !important;
--bs-table-color: #585858 !important;
}
}
html[data-bs-theme="light"] {
tr.user-unregistered > td, .chat-username-unregistered, .username-unregistered {
tr.user-unregistered .userlist-username, .chat-username-unregistered, .username-unregistered {
color: #6b6b6b !important;
}
tr.user-registered > td, .chat-username-registered, .username-registered {
tr.user-registered .userlist-username, .chat-username-registered, .username-registered {
color: #000 !important;
}
}
@ -149,7 +149,7 @@ tr.user-waiting > td {
--bs-table-bg-state: #ece1be !important;
}
.user-current {
.user-current .userlist-username {
font-style: italic;
}
@ -340,3 +340,11 @@ Theme: cvmDisabled
font-family: 'Noto Color Emoji';
src: url('../assets/NotoColorEmoji.ttf');
}
.userlist-flag {
padding-right: 0.5rem;
}
.userlist-flag:empty {
display: none;
}

View File

@ -141,6 +141,7 @@
<input id="accountSettingsConfirmNewPassword" type="password" class="form-control" name="confirmpassword"/><br/>
<label for="accountSettingsCurrentPassword" id="accountSettingsCurrentPasswordLabel"></label>
<input id="accountSettingsCurrentPassword" type="password" class="form-control" name="currentpassword" required/><br/>
<input type="checkbox" id="hideFlagCheckbox" class="form-check-input"/> <label for="hideFlagCheckbox" id="hideFlagCheckboxLabel"></label><br/>
<button type="submit" class="btn btn-primary" id="updateAccountSettingsBtn"></button>
</form>
</div>

View File

@ -89,6 +89,7 @@ const fallbackLanguage : Language = {
"kAccountModal_ConfirmNewPassword": "Confirm New Password",
"kAccountModal_CurrentPassword": "Current Password",
"kAccountModal_ConfirmPassword": "Confirm Password",
"kAccountModal_HideFlag": "Hide my Country Flag",
"kMissingCaptcha": "Please fill out the captcha.",
"kPasswordsMustMatch": "Passwords must match.",

View File

@ -94,6 +94,7 @@ export enum I18nStringKey {
kAccountModal_ConfirmNewPassword = 'kAccountModal_ConfirmNewPassword',
kAccountModal_CurrentPassword = 'kAccountModal_CurrentPassword',
kAccountModal_ConfirmPassword = 'kAccountModal_ConfirmPassword',
kAccountModal_HideFlag = 'kAccountModal_HideFlag',
kAccountModal_VerifyText = 'kAccountModal_VerifyText',
kAccountModal_VerifyPasswordResetText = 'kAccountModal_VerifyPasswordResetText',
@ -295,6 +296,7 @@ export class I18n {
accountSettingsNewPasswordLabel: I18nStringKey.kAccountModal_NewPassword,
accountSettingsConfirmNewPasswordLabel: I18nStringKey.kAccountModal_ConfirmNewPassword,
accountSettingsCurrentPasswordLabel: I18nStringKey.kAccountModal_CurrentPassword,
hideFlagCheckboxLabel: I18nStringKey.kAccountModal_HideFlag,
updateAccountSettingsBtn: I18nStringKey.kGeneric_Update,
accountResetPasswordEmailLabel: I18nStringKey.kGeneric_EMail,
accountResetPasswordUsernameLabel: I18nStringKey.kGeneric_Username,

View File

@ -117,6 +117,7 @@ const elements = {
accountSettingsNewPassword: document.getElementById("accountSettingsNewPassword") as HTMLInputElement,
accountSettingsConfirmNewPassword: document.getElementById("accountSettingsConfirmNewPassword") as HTMLInputElement,
accountSettingsCurrentPassword: document.getElementById("accountSettingsCurrentPassword") as HTMLInputElement,
hideFlagCheckbox: document.getElementById("hideFlagCheckbox") as HTMLInputElement,
accountResetPasswordSection: document.getElementById("accountResetPasswordSection") as HTMLDivElement,
accountResetPasswordForm: document.getElementById("accountResetPasswordForm") as HTMLFormElement,
@ -316,6 +317,8 @@ const vms: VM[] = [];
const cards: HTMLDivElement[] = [];
const users: {
user: User;
usernameElement: HTMLSpanElement;
flagElement: HTMLSpanElement;
element: HTMLTableRowElement;
}[] = [];
let turnInterval: number | undefined = undefined;
@ -392,6 +395,7 @@ async function openVM(vm: VM): Promise<void> {
VM!.on('chat', (username, message) => chatMessage(username, message));
VM!.on('adduser', (user) => addUser(user));
VM!.on('flag', () => flag());
VM!.on('remuser', (user) => remUser(user));
VM!.on('rename', (oldname, newname, selfrename) => userRenamed(oldname, newname, selfrename));
@ -598,7 +602,14 @@ function addUser(user: User) {
let tr = document.createElement('tr');
tr.setAttribute('data-cvm-turn', '-1');
let td = document.createElement('td');
td.innerHTML = user.username;
let flagSpan = document.createElement('span');
let usernameSpan = document.createElement('span');
flagSpan.classList.add("userlist-flag");
usernameSpan.classList.add("userlist-username");
td.appendChild(flagSpan);
if (user.countryCode !== null) flagSpan.innerHTML = getFlagEmoji(user.countryCode);
td.appendChild(usernameSpan);
usernameSpan.innerText = user.username;
switch (user.rank) {
case Rank.Admin:
tr.classList.add('user-admin');
@ -615,7 +626,7 @@ function addUser(user: User) {
}
if (user.username === w.username) tr.classList.add('user-current');
tr.appendChild(td);
let u = { user: user, element: tr };
let u = { user: user, element: tr, usernameElement: usernameSpan, flagElement: flagSpan };
if (rank === Rank.Admin || rank === Rank.Moderator) userModOptions(u);
elements.userlist.appendChild(tr);
if (olduser !== undefined) olduser.element = tr;
@ -630,10 +641,21 @@ function remUser(user: User) {
users.splice(olduser, 1);
}
function getFlagEmoji(countryCode: string) {
if (countryCode.length !== 2) throw new Error('Invalid country code');
return String.fromCodePoint(...countryCode.toUpperCase().split('').map(char => 127397 + char.charCodeAt(0)));
}
function flag() {
for (let user of users.filter(u => u.user.countryCode !== null)) {
user.flagElement.innerHTML = getFlagEmoji(user.user.countryCode!);
}
}
function userRenamed(oldname: string, newname: string, selfrename: boolean) {
let user = users.find((u) => u.user.username === newname);
if (user) {
user.element.children[0].innerHTML = newname;
user.usernameElement.innerHTML = newname;
}
if (selfrename) {
w.username = newname;
@ -1110,6 +1132,11 @@ elements.accountSettingsForm.addEventListener('submit', async e => {
elements.accountModalError.style.display = "block";
return false;
}
localStorage.setItem("collabvm-hide-flag", JSON.stringify(elements.hideFlagCheckbox.checked));
if (!password && !email && !username) {
accountModal.hide();
return false
}
var result = await auth!.updateAccount(currentPassword, email, username, password);
if (result.success) {
elements.accountSettingsNewPassword.value = "";
@ -1311,6 +1338,10 @@ document.addEventListener('DOMContentLoaded', async () => {
renderAuth();
}
var hideFlag = JSON.parse(localStorage.getItem("collabvm-hide-flag")!);
if (hideFlag === null) hideFlag = false;
elements.hideFlagCheckbox.checked = hideFlag;
document.title = TheI18n.GetString(I18nStringKey.kGeneric_CollabVM);
// Load all VMs

View File

@ -38,6 +38,8 @@ export interface CollabVMClientEvents {
// Auth stuff
auth: (server: string) => void;
accountlogin: (success: boolean) => void;
flag: () => void;
}
// types for private emitter
@ -413,6 +415,14 @@ export default class CollabVMClient {
}
}
}
case 'flag': {
for (let i = 1; i < msgArr.length; i += 2) {
let user = this.users.find((u) => u.username === msgArr[i]);
if (user) user.countryCode = msgArr[i + 1];
}
this.publicEmitter.emit('flag');
break;
}
}
}
@ -493,6 +503,7 @@ export default class CollabVMClient {
u();
res(success);
});
if (localStorage.getItem('collabvm-hide-flag') === 'true') this.send('noflag');
if (username === null) this.send('rename');
else this.send('rename', username);
this.send('connect', id);

View File

@ -5,6 +5,7 @@ export class User {
rank: Rank;
// -1 means not in the turn queue, 0 means the current turn, anything else is the position in the queue
turn: number;
countryCode: string | null = null;
constructor(username: string, rank: Rank = Rank.Unregistered) {
this.username = username;

View File

@ -88,6 +88,7 @@
"kAccountModal_ConfirmNewPassword": "Confirm New Password",
"kAccountModal_CurrentPassword": "Current Password",
"kAccountModal_ConfirmPassword": "Confirm Password",
"kAccountModal_HideFlag": "Hide my Country Flag",
"kMissingCaptcha": "Please fill out the captcha.",
"kPasswordsMustMatch": "Passwords must match.",

View File

@ -1441,6 +1441,13 @@
dependencies:
"@popperjs/core" "^2.9.2"
"@types/dompurify@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7"
integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==
dependencies:
"@types/trusted-types" "*"
"@types/graceful-fs@^4.1.3":
version "4.1.9"
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4"
@ -1487,6 +1494,11 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
"@types/trusted-types@*":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
"@types/yargs-parser@*":
version "21.0.3"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
@ -1864,6 +1876,11 @@ csso@^4.2.0:
dependencies:
css-tree "^1.1.2"
dayjs@^1.11.10:
version "1.11.11"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e"
integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -1922,6 +1939,11 @@ domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.1:
dependencies:
domelementtype "^2.2.0"
dompurify@^3.1.0:
version "3.1.5"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.5.tgz#2c6a113fc728682a0f55684b1388c58ddb79dc38"
integrity sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==
domutils@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"