Adrian Seth Tabotabo
(Use full screen for best experience, use arrow-keys for navigation)
Core Tech: C# (.NET) |
Google Firestore | Terminal.Gui |
LiteDB
Code Example: Real-time Message Listener
private void StartListener(string chatroom_id)
{
CollectionReference messegesRef = db.Collection("Chatrooms")
.Document(chatroom_id)
.Collection("messages");
Query query = messegesRef.OrderBy("timestamp");
_listener = query.Listen(callback: OnSnapshotReceived);
}
ProfanityChecker censors messages, including leetspeak.
Applying Profanity Filter
// ... inside OnSnapshotReceived ...
if (...)
senderName = "Unknown User";
string displayName =
senderId == SessionHandler.UserId ? $"{senderName} (me)" : senderName;
string chatEntry = $"{displayName}: {ProfanityChecker.CensorTextRobust(message)}";
// ...
Users,
Chatrooms, Messages, Admins, Pinned IDs.
Cache.db.User objects for faster lookups.
BanterLogo.txt is read from disk for the login screen.
Filtered Source Tree
C:.
| .gitignore
| BanterLogo.txt
| CpE261FinalProject.csproj
| Program.cs
| Schema.txt
|
+---Utilities
| CachingOperations.cs
| ClassWrappers.cs
| CustomColorScheme.cs
| FirebaseHelper.cs
| FirestoreManager.cs
| Interfaces.cs
| Models.cs
| ProfanityChecker.cs
| SessionHandler.cs
| Validator.cs
| WindowHelper.cs
|
\---Windows
AbstractWindow.cs
ChangeNameWindow.cs
CreateAccountWindow.cs
CreateChatroomWindow.cs
LogInWindow.cs
ViewPinnedMessagesWindow.cs
Window1.cs
Window2.cs
Window3.cs
/Users/{userId}
{
"name": "Adrian Seth...",
"username": "dreeyanzz",
"email": "adriangwapoz125@...",
"password": "scarara",
"chatrooms": [
"AyELCpLJgYy3TG0JedBH"
]
}
/Chatrooms/{chatroomId}
{
"chatroom_name": "American Rejects",
"type": "group",
"participants": [...],
"admins": [...],
"pinned_messages": [...],
"messages": { (subcollection)
"{messageId}": {
"sender_id": "ZzWLD...",
"text": "asd123123",
"timestamp": ...
}
}
}
Collection: /Users
| Field Name | Data Type | Description |
|---|---|---|
| name | string | User's full name |
| username | string | User's unique public username |
| string | User's private email address | |
| password | string | User's login password (plaintext) |
| chatrooms | Array<string> | List of Chatroom IDs the user is part of |
Collection: /Chatrooms
| Field Name | Data Type | Description |
|---|---|---|
| chatroom_name | string | Display name of the chat (e.g., "American Rejects") |
| type | string | "group" or "individual" |
| participants | Array<string> | List of User IDs in the chat |
| admins | Array<string> | List of User IDs with admin privileges |
| pinned_messages | Array<string> | List of Message IDs that are pinned |
| messages | Subcollection | Contains the actual chat messages (See below) |
Subcollection: /Chatrooms/{id}/messages
| Field Name | Data Type | Description |
|---|---|---|
| sender_id | string | The User ID of the participant who sent the message |
| text | string | The text content of the message |
| timestamp | Timestamp | The server-side timestamp of when the message was sent |
Authentication:
FAIL » Tried login with incorrect username/password.
PASS » Error message shown, access denied.
Registration:
FAIL » Tried creating account with taken username,
mismatched passwords, invalid email.
PASS » All validation checks handled the errors
gracefully.
Admin Functionality:
TEST » As admin, deleted a group chat.
PASS » Confirmed with collaborators that the chat was
instantly removed.
Essential for managing network requests to Firebase without freezing the UI.
Code Example: Async Login Button Click
private async void OnLogInButtonClicked()
{
// ... get username/password ...
logInButton.Text = "Logging in...";
SetInteractables(false);
string? user_id = await FirebaseHelper.GetUserIdFromUsername(inputUsername);
if (user_id == null)
{
// ... show error ...
return;
}
// ... get user info, set session, etc. ...
await SessionHandler.StartChatroomsListener();
}
Used C# events in
SessionHandler
to decouple code, allowing windows to react to state changes.
Code Example: Defining the Event
public static class SessionHandler
{
// ... other properties ...
public static event Action<string?>? CurrentChatroomChanged;
// ...
}
Code Example: Subscribing to the Event
private Window2()
{
// ...
SessionHandler.CurrentChatroomChanged += OnChatroomChanged;
SessionHandler.IsLoggedInChanged += OnLogInChanged;
// ...
}
The most surprising discovery was that a terminal app could be interacted with using a mouse.
That single
Terminal.Guifeature was the inspiration for the project. A chat app is hard to navigate with just a keyboard, and this "sparked" the interest to build Banter.
Google Firestore Documentation
Terminal.Gui (gui.cs) Project Page
LiteDB Documentation