diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index f59f3b3e..74b830f9 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -237,7 +237,7 @@ export default class ChatContextMenu { }; private onSelectClick = () => { - appImManager.chatSelection.toggleByBubble(findUpClassName(this.target, 'bubble')); + appImManager.chatSelection.toggleByBubble(findUpClassName(this.target, 'album-item') || findUpClassName(this.target, 'bubble')); }; private onClearSelectionClick = () => { diff --git a/src/components/chat/selection.ts b/src/components/chat/selection.ts index 7cf39957..2308e8ea 100644 --- a/src/components/chat/selection.ts +++ b/src/components/chat/selection.ts @@ -154,6 +154,7 @@ export default class ChatSelection { public toggleBubbleCheckbox(bubble: HTMLElement, show: boolean) { const hasCheckbox = !!this.getCheckboxInputFromBubble(bubble); + const isAlbum = bubble.classList.contains('is-album'); if(show) { if(hasCheckbox) return; @@ -171,6 +172,10 @@ export default class ChatSelection { } else if(hasCheckbox) { bubble.firstElementChild.remove(); } + + if(isAlbum) { + this.appImManager.getBubbleAlbumItems(bubble).forEach(item => this.toggleBubbleCheckbox(item, show)); + } } public getCheckboxInputFromBubble(bubble: HTMLElement) { @@ -304,15 +309,40 @@ export default class ChatSelection { this.toggleSelection(false); } - public toggleByBubble(bubble: HTMLElement) { + private updateBubbleSelection(bubble: HTMLElement, isSelected: boolean) { + this.toggleBubbleCheckbox(bubble, true); + const input = this.getCheckboxInputFromBubble(bubble); + input.checked = isSelected; + + this.toggleSelection(); + this.updateForwardContainer(); + SetTransition(bubble, 'is-selected', isSelected, 200); + } + + public isAlbumBubbleSelected(bubble: HTMLElement) { + const albumCheckboxInput = this.getCheckboxInputFromBubble(bubble); + return albumCheckboxInput?.checked; + } + + public toggleByBubble = (bubble: HTMLElement) => { const mid = +bubble.dataset.mid; - const mids = this.appMessagesManager.getMidsByMid(mid); - const found = mids.find(mid => this.selectedMids.has(mid)); + const isAlbum = bubble.classList.contains('is-album'); + if(isAlbum) { + if(!this.isAlbumBubbleSelected(bubble)) { + const mids = this.appMessagesManager.getMidsByMid(mid); + mids.forEach(mid => this.selectedMids.delete(mid)); + } + + this.appImManager.getBubbleAlbumItems(bubble).forEach(this.toggleByBubble); + return; + } + + const found = this.selectedMids.has(mid); if(found) { - mids.forEach(mid => this.selectedMids.delete(mid)); + this.selectedMids.delete(mid); } else { - let diff = MAX_SELECTION_LENGTH - this.selectedMids.size - mids.length; + const diff = MAX_SELECTION_LENGTH - this.selectedMids.size - 1; if(diff < 0) { toast('Max selection count reached.'); return; @@ -331,15 +361,23 @@ export default class ChatSelection { } while(this.selectedMids.size > MAX_SELECTION_LENGTH); */ } - mids.forEach(mid => this.selectedMids.add(mid)); + this.selectedMids.add(mid); } - this.toggleBubbleCheckbox(bubble, true); - const input = this.getCheckboxInputFromBubble(bubble); - input.checked = !found; + const isAlbumItem = bubble.classList.contains('album-item'); + if(isAlbumItem) { + const albumContainer = findUpClassName(bubble, 'bubble'); + const isAlbumSelected = this.isAlbumBubbleSelected(albumContainer); - this.toggleSelection(); - this.updateForwardContainer(); - SetTransition(bubble, 'is-selected', !found, 200); - } + const mids = this.appMessagesManager.getMidsByMid(mid); + const selectedMids = mids.filter(mid => this.selectedMids.has(mid)); + + const willChange = mids.length == selectedMids.length || isAlbumSelected; + if(willChange) { + this.updateBubbleSelection(albumContainer, mids.length == selectedMids.length); + } + } + + this.updateBubbleSelection(bubble, !found); + }; } \ No newline at end of file diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 81da62d1..0504dc39 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -798,11 +798,14 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo div.style.borderBottomRightRadius = 'inherit'; } + const mediaDiv = document.createElement('div'); + mediaDiv.classList.add('album-item-media'); + if(media._ == 'photo') { wrapPhoto( media, message, - div, + mediaDiv, 0, 0, false, @@ -814,7 +817,7 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo } else { wrapVideo({ doc: message.media.document, - container: div, + container: mediaDiv, message, boxWidth: 0, boxHeight: 0, @@ -828,6 +831,7 @@ export function wrapAlbum({groupID, attachmentDiv, middleware, uploading, lazyLo // @ts-ignore //div.style.backgroundColor = '#' + Math.floor(Math.random() * (2 ** 24 - 1)).toString(16).padStart(6, '0'); + div.append(mediaDiv); attachmentDiv.append(div); } } diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index bec758dd..c390d6f9 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -528,7 +528,8 @@ export class AppImManager { return; } - this.chatSelection.toggleByBubble(bubble); + //this.chatSelection.toggleByBubble(bubble); + this.chatSelection.toggleByBubble(findUpClassName(target, 'album-item') || bubble); return; } @@ -892,6 +893,10 @@ export class AppImManager { return null; } + public getBubbleAlbumItems(bubble: HTMLElement) { + return Array.from(bubble.querySelectorAll('.album-item')) as HTMLElement[]; + } + public loadMoreHistory(top: boolean, justLoad = false) { //this.log('loadMoreHistory', top); if(!this.peerID || /* TEST_SCROLL || */ this.setPeerPromise || (top && this.getHistoryTopPromise) || (!top && this.getHistoryBottomPromise)) return; diff --git a/src/scss/components/_global.scss b/src/scss/components/_global.scss index 4c6014c4..4a8dce25 100644 --- a/src/scss/components/_global.scss +++ b/src/scss/components/_global.scss @@ -25,6 +25,9 @@ a { -webkit-tap-highlight-color: transparent; } +img, video { + -webkit-user-drag: none; +} // Positioning .valign-wrapper { diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 79b3e800..38ce344e 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -209,6 +209,9 @@ $bubble-margin: .25rem; } .checkbox-caption { + padding: 0; + width: 25px; + &:before { top: 7px !important; left: 3px !important; @@ -614,10 +617,7 @@ $bubble-margin: .25rem; max-height: none; .album-item { - background-color: #000; - background-size: cover; - /* background-position: center center; */ - /* flex: 1 0 auto; */ + background-color: lighten($color-blue, 35%); max-width: 100%; cursor: pointer; position: absolute; @@ -626,6 +626,40 @@ $bubble-margin: .25rem; img, video { border-radius: inherit; } + + .bubble-select-checkbox { + bottom: auto !important; + left: auto; + right: .25rem; + top: .25rem; + } + + &.is-selected { + border-radius: 0; + + .album-item-media { + transform: scale(1); + border-radius: 0; + } + + &.animating { + transition: border-radius var(--layer-transition); + + .album-item-media { + transition: transform var(--layer-transition), border-radius var(--layer-transition); + } + } + + &:not(.backwards) { + .album-item-media { + transform: scale(.925); + } + + &, .album-item-media { + border-radius: .5rem; + } + } + } } } } @@ -1463,6 +1497,10 @@ $bubble-margin: .25rem; .quote .name, .reply-title, .reply i { color: $darkgreen; } + + .album-item { + background-color: darken(#eeffde, 10%) !important; + } .time { padding-right: 4px; diff --git a/src/scss/style.scss b/src/scss/style.scss index efffe251..0c91cbf4 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -1023,3 +1023,10 @@ middle-ellipsis-element { overflow: hidden; display: block; } + +.album-item { + &-media { + width: 100%; + height: 100%; + } +}