Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
eacb625
Set up basic project structure
Galane-dev Feb 10, 2026
ffc2019
Merge pull request #2 from Galane-dev/project/structure
Galane-dev Feb 10, 2026
458e403
Created basic local storage service
Galane-dev Feb 10, 2026
6a85e5e
Merge pull request #5 from Galane-dev/feature/local-storage
Galane-dev Feb 10, 2026
de29e6c
Added basic user model
Galane-dev Feb 10, 2026
a8683a4
Added models for chat, and message
Galane-dev Feb 10, 2026
eae0b37
Added session manager for log in and log out
Galane-dev Feb 10, 2026
20bbbcb
Merge pull request #7 from Galane-dev/feature/models
Galane-dev Feb 10, 2026
02314d3
added html for landing page
Galane-dev Feb 10, 2026
fbb8eb6
Added html code for landing page
Galane-dev Feb 10, 2026
122ce4e
Implemented auth functionality
Galane-dev Feb 11, 2026
5d93d12
Merge pull request #9 from Galane-dev/page/landing-page
Galane-dev Feb 11, 2026
e9f0173
added redirect to main page after login
Galane-dev Feb 11, 2026
2449b55
Merge pull request #11 from Galane-dev/page/landing-page
Galane-dev Feb 11, 2026
b39f107
Added users list and open chat functionality to main page
Galane-dev Feb 11, 2026
06fab54
Added list of users and open chat functionality
Galane-dev Feb 11, 2026
a35bc7d
Added send message functionality
Galane-dev Feb 11, 2026
846527d
Added send message functionality
Galane-dev Feb 11, 2026
5865d5a
Implemented show hide for messages
Galane-dev Feb 11, 2026
585ad2d
Basic HTML structure complete
Galane-dev Feb 11, 2026
5037d41
Added user search
Galane-dev Feb 11, 2026
0dfd80c
Added user search to main page
Galane-dev Feb 11, 2026
21c5ecf
Added online offline checks
Galane-dev Feb 11, 2026
52f3b69
Implemented edit profile functionality
Galane-dev Feb 11, 2026
01b313f
implemented logout functionality
Galane-dev Feb 11, 2026
7936d45
Added logout functionality
Galane-dev Feb 11, 2026
45d1e4d
implemented user side profile
Galane-dev Feb 11, 2026
b301a61
added groups
Galane-dev Feb 12, 2026
f243f3b
Merge pull request #17 from Galane-dev/page/main
Galane-dev Feb 12, 2026
0f13d7e
Added nav section styling
Galane-dev Feb 12, 2026
141e3a0
Added nav section styling
Galane-dev Feb 12, 2026
6b17572
Added chat styling
Galane-dev Feb 12, 2026
e6868d1
Added chat styling
Galane-dev Feb 12, 2026
69e9a50
added user profile edit styling
Galane-dev Feb 12, 2026
1b377cb
Merge pull request #20 from Galane-dev/page/main
Galane-dev Feb 12, 2026
ec03258
Added navigation
Galane-dev Feb 12, 2026
3cd593c
Added navigation for mobile clients
Galane-dev Feb 12, 2026
8bc39c8
refactored main page js
Galane-dev Feb 12, 2026
213b2c9
Merge pull request #23 from Galane-dev/process/refactoring
Galane-dev Feb 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/images/icons/add.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/icons/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/icons/profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/icons/search.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/icons/send.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/icons/user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>Authenticate | LocalChat</title>
<link href="./styles/landing-page.css" rel="stylesheet">
</head>
<body>
<main>
<article>
<div id="landing-message">
<img id="logo-image" src="./assets/images/logo.png" alt="logo image">
<h1 id="welcome-heading">Welcome Back...</h1>
<p id="welcome-message">LocalChat is a local chat app<br>
Your data is stored securely in your own <br>computer, no one but you will be able to access the data</p>
</div>
</article>
<!--The login form on the side-->
<aside>
<section id="landing-form">
<h3 id="text-logo">Local<span>Chat</span></h3>
<form>
<input id="username-input" name="username-input" type="text" placeholder="Enter username">
<input id="password-input" name="password-input" type="password" placeholder="Enter password">
<button id="submit-button" name="submit-button" type="button">Sign In</button>
</form>
<p id="toggle-question">Don't have an account?</p>
<p id="toggle-action">Sign Up</p>
</section>
</aside>
</main>
<footer></footer>
<script type="module" src="./js/landing-page.js"></script>
</body>
</html>
20 changes: 20 additions & 0 deletions js/helpers/create-message-tile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const createMessageTile = (message, isCurrentUser) => {
const tile=document.createElement('li');
tile.id='message-tile';

const messageText=document.createElement('p');
messageText.id='message-tile-text';
messageText.innerText=message.content;

const messageDate=document.createElement('p');
messageDate.id='message-date';
messageDate.textContent=new Date(message.timestamp).toDateString();

tile.style.backgroundColor=isCurrentUser?'rgb(244, 107, 107)'
:'rgb(169, 169, 169)';
tile.append(messageText, messageDate);

return tile;
};

