The initial idea of this application was to take features from Whatsapp, Slack, and Telegram in building a hybrid-like Chatting System without depending on any form of external chatting SDK like Stream, SendBird including the Big Wolf, Chat Engine.
Versions of different technologies where features were extracted
Tech | Type | Description |
---|---|---|
Whatsapp |
web/apk |
2.21.17.2. Andriod version |
Slack |
apk |
21.08. Andriod version |
Telegram |
web/apk |
Desktop 2.9.2. + 2021 alpha-version |
Database Rules |
---|
{
"rules": {
"users": {
".read": "auth != null",
".write": "auth != null",
"$uid": {
".write": "auth != null && auth.uid === $uid",
".validate": "(newData.hasChildren(['about', 'name', 'avatar', 'userRawData']) && !newData.hasChildren(['starredMsgs'])) || (newData.hasChildren(['starredMsgs', 'about', 'name', 'avatar', 'userRawData']) && newData.hasChildren(['starredMsgs']))",
"about": {
".write": "auth != null",
".validate": "newData.hasChildren(['details', 'timeUpdated'])",
"details": {
".validate": "newData.val().length > 0"
},
"timeUpdated": {
".validate": "newData.val() <= now"
}
},
"name": {
".validate": "newData.val().length > 0"
},
"avatar": {
".validate": "newData.val().length > 0"
},
"starredMsgs": {
".read": "auth != null",
"$starredMsgsId": {
".write": "auth != null && auth.uid === $uid",
".validate": "newData.hasChildren(['createdBy', 'isContent', 'item', 'timeStarred', 'timestamp'])",
"createdBy": {
".write": "auth != null",
".validate": "newData.hasChildren(['id', 'name'])",
"id": {
".validate": "newData.val().length > 0"
},
"name": {
".validate": "newData.val().length > 0"
}
},
"isContent": {
".validate": "newData.val().length >= 4"
},
"item": {
".validate": "newData.val().length > 0"
},
"timeStarred": {
".validate": "newData.val() <= now"
},
"timestamp": {
".validate": "newData.val() <= now"
}
}
},
"userRawData": {
".write": "auth != null",
".validate": "newData.hasChildren(['creationTime', 'id'])",
"creationTime": {
".validate": "newData.val() <= now"
},
"id": {
".validate": "newData.val() === $uid"
}
}
}
},
"channels": {
".read": "auth != null",
"$channelId": {
".write": "auth != null",
".validate": "newData.hasChildren(['id', 'name', 'createdBy', 'details', 'suscribed_users'])",
"id": {
".validate": "newData.val() === $channelId"
},
"name": {
".validate": "newData.val().length >= 5"
},
"details": {
".validate": "newData.val().length >= 5"
},
"suscribed_users": {
".validate": "newData.hasChildren(['users_id', 'users_name'])"
},
"createdBy": {
".validate": "newData.hasChildren(['avatar', 'id', 'name', 'timestamp'])"
}
}
},
"messages": {
".read": "auth != null",
".write": "auth != null"
},
"typing": {
".read": "auth != null",
".write": "auth != null"
},
"presence": {
".read": "true",
".write": "true"
},
"privateMessagesIds": {
".read": "auth != null",
".write": "auth != null"
},
"unique_id_of_users": {
".read": "auth != null",
"$id": {
".write": "auth != null",
".validate": "newData.hasChildren(['suscribedUsersId'])"
}
},
"lastOnlinePresence": {
".read": "auth != null",
"$id": {
".write": "auth != null",
".validate": "newData.hasChildren(['last_seen'])"
}
}
}
}
Storage Rules |
---|
rules_version = '2';
service firebase.storage {
match /b/january-ee57e.appspot.com/o {
match /avatars {
match /users/{userId} {
allow read: if request.auth != null;
allow write: if request.auth != null && request.auth.uid == userId && request.resource.contentType.matches('image/.*') && request.resource.size < 1 * 1024 * 1024;
}
}
match /chat {
match /public/{imagePath=**} {
allow read: if request.auth != null;
allow write: if request.auth != null && request.resource.contentType.matches('image/.*') && request.resource.size < 1 * 1024 * 1024;
}
match /private/{userId1}/{userId2}/{imagePath=**} {
allow read: if request.auth != null && (request.auth.uid == userId1 || request.auth.uid == userId2);
allow write: if request.auth != null && (request.auth.uid == userId1 || request.auth.uid == userId2) && request.resource.contentType.matches('image/.*') && request.resource.size < 1 * 1024 * 1024;
}
}
}
}
- Stackoverflow :: React-router-dom useParams() inside class component
- Firebase Data Docs
- Uploading Files to Firebase Storage
- Mozilla Regex
- Logic to return boolean based on client's connection status
- Retrieving Firebase data
- Firebase "child-added"
- Handling window event in React
- Adding and removing class from document.body
- How to get the DOM ref of a child class component in a parent class component
- SVG loaders
- Know everything about useEffect Hook
- The answer by "Aaquib Ahmed" on stackoverflow was very helpful (he explained how the hook "useHistory" can be used to access the history object in a functional component )
- Setting Authorized domains in Firebase
- Cypress Login Test
- Best Practices in writing Cypress Tests
- Firebase Rules
- Firebase Security and Rules
- More on Firebase Rules
Client: React, Redux, Styled-component
Server: Firebase
E2E Testing: Cypress
To run this project, you will need to add the following environment variables to your .env file
REACT_APP_API_KEY
REACT_APP_AUTH_DOMAIN
REACT_APP_DATABASE_URL
REACT_APP_PROJECT_ID
REACT_APP_STORAGE_BUCKET
REACT_APP_SENDER_ID
REACT_APP_APP_ID
-
Adding more tests
-
Fixing minor UI bugs
-
Implementing Hasher Algorithms Using CryptoJS (soon)
For support, email luckypius50@gmail.com