diff --git a/fonts/selawk.ttf b/fonts/selawk.ttf new file mode 100644 index 0000000..736bac3 Binary files /dev/null and b/fonts/selawk.ttf differ diff --git a/fonts/selawkb.ttf b/fonts/selawkb.ttf new file mode 100644 index 0000000..2134a76 Binary files /dev/null and b/fonts/selawkb.ttf differ diff --git a/fonts/selawkl.ttf b/fonts/selawkl.ttf new file mode 100644 index 0000000..cbfaaf9 Binary files /dev/null and b/fonts/selawkl.ttf differ diff --git a/fonts/selawksb.ttf b/fonts/selawksb.ttf new file mode 100644 index 0000000..5ec501d Binary files /dev/null and b/fonts/selawksb.ttf differ diff --git a/fonts/selawksl.ttf b/fonts/selawksl.ttf new file mode 100644 index 0000000..bc3fa08 Binary files /dev/null and b/fonts/selawksl.ttf differ diff --git a/index.html b/index.html index 97d55cf..1f455f7 100644 --- a/index.html +++ b/index.html @@ -16,10 +16,11 @@
- +
- -
@@ -119,6 +113,17 @@
+
+

Pandora User

+
+ +
+

Pandora User

+ +
+
+
+
diff --git a/js/app.js b/js/app.js index 43ad99d..8f5a749 100644 --- a/js/app.js +++ b/js/app.js @@ -2,8 +2,8 @@ import { config } from "./config.js"; import { waha } from "./waha.js"; import { ui, elements } from "./ui.js"; import { websocket } from "./websocket.js"; -import { compensateMessageOrdering, formatTime, normalizeId } from "./utils.js"; -import { fetchChats, getAppUser, getChatMessages, getChats, updateOnlineStatus } from "./storage.js"; +import { compensateMessageOrdering, debounce, formatTime, normalizeId } from "./utils.js"; +import { fetchChats, getAppUser, getChatMessages, getChatPicture, getChats, getUser, getUserAbout, sendStatus, updateOnlineStatus } from "./storage.js"; import { upsertMessages } from "./db.js"; let activeChatState = null; @@ -20,7 +20,7 @@ document.addEventListener('DOMContentLoaded', async () => { await updateOnlineStatus(); setupEventListeners(); try { - elements.loggedUserName.textContent = (await getAppUser()).pushName; + setupElementsData(); loadChats(); checkWahaStatus(); initWebSocket(); @@ -29,6 +29,25 @@ document.addEventListener('DOMContentLoaded', async () => { } }); +async function setupElementsData() { + const usr = await getAppUser(); + const usrPic = (await getChatPicture(usr.id))?.url; + const usrInfo = await getUser(usr.id); + const usrAbout = (await getUserAbout(usr.id)).about; + elements.contentUserName.forEach(e => { + e.innerHTML = usr.pushName; + }) + elements.contentUserNumber.forEach(async e => { + e.innerHTML = usrInfo.number; + }) + elements.resourceUserPic.forEach(async e => { + e.src = usrPic; + }) + elements.valueUserStatus.forEach(async e => { + e.value = usrAbout.trim(); + }) +} + function loadChats() { elements.chatsLoader.classList.remove('hidden'); try { @@ -168,6 +187,17 @@ function setupEventListeners() { }) elements.saveSettingsBtn.addEventListener('click', saveSettings); + + elements.inputUserStatus.addEventListener('input', debounce(async function() { + const result = await sendStatus(elements.inputUserStatus.value); + console.log(result); + }, 2000)) + + // elements.selectable.forEach(e => { + // e.addEventListener('pointerdown', () => { + + // }) + // }) } function initWebSocket() { diff --git a/js/storage.js b/js/storage.js index f83d66c..238b4b9 100644 --- a/js/storage.js +++ b/js/storage.js @@ -43,8 +43,20 @@ export function getGroups() { return chats.filter(c => c.id.endsWith("@g.us")); } -export function getUser(number) { - return users.find(user => user.id === number); +export async function getUser(number) { + if (online) { + return await waha.getUser(number); + } else { + return; + } +} + +export async function getUserAbout(userId) { + if (online) { + return await waha.getUserAbout(userId); + } else { + return; + } } export function getChats() { @@ -125,4 +137,14 @@ export async function getChatPicture(chatId) { export function isOnline() { return online; +} + +export async function sendStatus(text) { + if (online) { + return await waha.setStatus(text); + } else { + return { + success: false + } + } } \ No newline at end of file diff --git a/js/ui.js b/js/ui.js index 698cd6a..d2e1261 100644 --- a/js/ui.js +++ b/js/ui.js @@ -33,7 +33,13 @@ export const elements = { attachmentInput: document.getElementById('attachment-input'), attachmentBtn: document.getElementById('attachment-btn'), extraPages: document.querySelectorAll('.extra-page'), - desktopSidebarButtons: document.querySelectorAll("#desktop-aside button") + desktopSidebarButtons: document.querySelectorAll("#desktop-aside button"), + contentUserName: document.querySelectorAll('[data-content="app-user"]'), + contentUserNumber: document.querySelectorAll('[data-content="app-user-number"]'), + resourceUserPic: document.querySelectorAll('[data-resource="app-user-image"]'), + valueUserStatus: document.querySelectorAll('[data-value="app-user-status"]'), + inputUserStatus: document.getElementById('profile-page-status-input'), + selectable: document.querySelectorAll('.selectable'), }; export const ui = { @@ -92,7 +98,7 @@ export const ui = { for (const chat of chats) { const li = document.createElement('li'); - li.className = `chat-item ${activeChat && activeChat.id === chat.id ? 'active' : ''}`; + li.className = `chat-item selectable ${activeChat && activeChat.id === chat.id ? 'active' : ''}`; li.dataset.id = chat.id; const initials = chat.name ? chat.name.substring(0, 1).toUpperCase() : '?'; @@ -235,7 +241,7 @@ export const ui = { const prevMsgEl = getPrevMessageElem(); const groupDiv = document.createElement('div'); - groupDiv.className = `message-group ${isOutgoing ? 'outgoing' : 'incoming'}`; + groupDiv.className = `message-group selectable ${isOutgoing ? 'outgoing' : 'incoming'}`; groupDiv.id = normalizeId(msg._serialized ? msg : msg.id); groupDiv.dataset.timestamp = msg.timestamp; groupDiv.dataset.from = msg.participant || msg.from; diff --git a/js/utils.js b/js/utils.js index 9e8418b..81f05f0 100644 --- a/js/utils.js +++ b/js/utils.js @@ -112,4 +112,12 @@ export function normalizeId(raw) { return raw._serialized || raw.user || JSON.stringify(raw); } return raw; +} + +export function debounce(func, delay) { + let timeoutId; + return function() { + clearTimeout(timeoutId); + timeoutId = setTimeout(func, delay); + }; } \ No newline at end of file diff --git a/js/waha.js b/js/waha.js index 66683b1..2c5c077 100644 --- a/js/waha.js +++ b/js/waha.js @@ -102,6 +102,14 @@ export const waha = { return request(`/api/${config.session}/chats/${chatId}/picture`); }, + async getUser(chatId) { + return request(`/api/${config.session}/contacts/${chatId}`); + }, + + async getUserAbout(chatId) { + return request(`/api/contacts/about?contactId=${chatId}&session=${config.session}`); + }, + async readChat(chatId) { return request('/api/sendSeen', { method: 'POST', @@ -143,6 +151,15 @@ export const waha = { }); }, + async setStatus(text) { + return request(`/api/${config.session}/profile/status`, { + method: 'PUT', + body: JSON.stringify({ + status: text + }) + }); + }, + async sendFileMessage(chatId, file) { const fileBase64 = await getBase64(file); const body = { diff --git a/style.css b/style.css index 1c17b27..c1ae0e3 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,33 @@ +@font-face { + font-family: Selawik; + src: url(./fonts/selawk.ttf); + font-weight: normal; +} + +@font-face { + font-family: Selawik; + src: url(./fonts/selawkl.ttf); + font-weight: 200; +} + +@font-face { + font-family: Selawik; + src: url(./fonts/selawksl.ttf); + font-weight: 300; +} + +@font-face { + font-family: Selawik; + src: url(./fonts/selawkb.ttf); + font-weight: bold; +} + +@font-face { + font-family: Selawik; + src: url(./fonts/selawkb.ttf); + font-weight: 800; +} + :root { --bg-main: black; --bg-secondary: #242424; @@ -44,7 +74,7 @@ body.light { margin: 0; padding: 0; box-sizing: border-box; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-family: Selawik, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; user-select: none; } @@ -106,7 +136,7 @@ input:focus { } .app-name { - font-weight: bolder; + font-weight: bold; } /* Sidebar Styling */ @@ -299,15 +329,28 @@ input:focus { border: 1px solid transparent; } +.selected { + background: rgba(255, 255, 255, 0.04); + transform: scale(0.98) skewX(10deg); + filter: blur(.1px); + opacity: .6; +} + @media (hover: hover) { - .chat-item:hover { - background: rgba(255, 255, 255, 0.04); transform: scale(0.98) skewX(10deg); filter: blur(.1px); opacity: .6; + .selectable:hover { + background: rgba(255, 255, 255, 0.04); + transform: scale(0.98) skewX(10deg); + filter: blur(.1px); + opacity: .6; } } @media (hover: none) { - .chat-item:active { - background: rgba(255, 255, 255, 0.04); transform: scale(0.98) skewX(10deg); filter: blur(.1px); opacity: .6; + .selectable:active { + background: rgba(255, 255, 255, 0.04); + transform: scale(0.98) skewX(10deg); + filter: blur(.1px); + opacity: .6; } } @@ -351,6 +394,7 @@ input:focus { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + font-weight: 300; flex: 1; } @@ -403,11 +447,6 @@ input:focus { } /* Sidebar Footer */ -.sidebar-footer { - padding: 14px 20px; - background: var(--bg-secondary); -} - .api-status-badge { display: flex; align-items: center; @@ -555,7 +594,7 @@ input:focus { .active-contact-info h3 { font-size: 1rem; - font-weight: bolder; + font-weight: bold; } .contact-status { @@ -604,6 +643,7 @@ input:focus { display: flex; flex-direction: column; max-width: 65%; + transition: .1s; } .message-group.incoming { @@ -826,6 +866,49 @@ input:focus { display: flex; } +#profile-page { + flex-direction: column; + background-color: var(--bg-main); +} + +#profile-page .content { + padding: 1.4rem; + display: flex; + gap: 1em; +} + +#profile-page-user-info { + flex: 1; +} + +#profile-page h2 { + padding: 1.4rem; + font-size: 5rem; + text-wrap-mode: nowrap; + overflow: hidden; + font-weight: 200; +} + +#profile-page-picture { + width: 100%; + max-width: 10%; + height: auto; + aspect-ratio: 1/1; +} + +#profile-page-status-input { + width: 100%; + background-color: var(--bg-main); + font-size: xx-large; + border-bottom: medium solid transparent; + padding: 0; +} + +#profile-page-status-input:focus { + color: var(--text-primary); + border-bottom-color: var(--text-primary); +} + @media (max-width: 768px) { .app-container { width: 100%; @@ -901,6 +984,19 @@ input:focus { #desktop-aside { display: none; } + + #profile-page .content { + flex-direction: column; + } + + #profile-page-picture { + max-width: 50%; + margin: auto; + } + + #profile-page-user-number { + text-align: center; + } } /* Modal Styling */