Restructure code and documentation

This commit is contained in:
2025-08-25 16:28:43 +03:00
Unverified
parent 2999579bc5
commit 0f0db2041d
42 changed files with 198 additions and 306 deletions
+72
View File
@@ -0,0 +1,72 @@
import { API_BASE_URL } from "../core/config";
import { showLogin } from "../navigation";
import type { Headers, User } from "../core/types";
import { clearAlerts } from "./auth";
/**
* Current authenticated user information
* @type {User | null}
*/
export let currentUser: User | null = null;
/**
* JWT authentication token
* @type {string | null}
*/
export let authToken: string | null = null;
/**
* Sets the current user to the values provided.
* @param token The authentication JWT token.
* @param user The current authenticated user.
*/
export function setUser(token: string, user: User) {
authToken = token
currentUser = user
}
/**
* Generates authentication headers for API requests
* @param {boolean} json - Whether to include JSON content type header
* @returns {Headers} Headers object with authentication and content type
*/
export function getAuthHeaders(json: boolean = true): Headers {
const headers: Headers = {};
if (json) {
headers["Content-Type"] = "application/json";
}
if (authToken) {
headers['Authorization'] = `Bearer ${authToken}`;
}
return headers;
}
/**
* Checks authentication status on page load
*/
export async function checkAuthStatus(): Promise<void> {
// For JWT, we don't have a persistent token on page load
// So we'll just show the login form
showLogin();
}
/**
* Logs out the current user and clears session data
*/
export async function logout(): Promise<void> {
try {
await fetch(`${API_BASE_URL}/logout`, {
method: 'GET',
headers: getAuthHeaders()
});
} catch (error) {
console.error('Logout error:', error);
}
currentUser = null;
authToken = null;
showLogin();
clearAlerts();
}
@@ -5,90 +5,14 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import { loadMessages } from "./chat"; import { initializeProfile } from "../userPanel/profile/profile";
import { initializeProfile } from "./profile"; import type { ErrorResponse, LoginResponse, LoginRequest, RegisterRequest } from "../core/types";
import type { Headers, ErrorResponse, User, LoginResponse, LoginRequest, RegisterRequest } from "./types"; import { API_BASE_URL } from "../core/config";
import { API_BASE_URL } from "./config"; import { loadChat, showLogin, showRegister } from "../navigation";
import { setUser } from "./api";
/**
* Current authenticated user information
* @type {User | null}
*/
export let currentUser: User | null = null;
/**
* JWT authentication token
* @type {string | null}
*/
export let authToken: string | null = null;
/**
* Generates authentication headers for API requests
* @param {boolean} json - Whether to include JSON content type header
* @returns {Headers} Headers object with authentication and content type
* @function getAuthHeaders
* @example
* const headers = getAuthHeaders();
* fetch('/api/endpoint', { headers });
*/
export function getAuthHeaders(json: boolean = true): Headers {
const headers: Headers = {};
if (json) {
headers["Content-Type"] = "application/json";
}
if (authToken) {
headers['Authorization'] = `Bearer ${authToken}`;
}
return headers;
}
/**
* Shows the login form and hides other interfaces
* @function showLogin
* @example
* showLogin();
*/
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';
clearAlerts();
document.getElementById("electron-title-bar")!.classList.add("color-surface");
}
/**
* Shows the registration form and hides other interfaces
* @function showRegister
* @example
* showRegister();
*/
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';
clearAlerts();
document.getElementById("electron-title-bar")!.classList.add("color-surface");
}
/**
* Shows the chat interface and hides authentication forms
* @function showChat
* @example
* showChat();
*/
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';
loadMessages();
document.getElementById("electron-title-bar")!.classList.remove("color-surface");
}
/** /**
* Clears all alert messages from authentication forms * Clears all alert messages from authentication forms
* @function clearAlerts
* @private * @private
*/ */
export function clearAlerts(): void { export function clearAlerts(): void {
@@ -101,9 +25,6 @@ export function clearAlerts(): void {
* @param {string} containerId - ID of the container to show the alert in * @param {string} containerId - ID of the container to show the alert in
* @param {string} message - Alert message to display * @param {string} message - Alert message to display
* @param {'success' | 'danger'} type - Type of alert (success or danger) * @param {'success' | 'danger'} type - Type of alert (success or danger)
* @function showAlert
* @example
* showAlert('login-alerts', 'Login successful!', 'success');
*/ */
export function showAlert(containerId: string, message: string, type: "success" | "danger" = 'danger'): void { export function showAlert(containerId: string, message: string, type: "success" | "danger" = 'danger'): void {
const container = document.getElementById(containerId)!; const container = document.getElementById(containerId)!;
@@ -115,10 +36,7 @@ export function showAlert(containerId: string, message: string, type: "success"
/** /**
* Handles login form submission * Handles login form submission
* @async
* @function handleLogin
* @param {Event} e - Form submission event * @param {Event} e - Form submission event
* @private
*/ */
async function handleLogin(e: Event): Promise<void> { async function handleLogin(e: Event): Promise<void> {
e.preventDefault(); e.preventDefault();
@@ -151,10 +69,8 @@ async function handleLogin(e: Event): Promise<void> {
if (response.ok) { if (response.ok) {
const data: LoginResponse = await response.json(); const data: LoginResponse = await response.json();
// Store the JWT token // Store the JWT token
authToken = data.token; setUser(data.token, data.user)
currentUser = data.user; loadChat();
showChat();
loadMessages(); // Start loading messages
initializeProfile(); // Initialize profile after login initializeProfile(); // Initialize profile after login
} else { } else {
const data: ErrorResponse = await response.json(); const data: ErrorResponse = await response.json();
@@ -167,8 +83,6 @@ async function handleLogin(e: Event): Promise<void> {
/** /**
* Handles registration form submission * Handles registration form submission
* @async
* @function handleRegister
* @param {Event} e - Form submission event * @param {Event} e - Form submission event
* @private * @private
*/ */
@@ -234,72 +148,15 @@ async function handleRegister(e: Event): Promise<void> {
} }
/** /**
* Logs out the current user and clears session data * Initializes authentication functionality
* @async
* @function logout
* @example
* await logout();
*/
export async function logout(): Promise<void> {
try {
await fetch(`${API_BASE_URL}/logout`, {
method: 'GET',
headers: getAuthHeaders()
});
} catch (error) {
console.error('Logout error:', error);
}
currentUser = null;
authToken = null;
showLogin();
clearAlerts();
}
/**
* Loads the chat interface and initializes messaging
* @function loadChat
* @example
* loadChat();
*/
export function loadChat(): void {
showChat();
loadMessages();
}
/**
* Checks authentication status on page load
* @async
* @function checkAuthStatus
* @example
* await checkAuthStatus();
*/
export async function checkAuthStatus(): Promise<void> {
// For JWT, we don't have a persistent token on page load
// So we'll just show the login form
showLogin();
}
/**
* Sets up authentication form event listeners
* @function setupAuthForms
* @private * @private
*/ */
function setupAuthForms(): void { function init(): void {
document.getElementById('login-form-element')!.addEventListener('submit', handleLogin); document.getElementById('login-form-element')!.addEventListener('submit', handleLogin);
document.getElementById('register-form-element')!.addEventListener('submit', handleRegister); document.getElementById('register-form-element')!.addEventListener('submit', handleRegister);
}
/**
* Initializes links
* @function setupLinks
* @private
*/
function setupLinks(): void {
document.getElementById("login-link")!.addEventListener("click", showLogin); document.getElementById("login-link")!.addEventListener("click", showLogin);
document.getElementById("register-link")!.addEventListener("click", showRegister); document.getElementById("register-link")!.addEventListener("click", showRegister);
} }
// Initialize authentication forms init();
setupAuthForms();
setupLinks();
@@ -5,22 +5,19 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import { getAuthHeaders, currentUser, authToken } from "./auth"; import { API_BASE_URL } from "../core/config";
import { API_BASE_URL } from "./config"; import { websocket } from "../websocket";
import { websocket } from "./websocket"; import type { Message, Messages, WebSocketMessage } from "../core/types";
import type { Message, Messages, WebSocketMessage } from "./types"; import { formatTime } from "../utils/utils";
import { formatTime } from "./utils/utils"; import { show as showContextMenu } from "./contextMenu";
import { show as showContextMenu } from "./message-context-menu"; import { show as showUserProfileDialog } from "./profileDialog";
import { show as showUserProfileDialog } from "./user-profile-dialog"; import defaultAvatar from "../resources/images/default-avatar.png";
import defaultAvatar from "./images/default-avatar.png"; import { authToken, currentUser, getAuthHeaders } from "../auth/api";
/** /**
* Adds a new message to the chat interface * Adds a new message to the chat interface
* @param {Message} message - Message object to display * @param {Message} message - Message object to display
* @param {boolean} isAuthor - Whether the current user is the message author * @param {boolean} isAuthor - Whether the current user is the message author
* @function addMessage
* @example
* addMessage(messageData, messageData.username === currentUser.username);
*/ */
export function addMessage(message: Message, isAuthor: boolean): void { export function addMessage(message: Message, isAuthor: boolean): void {
const messagesContainer = document.querySelector('.chat-messages') as HTMLElement; const messagesContainer = document.querySelector('.chat-messages') as HTMLElement;
@@ -127,9 +124,6 @@ export function addMessage(message: Message, isAuthor: boolean): void {
/** /**
* Loads chat messages from the server * Loads chat messages from the server
* @function loadMessages
* @example
* loadMessages();
*/ */
export function loadMessages(): void { export function loadMessages(): void {
fetch(`${API_BASE_URL}/get_messages`, { fetch(`${API_BASE_URL}/get_messages`, {
@@ -158,9 +152,6 @@ export function loadMessages(): void {
/** /**
* Sends a message via WebSocket * Sends a message via WebSocket
* @function sendMessage
* @example
* sendMessage();
*/ */
export function sendMessage(): void { export function sendMessage(): void {
const input = document.querySelector('.message-input') as HTMLInputElement; const input = document.querySelector('.message-input') as HTMLInputElement;
@@ -202,7 +193,6 @@ document.getElementById('message-form')!.addEventListener('submit', (e) => {
/** /**
* Updates an existing message in the chat interface * Updates an existing message in the chat interface
* @param {Message} message - Updated message object * @param {Message} message - Updated message object
* @function updateMessage
*/ */
export function updateMessage(message: Message): void { export function updateMessage(message: Message): void {
const messageElement = document.querySelector(`[data-id="${message.id}"]`) as HTMLElement; const messageElement = document.querySelector(`[data-id="${message.id}"]`) as HTMLElement;
@@ -227,7 +217,6 @@ export function updateMessage(message: Message): void {
/** /**
* Removes a message from the chat interface * Removes a message from the chat interface
* @param {number} messageId - ID of the message to remove * @param {number} messageId - ID of the message to remove
* @function removeMessage
*/ */
export function removeMessage(messageId: number): void { export function removeMessage(messageId: number): void {
const messageElement = document.querySelector(`[data-id="${messageId}"]`) as HTMLElement; const messageElement = document.querySelector(`[data-id="${messageId}"]`) as HTMLElement;
@@ -239,7 +228,6 @@ export function removeMessage(messageId: number): void {
/** /**
* Handles WebSocket message updates * Handles WebSocket message updates
* @param {WebSocketMessage} response - WebSocket response * @param {WebSocketMessage} response - WebSocket response
* @function handleWebSocketMessage
*/ */
export function handleWebSocketMessage(response: WebSocketMessage): void { export function handleWebSocketMessage(response: WebSocketMessage): void {
switch (response.type) { switch (response.type) {
@@ -5,13 +5,13 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import { currentUser, authToken } from "./auth"; import { websocket } from "../websocket";
import { websocket } from "./websocket"; import type { Message, WebSocketMessage } from "../core/types";
import type { Message, WebSocketMessage } from "./types"; import { showSuccess, showError } from "../utils/notification";
import { showSuccess, showError } from "./utils/notification"; import { delay } from "../utils/utils";
import { delay } from "./utils/utils";
import type { Dialog } from "mdui/components/dialog"; import type { Dialog } from "mdui/components/dialog";
import type { TextField } from "mdui/components/text-field"; import type { TextField } from "mdui/components/text-field";
import { currentUser, authToken } from "../auth/api";
let menu = document.getElementById("message-context-menu")!; let menu = document.getElementById("message-context-menu")!;
@@ -83,7 +83,7 @@ export function show(message: Message, x: number, y: number): void {
const deleteItem = menu.querySelector('[data-action="delete"]') as HTMLElement; const deleteItem = menu.querySelector('[data-action="delete"]') as HTMLElement;
const isAuthor = message.username === currentUser?.username; const isAuthor = message.username === currentUser?.username;
const isOwner = !!currentUser?.admin; const isOwner = currentUser?.admin;
editItem.style.display = isAuthor ? 'flex' : 'none'; editItem.style.display = isAuthor ? 'flex' : 'none';
deleteItem.style.display = (isAuthor || isOwner) ? 'flex' : 'none'; deleteItem.style.display = (isAuthor || isOwner) ? 'flex' : 'none';
@@ -5,12 +5,12 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import { getAuthHeaders, currentUser } from "./auth"; import { getAuthHeaders, currentUser } from "../auth/api";
import { API_BASE_URL } from "./config"; import { API_BASE_URL } from "../core/config";
import type { UserProfile } from "./types"; import type { UserProfile } from "../core/types";
import { showError, showSuccess } from "./utils/notification"; import { showError, showSuccess } from "../utils/notification";
import { formatTime } from "./utils/utils"; import { formatTime } from "../utils/utils";
import defaultAvatar from "./images/default-avatar.png"; import defaultAvatar from "../resources/images/default-avatar.png";
let dialog = document.getElementById("user-profile-dialog")!; let dialog = document.getElementById("user-profile-dialog")!;
@@ -7,21 +7,18 @@
/** /**
* Base API endpoint for all backend requests * Base API endpoint for all backend requests
* @type {string}
* @constant * @constant
*/ */
export const API_BASE_URL: string = '/api'; export const API_BASE_URL = `${location.host || "https://fromchat.toolbox-io.ru"}/api`;
/** /**
* Full API URL including hostname and port for WebSocket connections * Full API URL including hostname and port for WebSocket connections
* @type {string}
* @constant * @constant
*/ */
export const API_FULL_BASE_URL: string = `${location.host}/api`; export const API_WS_BASE_URL = `${location.host || "fromchat.toolbox-io.ru"}/api`;
/** /**
* Application name displayed in UI and document title * Application name displayed in UI and document title
* @type {string}
* @constant * @constant
*/ */
export const PRODUCT_NAME: string = "FromChat"; export const PRODUCT_NAME = "FromChat";
@@ -5,7 +5,7 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import { showLogin } from "./auth"; import { showLogin } from "../navigation";
import { PRODUCT_NAME } from "./config"; import { PRODUCT_NAME } from "./config";
showLogin(); showLogin();
+8 -1
View File
@@ -1,5 +1,12 @@
/**
* @fileoverview Electron-specific code
* @description This module initializes Electron-specific functionality.
* @author denis0001-dev
* @version 1.0.0
*/
import "../../electron.d.ts"; import "../../electron.d.ts";
import { PRODUCT_NAME } from "../config.ts"; import { PRODUCT_NAME } from "../core/config.ts";
if (window.electronInterface !== undefined) { if (window.electronInterface !== undefined) {
console.log("Running in Electron"); console.log("Running in Electron");
+8 -8
View File
@@ -5,15 +5,15 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import './css/style.scss'; import './resources/css/style.scss';
import "mdui/mdui.css"; import "mdui/mdui.css";
import "./utils/material"; import "./utils/material";
import "./chat"; import "./chat/chat";
import "./settings"; import "./userPanel/settings";
import "./leftpanel"; import "./userPanel/leftpanel";
import "./init"; import "./core/init";
import "./profile"; import "./userPanel/profile/profile";
import "./message-context-menu"; import "./chat/contextMenu";
import "./user-profile-dialog"; import "./chat/profileDialog";
import "./electron/electron"; import "./electron/electron";
+43
View File
@@ -0,0 +1,43 @@
import { clearAlerts } from "./auth/auth";
import { loadMessages } from "./chat/chat";
/**
* 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';
clearAlerts();
document.getElementById("electron-title-bar")!.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';
clearAlerts();
document.getElementById("electron-title-bar")!.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';
loadMessages();
document.getElementById("electron-title-bar")!.classList.remove("color-surface");
}
/**
* Loads the chat interface and initializes messaging.
*/
export function loadChat(): void {
showChat();
loadMessages();
}

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

@@ -20,7 +20,6 @@ const dialogClose = document.getElementById("profile-dialog-close")!;
/** /**
* Sets up chat collapse functionality * Sets up chat collapse functionality
* @function setupChatCollapse
* @private * @private
*/ */
function setupChatCollapse(): void { function setupChatCollapse(): void {
@@ -32,7 +31,6 @@ function setupChatCollapse(): void {
/** /**
* Sets up chat switching functionality * Sets up chat switching functionality
* @function setupChatSwitching
* @private * @private
*/ */
function setupChatSwitching(): void { function setupChatSwitching(): void {
@@ -51,7 +49,6 @@ function setupChatSwitching(): void {
/** /**
* Sets up profile dialog functionality * Sets up profile dialog functionality
* @function setupProfileDialog
* @private * @private
*/ */
function setupProfileDialog(): void { function setupProfileDialog(): void {
@@ -5,14 +5,13 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import { getAuthHeaders } from '../auth'; import { getAuthHeaders } from '../../auth/api';
import type { ProfileData, UploadResponse } from './types'; import type { ProfileData, UploadResponse } from './types';
/** /**
* Loads user profile data from the server * Loads user profile data from the server
* @async * @async
* @function loadProfile * @returns User profile data or null if failed
* @returns {Promise<ProfileData | null>} User profile data or null if failed
* @example * @example
* const profile = await loadProfile(); * const profile = await loadProfile();
* if (profile) { * if (profile) {
@@ -38,8 +37,6 @@ export async function loadProfile(): Promise<ProfileData | null> {
/** /**
* Uploads a profile picture to the server * Uploads a profile picture to the server
* @async
* @function uploadProfilePicture
* @param {Blob} file - The image file to upload * @param {Blob} file - The image file to upload
* @returns {Promise<UploadResponse | null>} Upload response with URL or null if failed * @returns {Promise<UploadResponse | null>} Upload response with URL or null if failed
* @example * @example
@@ -73,8 +70,6 @@ export async function uploadProfilePicture(file: Blob): Promise<UploadResponse |
/** /**
* Updates user profile information * Updates user profile information
* @async
* @function updateProfile
* @param {Partial<ProfileData>} data - Profile data to update * @param {Partial<ProfileData>} data - Profile data to update
* @returns {Promise<boolean>} True if update was successful, false otherwise * @returns {Promise<boolean>} True if update was successful, false otherwise
* @example * @example
@@ -103,8 +98,6 @@ export async function updateProfile(data: Partial<ProfileData>): Promise<boolean
/** /**
* Updates user bio * Updates user bio
* @async
* @function updateBio
* @param {string} bio - New bio text * @param {string} bio - New bio text
* @returns {Promise<boolean>} True if update was successful, false otherwise * @returns {Promise<boolean>} True if update was successful, false otherwise
* @example * @example
@@ -7,7 +7,7 @@
import { updateProfile } from './api'; import { updateProfile } from './api';
import { loadProfile } from './api'; import { loadProfile } from './api';
import { showSuccess, showError } from '../utils/notification'; import { showSuccess, showError } from '../../utils/notification';
import type { TextField } from 'mdui/components/text-field'; import type { TextField } from 'mdui/components/text-field';
let profileForm = document.getElementById('profile-form')!; let profileForm = document.getElementById('profile-form')!;
@@ -23,9 +23,6 @@ let isInitialized = false;
/** /**
* Sets the username field value * Sets the username field value
* @param {string} value - The username value to set * @param {string} value - The username value to set
* @function setUsernameValue
* @example
* setUsernameValue('John Doe');
*/ */
export function setUsernameValue(value: string): void { export function setUsernameValue(value: string): void {
if (nicknameField && nicknameField.value !== undefined) { if (nicknameField && nicknameField.value !== undefined) {
@@ -36,9 +33,6 @@ export function setUsernameValue(value: string): void {
/** /**
* Sets the description field value * Sets the description field value
* @param {string} value - The description value to set * @param {string} value - The description value to set
* @function setDescriptionValue
* @example
* setDescriptionValue('Software Developer');
*/ */
export function setDescriptionValue(value: string): void { export function setDescriptionValue(value: string): void {
if (descriptionField && descriptionField.value !== undefined) { if (descriptionField && descriptionField.value !== undefined) {
@@ -49,10 +43,6 @@ export function setDescriptionValue(value: string): void {
/** /**
* Gets the current username field value * Gets the current username field value
* @returns {string} The current username value * @returns {string} The current username value
* @function getUsernameValue
* @example
* const username = getUsernameValue();
* console.log('Current username:', username);
*/ */
export function getUsernameValue(): string { export function getUsernameValue(): string {
if (nicknameField && nicknameField.value !== undefined) { if (nicknameField && nicknameField.value !== undefined) {
@@ -64,10 +54,6 @@ export function getUsernameValue(): string {
/** /**
* Gets the current description field value * Gets the current description field value
* @returns {string} The current description value * @returns {string} The current description value
* @function getDescriptionValue
* @example
* const description = getDescriptionValue();
* console.log('Current description:', description);
*/ */
export function getDescriptionValue(): string { export function getDescriptionValue(): string {
if (descriptionField && descriptionField.value !== undefined) { if (descriptionField && descriptionField.value !== undefined) {
@@ -78,10 +64,6 @@ export function getDescriptionValue(): string {
/** /**
* Loads profile data from the server and populates the form fields * Loads profile data from the server and populates the form fields
* @async
* @function loadProfileData
* @example
* await loadProfileData();
*/ */
export async function loadProfileData(): Promise<void> { export async function loadProfileData(): Promise<void> {
const userData = await loadProfile(); const userData = await loadProfile();
@@ -97,8 +79,6 @@ export async function loadProfileData(): Promise<void> {
/** /**
* Handles profile form submission * Handles profile form submission
* @async
* @function handleFormSubmission
* @param {Event} e - Form submission event * @param {Event} e - Form submission event
* @private * @private
*/ */
@@ -124,7 +104,6 @@ async function handleFormSubmission(e: Event): Promise<void> {
/** /**
* Sets up form submission handler * Sets up form submission handler
* @function setupFormHandler
* @private * @private
*/ */
function setupFormHandler(): void { function setupFormHandler(): void {
@@ -142,9 +121,6 @@ function setupFormHandler(): void {
/** /**
* Initializes profile editor functionality * Initializes profile editor functionality
* @function initializeProfileEditor
* @example
* initializeProfileEditor();
*/ */
export function initializeProfileEditor(): void { export function initializeProfileEditor(): void {
setupFormHandler(); setupFormHandler();
@@ -5,7 +5,7 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import type { Size2D } from "../types"; import type { Size2D } from "../../core/types";
/** /**
* Image cropper class for circular profile picture cropping * Image cropper class for circular profile picture cropping
@@ -63,7 +63,6 @@ export class ImageCropper {
/** /**
* Sets up mouse and touch event listeners * Sets up mouse and touch event listeners
* @function setupEventListeners
* @private * @private
*/ */
private setupEventListeners(): void { private setupEventListeners(): void {
@@ -78,7 +77,6 @@ export class ImageCropper {
/** /**
* Handles mouse down events * Handles mouse down events
* @param {MouseEvent} e - Mouse event * @param {MouseEvent} e - Mouse event
* @function onMouseDown
* @private * @private
*/ */
private onMouseDown(e: MouseEvent): void { private onMouseDown(e: MouseEvent): void {
@@ -89,7 +87,6 @@ export class ImageCropper {
/** /**
* Handles mouse move events during dragging * Handles mouse move events during dragging
* @param {MouseEvent} e - Mouse event * @param {MouseEvent} e - Mouse event
* @function onMouseMove
* @private * @private
*/ */
private onMouseMove(e: MouseEvent): void { private onMouseMove(e: MouseEvent): void {
@@ -107,7 +104,6 @@ export class ImageCropper {
/** /**
* Handles mouse up events * Handles mouse up events
* @function onMouseUp
* @private * @private
*/ */
private onMouseUp(): void { private onMouseUp(): void {
@@ -117,7 +113,6 @@ export class ImageCropper {
/** /**
* Handles touch start events * Handles touch start events
* @param {TouchEvent} e - Touch event * @param {TouchEvent} e - Touch event
* @function onTouchStart
* @private * @private
*/ */
private onTouchStart(e: TouchEvent): void { private onTouchStart(e: TouchEvent): void {
@@ -130,7 +125,6 @@ export class ImageCropper {
/** /**
* Handles touch move events during dragging * Handles touch move events during dragging
* @param {TouchEvent} e - Touch event * @param {TouchEvent} e - Touch event
* @function onTouchMove
* @private * @private
*/ */
private onTouchMove(e: TouchEvent): void { private onTouchMove(e: TouchEvent): void {
@@ -150,7 +144,6 @@ export class ImageCropper {
/** /**
* Handles touch end events * Handles touch end events
* @function onTouchEnd
* @private * @private
*/ */
private onTouchEnd(): void { private onTouchEnd(): void {
@@ -161,9 +154,6 @@ export class ImageCropper {
* Loads an image file for cropping * Loads an image file for cropping
* @param {File} file - Image file to load * @param {File} file - Image file to load
* @returns {Promise<void>} Promise that resolves when image is loaded * @returns {Promise<void>} Promise that resolves when image is loaded
* @async
* @example
* await cropper.loadImage(fileInput.files[0]);
*/ */
loadImage(file: File): Promise<void> { loadImage(file: File): Promise<void> {
return new Promise((resolve) => { return new Promise((resolve) => {
@@ -178,7 +168,6 @@ export class ImageCropper {
/** /**
* Renders the image with circular crop overlay * Renders the image with circular crop overlay
* @function render
* @private * @private
*/ */
private render(): void { private render(): void {
@@ -216,9 +205,6 @@ export class ImageCropper {
/** /**
* Gets the cropped image as a data URL * Gets the cropped image as a data URL
* @returns {string} Data URL of the cropped image * @returns {string} Data URL of the cropped image
* @example
* const croppedImage = cropper.getCroppedImage();
* // Use croppedImage as src for an img element
*/ */
getCroppedImage(): string { getCroppedImage(): string {
return this.canvas.toDataURL('image/jpeg', 0.8); return this.canvas.toDataURL('image/jpeg', 0.8);
@@ -226,9 +212,6 @@ export class ImageCropper {
/** /**
* Destroys the cropper and removes the canvas from DOM * Destroys the cropper and removes the canvas from DOM
* @function destroy
* @example
* cropper.destroy();
*/ */
destroy(): void { destroy(): void {
if (this.canvas.parentNode) { if (this.canvas.parentNode) {
@@ -6,9 +6,9 @@
*/ */
import type { Dialog } from "mdui/components/dialog"; import type { Dialog } from "mdui/components/dialog";
import { loadProfileData } from './profile/editor'; import { loadProfileData } from './editor';
import { loadProfilePicture, initializeProfileUpload } from "./profile/upload"; import { loadProfilePicture, initializeProfileUpload } from "./upload";
import { initializeProfileEditor } from './profile/editor'; import { initializeProfileEditor } from './editor';
// Handle profile form submission // Handle profile form submission
const form = document.getElementById("profile-form")!; const form = document.getElementById("profile-form")!;
@@ -24,10 +24,6 @@ form.addEventListener("submit", async (e) => {
/** /**
* Initializes profile functionality after user login * Initializes profile functionality after user login
* @function initializeProfile
* @example
* // Called after successful authentication
* initializeProfile();
*/ */
export function initializeProfile(): void { export function initializeProfile(): void {
// Initialize profile modules // Initialize profile modules
@@ -6,10 +6,10 @@
*/ */
import type { Dialog } from "mdui/components/dialog"; import type { Dialog } from "mdui/components/dialog";
import { ImageCropper } from './image-cropper'; import { ImageCropper } from './imageCropper';
import { uploadProfilePicture } from './api'; import { uploadProfilePicture } from './api';
import { loadProfile } from './api'; import { loadProfile } from './api';
import { showSuccess, showError } from '../utils/notification'; import { showSuccess, showError } from '../../utils/notification';
/** /**
* Global image cropper instance * Global image cropper instance
@@ -33,8 +33,6 @@ let cropperArea = document.getElementById('cropper-area')!;
/** /**
* Opens the image cropper with the selected file * Opens the image cropper with the selected file
* @async
* @function openCropper
* @param {File} file - The image file to crop * @param {File} file - The image file to crop
* @private * @private
*/ */
@@ -54,7 +52,6 @@ async function openCropper(file: File): Promise<void> {
/** /**
* Closes the image cropper and cleans up resources * Closes the image cropper and cleans up resources
* @function closeCropper
* @private * @private
*/ */
function closeCropper(): void { function closeCropper(): void {
@@ -69,8 +66,6 @@ function closeCropper(): void {
/** /**
* Saves the cropped image and uploads it to the server * Saves the cropped image and uploads it to the server
* @async
* @function saveCroppedImage
* @private * @private
*/ */
async function saveCroppedImage(): Promise<void> { async function saveCroppedImage(): Promise<void> {
@@ -87,7 +82,7 @@ async function saveCroppedImage(): Promise<void> {
if (result) { if (result) {
// Update profile picture display // Update profile picture display
const profilePicture = document.getElementById('profile-picture') as HTMLImageElement; const profilePicture = document.getElementById('profile-picture') as HTMLImageElement;
profilePicture.src = result.profile_picture_url + '?t=' + Date.now(); // Cache bust profilePicture.src = `${result.profile_picture_url}?t=${Date.now()}`; // Cache bust
// Close cropper // Close cropper
closeCropper(); closeCropper();
@@ -101,7 +96,6 @@ async function saveCroppedImage(): Promise<void> {
/** /**
* Sets up event listeners for upload functionality * Sets up event listeners for upload functionality
* @function setupEventListeners
* @private * @private
*/ */
function setupEventListeners(): void { function setupEventListeners(): void {
@@ -136,9 +130,6 @@ function setupEventListeners(): void {
/** /**
* Loads and displays the user's profile picture * Loads and displays the user's profile picture
* @async * @async
* @function loadProfilePicture
* @example
* await loadProfilePicture();
*/ */
export async function loadProfilePicture(): Promise<void> { export async function loadProfilePicture(): Promise<void> {
const userData = await loadProfile(); const userData = await loadProfile();
@@ -154,9 +145,6 @@ export async function loadProfilePicture(): Promise<void> {
/** /**
* Initializes profile upload functionality * Initializes profile upload functionality
* @function initializeProfileUpload
* @example
* initializeProfileUpload();
*/ */
export function initializeProfileUpload(): void { export function initializeProfileUpload(): void {
setupEventListeners(); setupEventListeners();
@@ -32,7 +32,6 @@ const panelMapping = {
/** /**
* Handles click events on settings list items * Handles click events on settings list items
* @param {Element} item - The clicked list item element * @param {Element} item - The clicked list item element
* @function handleListItemClick
* @private * @private
*/ */
function handleListItemClick(item: Element): void { function handleListItemClick(item: Element): void {
@@ -58,7 +57,6 @@ function handleListItemClick(item: Element): void {
/** /**
* Sets up click listeners for all settings list items * Sets up click listeners for all settings list items
* @function setupSettingsNavigation
* @private * @private
*/ */
function setupSettingsNavigation(): void { function setupSettingsNavigation(): void {
@@ -70,7 +68,6 @@ function setupSettingsNavigation(): void {
/** /**
* Resets settings dialog to show the first panel * Resets settings dialog to show the first panel
* @function resetToFirstPanel
* @private * @private
*/ */
function resetToFirstPanel(): void { function resetToFirstPanel(): void {
@@ -86,7 +83,6 @@ function resetToFirstPanel(): void {
/** /**
* Sets up dialog event listeners * Sets up dialog event listeners
* @function setupDialogListeners
* @private * @private
*/ */
function setupDialogListeners(): void { function setupDialogListeners(): void {
-7
View File
@@ -15,7 +15,6 @@ export type NotificationType = 'success' | 'error';
* Shows a notification with the specified message and type * Shows a notification with the specified message and type
* @param {string} message - The message to display * @param {string} message - The message to display
* @param {NotificationType} type - The type of notification (success or error) * @param {NotificationType} type - The type of notification (success or error)
* @function showNotification
* @private * @private
*/ */
function showNotification(message: string, type: NotificationType): void { function showNotification(message: string, type: NotificationType): void {
@@ -49,9 +48,6 @@ function showNotification(message: string, type: NotificationType): void {
/** /**
* Shows a success notification * Shows a success notification
* @param {string} message - The success message to display * @param {string} message - The success message to display
* @function showSuccess
* @example
* showSuccess('Profile updated successfully!');
*/ */
export function showSuccess(message: string): void { export function showSuccess(message: string): void {
showNotification(message, 'success'); showNotification(message, 'success');
@@ -60,9 +56,6 @@ export function showSuccess(message: string): void {
/** /**
* Shows an error notification * Shows an error notification
* @param {string} message - The error message to display * @param {string} message - The error message to display
* @function showError
* @example
* showError('Failed to update profile');
*/ */
export function showError(message: string): void { export function showError(message: string): void {
showNotification(message, 'error'); showNotification(message, 'error');
+21 -15
View File
@@ -5,14 +5,12 @@
* @version 1.0.0 * @version 1.0.0
*/ */
import { handleWebSocketMessage } from "./chat"; import { handleWebSocketMessage } from "./chat/chat";
import { API_FULL_BASE_URL } from "./config"; import { API_WS_BASE_URL } from "./core/config";
import type { WebSocketMessage } from "./types";
import { delay } from "./utils/utils"; import { delay } from "./utils/utils";
/** /**
* Creates a new WebSocket connection to the chat server * Creates a new WebSocket connection to the chat server
* @function create
* @returns {WebSocket} New WebSocket instance * @returns {WebSocket} New WebSocket instance
* @private * @private
*/ */
@@ -22,7 +20,7 @@ function create(): WebSocket {
prefix = "wss://"; prefix = "wss://";
} }
return new WebSocket(`${prefix}${API_FULL_BASE_URL}/chat/ws`); return new WebSocket(`${prefix}${API_WS_BASE_URL}/chat/ws`);
} }
/** /**
@@ -31,16 +29,14 @@ function create(): WebSocket {
*/ */
export let websocket: WebSocket = create(); export let websocket: WebSocket = create();
// -------------- /**
// Initialization * This function will wait 3 seconds and them attempts to reconnect the WebSocket.
// -------------- * If it fails, tries again in an endless loop until the connection is established
* again.
websocket.addEventListener("message", (e) => { *
const message: WebSocketMessage = JSON.parse(e.data); * @private
handleWebSocketMessage(message); */
}); async function onError() {
websocket.addEventListener("error", async () => {
console.warn("WebSocket disconnected, retrying in 3 seconds..."); console.warn("WebSocket disconnected, retrying in 3 seconds...");
await delay(3000); await delay(3000);
websocket = create(); websocket = create();
@@ -52,4 +48,14 @@ websocket.addEventListener("error", async () => {
} }
websocket.addEventListener("open", listener); websocket.addEventListener("open", listener);
websocket.addEventListener("error", onError);
}
// --------------
// Initialization
// --------------
websocket.addEventListener("message", (e) => {
handleWebSocketMessage(JSON.parse(e.data));
}); });
websocket.addEventListener("error", onError);