diff --git a/frontend/src/auth/auth.ts b/frontend/src/auth/auth.ts index 4dd35e0..2aa83ba 100644 --- a/frontend/src/auth/auth.ts +++ b/frontend/src/auth/auth.ts @@ -10,14 +10,15 @@ import type { ErrorResponse, LoginResponse, LoginRequest, RegisterRequest } from import { API_BASE_URL } from "../core/config"; import { loadChat, showLogin, showRegister } from "../navigation"; import { setUser } from "./api"; +import { id } from "../utils/utils"; /** * Clears all alert messages from authentication forms * @private */ export function clearAlerts(): void { - document.getElementById('login-alerts')!.innerHTML = ''; - document.getElementById('register-alerts')!.innerHTML = ''; + id('login-alerts').innerHTML = ''; + id('register-alerts').innerHTML = ''; } /** @@ -27,7 +28,7 @@ export function clearAlerts(): void { * @param {'success' | 'danger'} type - Type of alert (success or danger) */ export function showAlert(containerId: string, message: string, type: "success" | "danger" = 'danger'): void { - const container = document.getElementById(containerId)!; + const container = id(containerId); const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type}`; alertDiv.textContent = message; @@ -41,8 +42,8 @@ export function showAlert(containerId: string, message: string, type: "success" async function handleLogin(e: Event): Promise { e.preventDefault(); - const usernameElement = document.getElementById('login-username') as HTMLInputElement; - const passwordElement = document.getElementById('login-password') as HTMLInputElement; + const usernameElement = id('login-username'); + const passwordElement = id('login-password'); const username = usernameElement.value.trim(); const password = passwordElement.value.trim(); @@ -89,9 +90,9 @@ async function handleLogin(e: Event): Promise { async function handleRegister(e: Event): Promise { e.preventDefault(); - const usernameElement = document.getElementById('register-username') as HTMLInputElement; - const passwordElement = document.getElementById('register-password') as HTMLInputElement; - const confirmPasswordElement = document.getElementById('register-confirm-password') as HTMLInputElement; + const usernameElement = id('register-username'); + const passwordElement = id('register-password'); + const confirmPasswordElement = id('register-confirm-password'); const username = usernameElement.value.trim(); const password = passwordElement.value.trim(); @@ -152,11 +153,11 @@ async function handleRegister(e: Event): Promise { * @private */ function init(): void { - document.getElementById('login-form-element')!.addEventListener('submit', handleLogin); - document.getElementById('register-form-element')!.addEventListener('submit', handleRegister); + id('login-form-element').addEventListener('submit', handleLogin); + id('register-form-element').addEventListener('submit', handleRegister); - document.getElementById("login-link")!.addEventListener("click", showLogin); - document.getElementById("register-link")!.addEventListener("click", showRegister); + id("login-link").addEventListener("click", showLogin); + id("register-link").addEventListener("click", showRegister); } init(); \ No newline at end of file diff --git a/frontend/src/chat/contextMenu.ts b/frontend/src/chat/contextMenu.ts index 6705dc8..92f2056 100644 --- a/frontend/src/chat/contextMenu.ts +++ b/frontend/src/chat/contextMenu.ts @@ -8,15 +8,15 @@ import { websocket } from "../websocket"; import type { Message, WebSocketMessage } from "../core/types"; import { showSuccess, showError } from "../utils/notification"; -import { delay } from "../utils/utils"; +import { delay, id } from "../utils/utils"; import type { Dialog } from "mdui/components/dialog"; import type { TextField } from "mdui/components/text-field"; import { currentUser, authToken } from "../auth/api"; -let menu = document.getElementById("message-context-menu")!; -let editDialog = document.getElementById("edit-message-dialog") as Dialog; -let replyDialog = document.getElementById("reply-message-dialog") as Dialog; +let menu = id("message-context-menu")!; +let editDialog = id("edit-message-dialog"); +let replyDialog = id("reply-message-dialog"); let currentMessage: Message | null = null; function init() { diff --git a/frontend/src/chat/profileDialog.ts b/frontend/src/chat/profileDialog.ts index a5007fb..3018ee8 100644 --- a/frontend/src/chat/profileDialog.ts +++ b/frontend/src/chat/profileDialog.ts @@ -9,14 +9,13 @@ import { getAuthHeaders, currentUser } from "../auth/api"; import { API_BASE_URL } from "../core/config"; import type { UserProfile } from "../core/types"; import { showError, showSuccess } from "../utils/notification"; -import { formatTime } from "../utils/utils"; +import { formatTime, id } from "../utils/utils"; import defaultAvatar from "../resources/images/default-avatar.png"; -import type { Tabs } from "mdui/components/tabs"; import type { Dialog } from "mdui/components/dialog"; import type { TextField } from "mdui/components/text-field"; -let dialog = document.getElementById("user-profile-dialog") as Dialog; +let dialog = id("user-profile-dialog"); let currentProfile: UserProfile | null = null; let isOwnProfile: boolean = false; diff --git a/frontend/src/core/init.ts b/frontend/src/core/init.ts index 41ca520..63c7ec0 100644 --- a/frontend/src/core/init.ts +++ b/frontend/src/core/init.ts @@ -6,8 +6,9 @@ */ import { showLogin } from "../navigation"; +import { id } from "../utils/utils"; import { PRODUCT_NAME } from "./config"; showLogin(); -document.getElementById("productname")!.textContent = PRODUCT_NAME; +id("productname").textContent = PRODUCT_NAME; document.title = PRODUCT_NAME; \ No newline at end of file diff --git a/frontend/src/electron/electron.ts b/frontend/src/electron/electron.ts index 92541e1..1139870 100644 --- a/frontend/src/electron/electron.ts +++ b/frontend/src/electron/electron.ts @@ -7,11 +7,12 @@ import "../../electron.d.ts"; import { PRODUCT_NAME } from "../core/config.ts"; +import { id } from "../utils/utils.ts"; if (window.electronInterface !== undefined) { console.log("Running in Electron"); document.documentElement.classList.add("electron", `platform-${window.electronInterface.platform}`); - document.getElementById("window-title")!.textContent = PRODUCT_NAME; + id("window-title").textContent = PRODUCT_NAME; } else { console.log("Running in normal browser"); } \ No newline at end of file diff --git a/frontend/src/navigation.ts b/frontend/src/navigation.ts index e6e5306..f72e4b1 100644 --- a/frontend/src/navigation.ts +++ b/frontend/src/navigation.ts @@ -1,37 +1,43 @@ import { clearAlerts } from "./auth/auth"; import { loadMessages } from "./chat/chat"; +import { id } from "./utils/utils"; + +const loginForm = id("login-form"); +const registerForm = id("register-form"); +const chatInterface = id("chat-interface"); +const titleBar = id("electron-title-bar"); /** * Shows the login form and hides other interfaces. */ export function showLogin(): void { - document.getElementById('login-form')!.style.display = 'flex'; - document.getElementById('register-form')!.style.display = 'none'; - document.getElementById('chat-interface')!.style.display = 'none'; + loginForm.style.display = 'flex'; + registerForm.style.display = 'none'; + chatInterface.style.display = 'none'; clearAlerts(); - document.getElementById("electron-title-bar")!.classList.add("color-surface"); + titleBar.classList.add("color-surface"); } /** * Shows the registration form and hides other interfaces. */ export function showRegister(): void { - document.getElementById('login-form')!.style.display = 'none'; - document.getElementById('register-form')!.style.display = 'flex'; - document.getElementById('chat-interface')!.style.display = 'none'; + loginForm.style.display = 'none'; + registerForm.style.display = 'flex'; + chatInterface.style.display = 'none'; clearAlerts(); - document.getElementById("electron-title-bar")!.classList.add("color-surface"); + titleBar.classList.add("color-surface"); } /** * Shows the chat interface and hides authentication forms. */ export function showChat(): void { - document.getElementById('login-form')!.style.display = 'none'; - document.getElementById('register-form')!.style.display = 'none'; - document.getElementById('chat-interface')!.style.display = 'block'; + loginForm.style.display = 'none'; + registerForm.style.display = 'none'; + chatInterface.style.display = 'block'; loadMessages(); - document.getElementById("electron-title-bar")!.classList.remove("color-surface"); + titleBar.classList.remove("color-surface"); } /** diff --git a/frontend/src/userPanel/leftpanel.ts b/frontend/src/userPanel/leftpanel.ts index 652c8db..56a4724 100644 --- a/frontend/src/userPanel/leftpanel.ts +++ b/frontend/src/userPanel/leftpanel.ts @@ -5,18 +5,19 @@ * @version 1.0.0 */ -import type { Dialog } from "mdui/components/dialog"; +import { Dialog } from "mdui/components/dialog"; import { loadProfilePicture } from "./profile/upload"; +import { id } from "../utils/utils"; // сварачивание и разворачивание чата -const chatCollapseBtn = document.getElementById('hide-chat')!; -const chat1 = document.getElementById('chat-list-chat-1')!; -const chat2 = document.getElementById('chat-list-chat-2')!; -const chatInner = document.getElementById('chat-inner')!; -const chatName = document.getElementById('chat-name')!; -const profileButton = document.getElementById('profile-open')!; -const dialog = document.getElementById("profile-dialog") as Dialog; -const dialogClose = document.getElementById("profile-dialog-close")!; +const chatCollapseBtn = id('hide-chat')!; +const chat1 = id('chat-list-chat-1')!; +const chat2 = id('chat-list-chat-2')!; +const chatInner = id('chat-inner')!; +const chatName = id('chat-name')!; +const profileButton = id('profile-open')!; +const dialog = id("profile-dialog"); +const dialogClose = id("profile-dialog-close")!; /** * Sets up chat collapse functionality diff --git a/frontend/src/userPanel/profile/editor.ts b/frontend/src/userPanel/profile/editor.ts index de4dd7d..b67ff09 100644 --- a/frontend/src/userPanel/profile/editor.ts +++ b/frontend/src/userPanel/profile/editor.ts @@ -8,11 +8,12 @@ import { updateProfile } from './api'; import { loadProfile } from './api'; import { showSuccess, showError } from '../../utils/notification'; -import type { TextField } from 'mdui/components/text-field'; +import { TextField } from 'mdui/components/text-field'; +import { id } from '../../utils/utils'; -let profileForm = document.getElementById('profile-form')!; -let nicknameField = document.getElementById('username-field') as unknown as TextField; -let descriptionField = document.getElementById('description-field') as unknown as TextField; +let profileForm = id('profile-form')!; +let nicknameField = id('username-field'); +let descriptionField = id('description-field'); /** * Initialization state flag @@ -107,16 +108,10 @@ async function handleFormSubmission(e: Event): Promise { * @private */ function setupFormHandler(): void { - if (isInitialized) return; - - // Get DOM elements - profileForm = document.getElementById('profile-form')!; - nicknameField = document.getElementById('username-field') as TextField; - descriptionField = document.getElementById('description-field') as TextField; - - profileForm.addEventListener('submit', handleFormSubmission); - - isInitialized = true; + if (!isInitialized) { + profileForm.addEventListener('submit', handleFormSubmission); + isInitialized = true; + } } /** diff --git a/frontend/src/userPanel/profile/profile.ts b/frontend/src/userPanel/profile/profile.ts index 7910aa5..1cf8ba0 100644 --- a/frontend/src/userPanel/profile/profile.ts +++ b/frontend/src/userPanel/profile/profile.ts @@ -9,10 +9,11 @@ import type { Dialog } from "mdui/components/dialog"; import { loadProfileData } from './editor'; import { loadProfilePicture, initializeProfileUpload } from "./upload"; import { initializeProfileEditor } from './editor'; +import { id } from "../../utils/utils"; // Handle profile form submission -const form = document.getElementById("profile-form")!; -const dialog = document.getElementById("profile-dialog") as Dialog; +const form = id("profile-form")!; +const dialog = id("profile-dialog"); form.addEventListener("submit", async (e) => { e.preventDefault(); diff --git a/frontend/src/userPanel/profile/upload.ts b/frontend/src/userPanel/profile/upload.ts index 043d191..5327102 100644 --- a/frontend/src/userPanel/profile/upload.ts +++ b/frontend/src/userPanel/profile/upload.ts @@ -10,26 +10,25 @@ import { ImageCropper } from './imageCropper'; import { uploadProfilePicture } from './api'; import { loadProfile } from './api'; import { showSuccess, showError } from '../../utils/notification'; +import { id } from "../../utils/utils"; /** * Global image cropper instance - * @type {ImageCropper | null} */ let cropper: ImageCropper | null = null; /** * Initialization state flag - * @type {boolean} */ let isInitialized = false; -let cropperDialog = document.getElementById('cropper-dialog') as Dialog; -let fileInput = document.getElementById('pfp-file-input') as HTMLInputElement; -let uploadBtn = document.getElementById('upload-pfp-btn')!; -let cropSaveBtn = document.getElementById('crop-save')!; -let cropCancelBtn = document.getElementById('crop-cancel')!; -let cropperCloseBtn = document.getElementById('cropper-close')!; -let cropperArea = document.getElementById('cropper-area')!; +let cropperDialog = id('cropper-dialog'); +let fileInput = id('pfp-file-input'); +let uploadBtn = id('upload-pfp-btn'); +let cropSaveBtn = id('crop-save'); +let cropCancelBtn = id('crop-cancel'); +let cropperCloseBtn = id('cropper-close'); +let cropperArea = id('cropper-area'); /** * Opens the image cropper with the selected file @@ -81,7 +80,7 @@ async function saveCroppedImage(): Promise { if (result) { // Update profile picture display - const profilePicture = document.getElementById('profile-picture') as HTMLImageElement; + const profilePicture = id('profile-picture'); profilePicture.src = `${result.profile_picture_url}?t=${Date.now()}`; // Cache bust // Close cropper @@ -136,8 +135,8 @@ export async function loadProfilePicture(): Promise { if (userData?.profile_picture) { const url = `${userData.profile_picture}?t=${Date.now()}`; - const profilePicture = document.getElementById('profile-picture') as HTMLImageElement; - const profilePicture2 = document.getElementById("preview1") as HTMLImageElement; + const profilePicture = id('profile-picture'); + const profilePicture2 = id("preview1"); profilePicture.src = url; profilePicture2.src = url; } diff --git a/frontend/src/userPanel/settings.ts b/frontend/src/userPanel/settings.ts index 0b7d8f4..1d5d015 100644 --- a/frontend/src/userPanel/settings.ts +++ b/frontend/src/userPanel/settings.ts @@ -6,10 +6,11 @@ */ import type { Dialog } from "mdui/components/dialog"; +import { id } from "../utils/utils"; -const dialog = document.getElementById('settings-dialog') as Dialog; -const openButton = document.getElementById('settings-open')!; -const closeButton = document.getElementById('settings-close')!; +const dialog = id('settings-dialog'); +const openButton = id('settings-open')!; +const closeButton = id('settings-close')!; // Settings panel management const settingsList = document.querySelector('#settings-menu mdui-list')!; @@ -48,7 +49,7 @@ function handleListItemClick(item: Element): void { const panelId = panelMapping[itemText as keyof typeof panelMapping]; if (panelId) { - const targetPanel = document.getElementById(panelId); + const targetPanel = id(panelId); if (targetPanel) { targetPanel.classList.add('active'); } diff --git a/frontend/src/utils/utils.ts b/frontend/src/utils/utils.ts index 2220c33..184c0ef 100644 --- a/frontend/src/utils/utils.ts +++ b/frontend/src/utils/utils.ts @@ -30,4 +30,8 @@ export function formatTime(dateString: string): string { */ export function delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); +} + +export function id(id: string): T { + return document.getElementById(id) as unknown as T } \ No newline at end of file