export default createMessageTile;
29 changes: 29 additions & 0 deletions js/helpers/create-user-tile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import getLastText from "./get-last-text.js";
import openChat from "../main-page.js"

const createUserTile=(currentUser, user)=>{
const isGroup=user.participants!==undefined;
const tile=document.createElement('li');
const profilePicture=document.createElement('img');
const username=document.createElement('h3');
const lastMessage=document.createElement('p');
const onlineBadge=document.createElement('h4');

profilePicture.src='../assets/images/icons/image.png';
username.textContent=isGroup?user.name:user.username;
lastMessage.textContent=getLastText(isGroup,user.id);
onlineBadge.textContent = '•';
onlineBadge.style.color = 'green';
onlineBadge.style.display = (user.isOnline && !isGroup) ? 'inline' : 'none';

tile.addEventListener('click',()=>{
isGroup?openChat(user.id,null,'group')
:openChat(currentUser.id,user.id,'private');
});
tile.append(profilePicture, username, lastMessage, onlineBadge);

return tile;

}

export default createUserTile;
24 changes: 24 additions & 0 deletions js/helpers/display-messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import createMessageTile from "./create-message-tile.js";

const displayMessages=(chat,messagesCount,currentUser)=>{
const chatsList=document.getElementById('chats-list');
const noMessages=document.getElementById('no-messages');

if(messagesCount>0){
chatsList.style.display='inline';
noMessages.style.display='none';
}
else{
chatsList.style.display='none';
noMessages.style.display='inline';
return;
}

chat.messages.forEach(currentMessage => {
const message= currentMessage;
const messageTile = createMessageTile(message, message.senderId === currentUser);
chatsList.appendChild(messageTile);
});
}

export default displayMessages;
25 changes: 25 additions & 0 deletions js/helpers/get-chat-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Chat from "../models/chat.js";
import User from "../models/user.js";
import LocalStorageService from "../services/local-storage.js";

const getChatData=(user1,user2,type)=>{
const chatsList=document.getElementById('chats-list');
chatsList.innerHTML='';
let chatId,currentChat,messagesCount;
if(type==='private'){
chatId=Chat.generateChatId(user1, user2);
const allChats=LocalStorageService.getChats();
currentChat=allChats.find(chat=>chat.id===chatId);
}
else{
const groups=LocalStorageService.getGroups();
currentChat=groups.find(group=>group.id===user1);
chatId=user1;
}

messagesCount=currentChat?.messages?.length||0;

return {chatId,currentChat,messagesCount};
}

export default getChatData;
32 changes: 32 additions & 0 deletions js/helpers/get-last-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import LocalStorageService from "../services/local-storage.js";
import SessionManager from "../services/session-manager.js";
import Chat from "../models/chat.js";

function getLastText(isGroup,userId){
let allChats = LocalStorageService.getChats();
let allGroups = LocalStorageService.getGroups();
let currentUserId = SessionManager.getUser().id;
let lastMsgText='No message yet'

if (isGroup) {
let group = allGroups.find(g=>g.id===userId);

if (group && group.messages && group.messages.length > 0) {
let lastMsg = group.messages[group.messages.length - 1];
lastMsgText = lastMsg.content;

}
}
else {
let chatId = Chat.generateChatId(currentUserId, userId);
let chat = allChats.find(c => c.id === chatId);

if (chat && chat.messages && chat.messages.length > 0) {
let lastMsg = chat.messages[chat.messages.length - 1];
lastMsgText = lastMsg.content;
}
}
return lastMsgText;
}

export default getLastText;
21 changes: 21 additions & 0 deletions js/helpers/set-up-send-message-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import LocalStorageService from "../services/local-storage.js";
import openChat from "../main-page.js";

