diff --git a/README.md b/README.md index e0625a17..5804b3ac 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ------

-screenshot +screenshot

Try it here: [web.kahla.app](https://web.kahla.app) diff --git a/package-lock.json b/package-lock.json index 241b11b0..b48da726 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "kahla", - "version": "3.8.8", + "version": "3.9.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c629d004..59a722dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kahla", - "version": "3.8.8", + "version": "3.9.0", "description": "Kahla is a cross-platform business messaging app.", "author": "Aiursoft (https://www.aiursoft.com/)", "build": { diff --git a/src/app/Controllers/file-history.component.ts b/src/app/Controllers/file-history.component.ts index 631db38d..2efc6e9a 100644 --- a/src/app/Controllers/file-history.component.ts +++ b/src/app/Controllers/file-history.component.ts @@ -16,6 +16,7 @@ import { ProbeService } from '../Services/ProbeService'; export class FileHistoryComponent implements OnInit { public files: FileHistoryApiModel; + public loaded = false; constructor( private route: ActivatedRoute, @@ -26,11 +27,13 @@ export class FileHistoryComponent implements OnInit { } ngOnInit(): void { + this.loaded = false; this.route.params.pipe(switchMap(param => { return this.conversationApiService.FileHistory(param.id); })).subscribe(t => { + this.loaded = true; if (t.code === 0) { - t.items.reverse(); + t.items = t.items.filter(x => (x.files && x.files.length > 0)).reverse(); this.files = t; } }); diff --git a/src/app/Controllers/talking.component.ts b/src/app/Controllers/talking.component.ts index 9f03ef09..c4ab6713 100644 --- a/src/app/Controllers/talking.component.ts +++ b/src/app/Controllers/talking.component.ts @@ -250,6 +250,9 @@ export class TalkingComponent implements OnInit, OnDestroy { tempMessage.content = this.content; tempMessage.senderId = this.cacheService.cachedData.me.id; tempMessage.sender = this.cacheService.cachedData.me; + const prevMsg = this.messageService.localMessages[this.messageService.localMessages.length - 1]; + tempMessage.groupWithPrevious = prevMsg.senderId === this.cacheService.cachedData.me.id + && new Date().getTime() - prevMsg.timeStamp <= 3600000; tempMessage.sendTime = new Date().toISOString(); tempMessage.local = true; this.messageService.modifyMessage(tempMessage, false); @@ -516,8 +519,4 @@ export class TalkingComponent implements OnInit, OnDestroy { this.friendshipService.joinGroup(group, true); } } - - public localDate(date: string): string { - return new Date(date).toLocaleString(); - } } diff --git a/src/app/Models/Message.ts b/src/app/Models/Message.ts index b79703bd..dbc2a4e8 100644 --- a/src/app/Models/Message.ts +++ b/src/app/Models/Message.ts @@ -7,7 +7,9 @@ export class Message { public sender: KahlaUser; public sendTime: string; public content: string; + public groupWithPrevious: boolean; + public sendTimeDate: Date; public resend: boolean; public contentRaw: string; public isEmoji = false; diff --git a/src/app/Services/MessageService.ts b/src/app/Services/MessageService.ts index 0cfd5e4a..6d2d9ae7 100644 --- a/src/app/Services/MessageService.ts +++ b/src/app/Services/MessageService.ts @@ -268,6 +268,7 @@ export class MessageService { if (unread > 1) { // add a last read bar this.localMessages[this.localMessages.length - unread].lastRead = true; + this.localMessages[this.localMessages.length - unread].groupWithPrevious = false; } setTimeout(() => { const lis = document.querySelector('#messageList').querySelectorAll('li'); @@ -414,7 +415,8 @@ export class MessageService { } } t.contentRaw = t.content; - t.timeStamp = new Date(t.sendTime).getTime(); + t.sendTimeDate = new Date(t.sendTime); + t.timeStamp = t.sendTimeDate.getTime(); if (t.content.match(/^\[(video|img)\].*/)) { if (t.content.startsWith('[img]')) { let imageWidth = Number(t.content.split('|')[1]), diff --git a/src/app/Styles/file-list.scss b/src/app/Styles/file-list.scss index a9301022..fe17eb49 100644 --- a/src/app/Styles/file-list.scss +++ b/src/app/Styles/file-list.scss @@ -56,3 +56,9 @@ } } } + +.empty-notice { + margin-top: 40px; + text-align: center; + font-size: 14px; +} diff --git a/src/app/Styles/talking.scss b/src/app/Styles/talking.scss index fef22a90..5e920dee 100644 --- a/src/app/Styles/talking.scss +++ b/src/app/Styles/talking.scss @@ -19,39 +19,53 @@ background-color: var(--default-background); li { - margin-bottom: 25px; clear: both; box-sizing: border-box; } + .message-line { + display: flex; + width: 100%; + margin-bottom: 8px; + + &.grouped { + margin-bottom: 4px; + } + } + + .left .message-line { + flex-direction: row; + } + + .right .message-line { + flex-direction: row-reverse; + } + .chat-avatar { + flex: 0 0 auto; width: 40px; height: 40px; display: inline-block; text-align: center; - cursor: pointer; img { + cursor: pointer; width: 100%; height: 100%; border-radius: 4px; user-select: none; } - } - - .left .chat-avatar { - float: left; - } - .right .chat-avatar { - float: right; + &:focus { + outline: none; + } } - .chat-text { + .message-block { display: block; font-size: 12px; - width: 70%; - margin-bottom: 1.5%; + flex: 0 1 auto; + max-width: 70%; * { vertical-align: middle; @@ -67,27 +81,55 @@ } } - .left .chat-text { - float: left; + .left .message-block { text-align: left; margin-left: 12px; } - .right .chat-text { - float: right; + .right .message-block { text-align: right; margin-right: 12px; } - .last-read-bar { - color: var(--primary-color-depth2); + %split-bar { clear: both; - font-weight: 800; font-size: 14px; user-select: none; + margin: 6px 0 6px 0; + position: relative; + display: inline-block; + + &::before, &::after { + content: ' '; + display: block; + position: absolute; + top: 50%; + left: -120px; + width: 100px; // 100px line on either side + border-bottom: 1px solid #FFF; + } + + &::after { + left: auto; + right: -120px; + } + } + + .last-read-bar { + @extend %split-bar; + color: var(--primary-color-depth2); + font-weight: 800; + + &::before, &::after { + border-bottom: 2px solid var(--primary-color-depth2); + } + } + + .date-bar { + @extend %split-bar; } - .chat-content { + .message-balloon { border-radius: 8px; color: white; padding: 10px; @@ -102,14 +144,14 @@ white-space: pre-wrap; } + &.balloon-grouped:after, &.single-emoji:after { + border-left: unset !important; + border-right: unset !important; + } + &.single-emoji { background: transparent !important; - &:after { - border-left: unset !important; - border-right: unset !important; - } - p { font-size: 64px; } @@ -134,7 +176,7 @@ } } - .left .chat-content { + .left .message-balloon { border-color: var(--talking-chat-bg); color: var(--default-textcolor); background: var(--talking-chat-bg); @@ -146,7 +188,7 @@ } } - .right .chat-content { + .right .message-balloon { background: linear-gradient(120deg, var(--primary-color-depth3) 0%, var(--primary-color-depth2) 100%); &::after { @@ -156,16 +198,30 @@ } } + .sendTime { + color: #aaa; + flex: 0 1 auto; + align-self: center; + margin: 0 0 0 0; + opacity: 0; + transition: all 0.2s ease-in-out; + pointer-events: none; + padding-left: 7px; + padding-right: 7px; + } + .left .sendTime { - margin: 0.5% 0 0 1%; + transform: translateX(-30px); } .right .sendTime { - margin: 0.5% 1% 0 0; + transform: translateX(30px); } - .sendTime { - color: #aaa; + .message-block:hover ~ .sendTime, .sendTime.show { + opacity: 1; + transform: translateX(0) !important; + flex: 0 0 auto; } .sendFail { @@ -193,10 +249,6 @@ color: var(--minor-textcolor); } -.chat-avatar:focus { - outline: none; -} - .image-container { position: relative; max-height: 1000px; @@ -207,7 +259,7 @@ } } -.message-list .chat-content video { +.message-list .message-balloon video { width: 100%; } @@ -396,25 +448,25 @@ i { right: 20px; } -.chat-text a { +.message-block a { text-decoration: none; } .left { - .chat-text a, - .chat-content .voicemsg span { + .message-block a, + .message-balloon .voicemsg span { color: #9baec8; } } .right { - .chat-text a, - .chat-content .voicemsg span { + .message-block a, + .message-balloon .voicemsg span { color: #d9e1e8; } } -.right .chat-content span { +.right .message-balloon span { color: #fff; } @@ -452,7 +504,7 @@ i { } } -.message-list .chat-content .voicemsg { +.message-list .message-balloon .voicemsg { align-items: center; display: grid; grid-template-columns: 40px 2fr; diff --git a/src/app/Views/file-history.html b/src/app/Views/file-history.html index 28cc9482..4b04d6ec 100644 --- a/src/app/Views/file-history.html +++ b/src/app/Views/file-history.html @@ -1,5 +1,5 @@ -
+
@@ -48,3 +48,13 @@
+ +
+

No files.

+

Files uploaded to the conversation will be shown here.

+
+ +
+ Loading... +
+
diff --git a/src/app/Views/talking.html b/src/app/Views/talking.html index 3bcf5100..35cd9747 100644 --- a/src/app/Views/talking.html +++ b/src/app/Views/talking.html @@ -6,29 +6,42 @@ i18n="@@ClickToLoadMore">Click to load more -
  • -
    --- LAST READ ---
    +
  • +
    +
    LAST READ
    +
    -
    - -
    + *ngIf="i === 0 || messageService.localMessages[i - 1].sendTimeDate.getDate() !== message.sendTimeDate.getDate()"> +
    + {{message.sendTimeDate.toLocaleDateString()}} +
    +
    +
    +
    +
    + +
    +
    -
    +
    -
    +
    {{ message.sender.nickName }} @@ -101,7 +114,7 @@
    {{message.content.split('|')[1]}}
    - -

    - - -

    -

    +

    Send failed

    +

    + + +