const setupSendMessageHandler = (userId1, userId2, type, chatId) => {
const sendIcon=document.getElementById('message-send');
sendIcon.onclick = null;
sendIcon.onclick=()=>{
const content= document.getElementById('message-text').value.trim();
if (content==='') return;

if (type==='group') {
LocalStorageService.messageGroup(chatId, content, 'none');
} else {
LocalStorageService.sendMessage(chatId, content, 'none');
}
document.getElementById('message-text').value = '';
openChat(userId1, userId2, type);
};
};

export default setupSendMessageHandler;
27 changes: 27 additions & 0 deletions js/helpers/switch-views.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
function switchMobileView(view){
const nav = document.querySelector('nav');
const main = document.querySelector('main');
const aside = document.querySelector('aside');

const isMobile = window.matchMedia("(max-width:600px)").matches;

if(!isMobile){
return;
}

nav.style.display = 'none';
main.style.display = 'none';
aside.style.display = 'none';

if(view === 'nav'){
nav.style.display = 'block';
}
else if(view === 'chat'){
main.style.display = 'flex';
}
else if(view === 'profile'){
aside.style.display = 'block';
}
}

export default switchMobileView;
25 changes: 25 additions & 0 deletions js/helpers/update-header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import LocalStorageService from "../services/local-storage.js";

const updateChatHeader=(chateeId,chatData,type)=>{
const chateeName=document.getElementById('chatee-name');
const chateeSideName=document.getElementById('chatee-side-name');
const chateeStatus=document.getElementById('chatee-status');
const chateeSideStatus=document.getElementById('chatee-side-status');

if(type==='private'){
const chatee=LocalStorageService.getUser(chateeId);
chateeName.textContent=chatee.username;
chateeSideName.textContent=chatee.username;
chateeStatus.textContent=chatee.isOnline?'Online':'Offline';
chateeSideStatus.textContent=chateeStatus.textContent;
}
else{
chateeName.textContent=chatData.currentChat.name;
chateeSideName.textContent=chatData.currentChat.name;
chateeStatus.textContent='Group';
chateeSideStatus.textContent='Group';
}

}

export default updateChatHeader;
86 changes: 86 additions & 0 deletions js/landing-page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import LocalStorageService from "./services/local-storage.js";
import SessionManager from "./services/session-manager.js";
import User from "./models/user.js";


console.log(LocalStorageService.getUsers());
let isSignUp=true;
const defaultView=document.getElementById('toggle-action');
let authButton=document.getElementById('submit-button');


function toggleView(){
let heading=document.getElementById('welcome-heading');
let toggleQuestion=document.getElementById('toggle-question');
let toggleView=document.getElementById('toggle-action');
let toggleMessage=document.getElementById('welcome-message');
let submitButton=document.getElementById('submit-button');



if(isSignUp){
heading.textContent='Hey newbie...';
toggleQuestion.textContent='Already have an account?';
toggleMessage.innerHTML='LocalChat is a local chat app.<br>Your data is stored securely in your own <br>computer, no one but you will be able to access the data.';
toggleView.textContent="Sign In";
submitButton.textContent='Sign Up';

}
else{
heading.textContent='Welcome Back...';
toggleQuestion.textContent='Don’t have an account?';
toggleMessage.innerHTML='LocalChat is a local chat app.<br>Your data is stored securely in your own <br>computer, no one but you will be able to access the data.';
toggleView.textContent='Sign Up';
submitButton.textContent='Sign In';

}

isSignUp=!isSignUp;
console.log('done');
}


function authenticate(){
let username=document.getElementById('username-input').value;
let password=document.getElementById('password-input').value;
if(!isSignUp){
if(User.isUserNameUnique(username)){
//Create an account
LocalStorageService.createUser(username,password);
console.log(LocalStorageService.getUsers());
}
else{
alert('Please enter a new username, make it unique');
return;
}
}
//Login
let user=SessionManager.login(username,password);
console.log(user);
if(user){
console.log('logged in as '+ user.username);
//Navigate to the main page
window.location.replace('./pages/main.html');
}
else{
alert('Login failed, ensure you have entered correct credentials');
return;
}
}










function main(){
console.log('Main method test');
defaultView.addEventListener('click',()=>toggleView());
authButton.addEventListener('click', ()=>authenticate());
}

main();
Loading