@@ -11,7 +11,7 @@ import rootScope from '../lib/rootScope';
11
11
import Scrollable from './scrollable' ;
12
12
import { FocusDirection } from '../helpers/fastSmoothScroll' ;
13
13
import CheckboxField from './checkboxField' ;
14
- import { i18n , LangPackKey , _i18n } from '../lib/langPack' ;
14
+ import { _i18n , i18n , LangPackKey } from '../lib/langPack' ;
15
15
import findUpAttribute from '../helpers/dom/findUpAttribute' ;
16
16
import findUpClassName from '../helpers/dom/findUpClassName' ;
17
17
import PeerTitle from './peerTitle' ;
@@ -38,12 +38,112 @@ import getDialogIndex from '../lib/appManagers/utils/dialogs/getDialogIndex';
38
38
import { generateDelimiter } from './generateDelimiter' ;
39
39
import SettingSection from './settingSection' ;
40
40
import liteMode from '../helpers/liteMode' ;
41
+ import { ButtonMenuItemOptions , ButtonMenuSync } from "./buttonMenu" ;
42
+ import ListenerSetter from "../helpers/listenerSetter" ;
43
+ import { attachContextMenuListener } from "../helpers/dom/attachContextMenuListener" ;
44
+ import positionMenu from "../helpers/positionMenu" ;
45
+ import contextMenuController from "../helpers/contextMenuController" ;
46
+ import { addFullScreenListener , isFullScreen } from "../helpers/dom/fullScreen" ;
41
47
42
48
type SelectSearchPeerType = 'contacts' | 'dialogs' | 'channelParticipants' ;
43
49
44
50
// TODO: правильная сортировка для addMembers, т.е. для peerType: 'contacts', потому что там идут сначала контакты - потом неконтакты, а должно всё сортироваться по имени
51
+ export class AppSelectPeersContextMenu {
52
+ private buttons : ( ButtonMenuItemOptions & { verify : ( peerId : PeerId ) => boolean | Promise < boolean > } ) [ ] ;
53
+ private element : HTMLElement ;
54
+ private targetPeerId : PeerId ;
55
+ private managers : AppManagers ;
56
+
57
+
58
+ constructor ( options : {
59
+ listenerSetter : ListenerSetter ,
60
+ onContextElement : HTMLElement ,
61
+ toggleMultiSelectState : ( ) => void ,
62
+ selectContact : ( key : PeerId | string ) => void
63
+ } ) {
64
+ const { listenerSetter} = options ;
65
+ this . buttons = [ {
66
+ icon : 'select' ,
67
+ text : 'Message.Context.Select' ,
68
+ verify : ( ) => true ,
69
+ onClick : ( e ) => {
70
+ cancelEvent ( e ) ;
71
+ options . toggleMultiSelectState ( ) ;
72
+ options . selectContact ( this . targetPeerId ) ;
73
+ console . log ( this . targetPeerId )
74
+ }
75
+ } ] ;
76
+ this . element = ButtonMenuSync ( { buttons : this . buttons , listenerSetter} ) ;
77
+ this . element . classList . add ( 'select-peers-menu' , 'night' ) ;
78
+
79
+ attachContextMenuListener ( {
80
+ element : options . onContextElement ,
81
+ callback : async ( e ) => {
82
+
83
+ const li = findUpClassName ( e . target , 'chatlist-chat' ) ;
84
+ if ( ! li ) {
85
+ return ;
86
+ }
87
+
88
+ if ( this . element . parentElement !== appendTo ) {
89
+ appendTo . append ( this . element ) ;
90
+ }
91
+
92
+ cancelEvent ( e ) ;
93
+
94
+ const peerId = this . targetPeerId = li . dataset . peerId . toPeerId ( ) ;
95
+
96
+ await filterAsync ( this . buttons , async ( button ) => {
97
+ const good = await button . verify ( peerId ) ;
98
+ button . element . classList . toggle ( 'hide' , ! good ) ;
99
+ return good ;
100
+ } ) ;
101
+
102
+ positionMenu ( ( e as TouchEvent ) . touches ? ( e as TouchEvent ) . touches [ 0 ] : e as MouseEvent , this . element , 'right' ) ;
103
+ contextMenuController . openBtnMenu ( this . element ) ;
104
+ } ,
105
+ listenerSetter
106
+ } ) ;
107
+
108
+ let appendTo : HTMLElement = document . body ;
109
+ addFullScreenListener ( document . body , ( ) => {
110
+ const isFull = isFullScreen ( ) ;
111
+ appendTo = document . body ;
112
+
113
+ if ( ! isFull ) {
114
+ contextMenuController . close ( ) ;
115
+ }
116
+ } , listenerSetter ) ;
117
+ }
118
+
119
+ }
120
+
121
+ export interface ISelectPeers {
122
+ appendTo : AppSelectPeers [ 'appendTo' ] ,
123
+ onChange ?: AppSelectPeers [ 'onChange' ] ,
124
+ peerType ?: AppSelectPeers [ 'peerType' ] ,
125
+ peerId ?: AppSelectPeers [ 'peerId' ] ,
126
+ onFirstRender ?: ( ) => void ,
127
+ renderResultsFunc ?: AppSelectPeers [ 'renderResultsFunc' ] ,
128
+ chatRightsActions ?: AppSelectPeers [ 'chatRightsActions' ] ,
129
+ multiSelect ?: AppSelectPeers [ 'multiSelect' ] ,
130
+ rippleEnabled ?: AppSelectPeers [ 'rippleEnabled' ] ,
131
+ avatarSize ?: AppSelectPeers [ 'avatarSize' ] ,
132
+ placeholder ?: AppSelectPeers [ 'placeholder' ] ,
133
+ selfPresence ?: AppSelectPeers [ 'selfPresence' ] ,
134
+ exceptSelf ?: AppSelectPeers [ 'exceptSelf' ] ,
135
+ filterPeerTypeBy ?: AppSelectPeers [ 'filterPeerTypeBy' ] ,
136
+ sectionNameLangPackKey ?: AppSelectPeers [ 'sectionNameLangPackKey' ] ,
137
+ managers : AppSelectPeers [ 'managers' ] ,
138
+ design ?: AppSelectPeers [ 'design' ] ,
139
+ listenerSetter ?: ListenerSetter ,
140
+ toggleableMultiSelect ?: boolean ,
141
+ }
142
+
45
143
46
144
export default class AppSelectPeers {
145
+ private contextMenu : AppSelectPeersContextMenu ;
146
+ private listenerSetter : ListenerSetter ;
47
147
public container = document . createElement ( 'div' ) ;
48
148
public list = appDialogsManager . createChatList ( /* {
49
149
handheldsSize: 66,
@@ -52,8 +152,7 @@ export default class AppSelectPeers {
52
152
private chatsContainer = document . createElement ( 'div' ) ;
53
153
public scrollable : Scrollable ;
54
154
private selectedScrollable : Scrollable ;
55
-
56
- private selectedContainer : HTMLElement ;
155
+ public selectedContainer : HTMLElement ;
57
156
public input : HTMLInputElement ;
58
157
59
158
// public selected: {[peerId: PeerId]: HTMLElement} = {};
@@ -68,7 +167,7 @@ export default class AppSelectPeers {
68
167
private query = '' ;
69
168
private cachedContacts : PeerId [ ] ;
70
169
71
- private loadedWhat : Partial < { [ k in 'dialogs' | 'archived' | 'contacts' | 'channelParticipants' ] : true } > = { } ;
170
+ private loadedWhat : Partial < { [ k in 'dialogs' | 'archived' | 'contacts' | 'channelParticipants' ] : true } > = { } ;
72
171
73
172
private renderedPeerIds : Set < PeerId > = new Set ( ) ;
74
173
@@ -83,7 +182,7 @@ export default class AppSelectPeers {
83
182
private exceptSelf = false ;
84
183
private filterPeerTypeBy : IsPeerType [ ] ;
85
184
86
- private tempIds : { [ k in keyof AppSelectPeers [ 'loadedWhat' ] ] : number } = { } ;
185
+ private tempIds : { [ k in keyof AppSelectPeers [ 'loadedWhat' ] ] : number } = { } ;
87
186
private peerId : PeerId ;
88
187
89
188
private placeholder : LangPackKey ;
@@ -98,25 +197,11 @@ export default class AppSelectPeers {
98
197
99
198
private design : 'round' | 'square' = 'round' ;
100
199
101
- constructor ( options : {
102
- appendTo : AppSelectPeers [ 'appendTo' ] ,
103
- onChange ?: AppSelectPeers [ 'onChange' ] ,
104
- peerType ?: AppSelectPeers [ 'peerType' ] ,
105
- peerId ?: AppSelectPeers [ 'peerId' ] ,
106
- onFirstRender ?: ( ) => void ,
107
- renderResultsFunc ?: AppSelectPeers [ 'renderResultsFunc' ] ,
108
- chatRightsActions ?: AppSelectPeers [ 'chatRightsActions' ] ,
109
- multiSelect ?: AppSelectPeers [ 'multiSelect' ] ,
110
- rippleEnabled ?: AppSelectPeers [ 'rippleEnabled' ] ,
111
- avatarSize ?: AppSelectPeers [ 'avatarSize' ] ,
112
- placeholder ?: AppSelectPeers [ 'placeholder' ] ,
113
- selfPresence ?: AppSelectPeers [ 'selfPresence' ] ,
114
- exceptSelf ?: AppSelectPeers [ 'exceptSelf' ] ,
115
- filterPeerTypeBy ?: AppSelectPeers [ 'filterPeerTypeBy' ] ,
116
- sectionNameLangPackKey ?: AppSelectPeers [ 'sectionNameLangPackKey' ] ,
117
- managers : AppSelectPeers [ 'managers' ] ,
118
- design ?: AppSelectPeers [ 'design' ]
119
- } ) {
200
+ public toggleableMultiSelect : boolean = false ;
201
+ public toggleableMultiSelectState : boolean = false ;
202
+
203
+
204
+ constructor ( options : ISelectPeers ) {
120
205
safeAssign ( this , options ) ;
121
206
122
207
this . container . classList . add ( 'selector' , 'selector-' + this . design ) ;
@@ -195,7 +280,6 @@ export default class AppSelectPeers {
195
280
simulateClickEvent ( li ) ;
196
281
}
197
282
} ) ;
198
-
199
283
section . content . append ( topContainer ) ;
200
284
this . container . append ( section . container /* , delimiter */ ) ;
201
285
}
@@ -221,7 +305,7 @@ export default class AppSelectPeers {
221
305
let key : PeerId | string = target . dataset . peerId ;
222
306
key = key . isPeerId ( ) ? key . toPeerId ( ) : key ;
223
307
224
- if ( ! this . multiSelect ) {
308
+ if ( ! this . multiSelect || ( this . multiSelect && this . toggleableMultiSelect && ! this . toggleableMultiSelectState ) ) {
225
309
this . add ( key ) ;
226
310
return ;
227
311
}
@@ -260,6 +344,10 @@ export default class AppSelectPeers {
260
344
} , 0 ) ;
261
345
}
262
346
347
+ private toggleMultiSelectState = ( ) => {
348
+ this . toggleableMultiSelectState = true ;
349
+ this . container . querySelectorAll ( '.checkbox-field' ) . forEach ( node => node . classList . remove ( 'hide' ) ) ;
350
+ }
263
351
private onInput = ( ) => {
264
352
const value = this . input . value ;
265
353
if ( this . query !== value ) {
@@ -553,7 +641,7 @@ export default class AppSelectPeers {
553
641
}
554
642
555
643
private getMoreSomething ( peerType : SelectSearchPeerType ) {
556
- const map : { [ type in SelectSearchPeerType ] : ( ) => Promise < any > } = {
644
+ const map : { [ type in SelectSearchPeerType ] : ( ) => Promise < any > } = {
557
645
dialogs : this . getMoreDialogs ,
558
646
contacts : this . getMoreContacts ,
559
647
channelParticipants : this . getMoreChannelParticipants
@@ -565,7 +653,22 @@ export default class AppSelectPeers {
565
653
566
654
private async renderResults ( peerIds : PeerId [ ] ) {
567
655
// console.log('will renderResults:', peerIds);
568
-
656
+ if ( this . toggleableMultiSelect ) {
657
+ const { listenerSetter} = this ;
658
+ this . contextMenu = new AppSelectPeersContextMenu ( {
659
+ onContextElement : this . list ,
660
+ listenerSetter,
661
+ toggleMultiSelectState : ( ) => {
662
+ this . toggleMultiSelectState ( ) ;
663
+ } ,
664
+ selectContact : ( peerId ) => {
665
+ window . requestAnimationFrame ( ( ) => {
666
+ const li = this . chatsContainer . querySelector ( '[data-peer-id="' + peerId + '"]' ) as HTMLElement ;
667
+ simulateClickEvent ( li ) ;
668
+ } )
669
+ }
670
+ } ) ;
671
+ }
569
672
// оставим только неконтакты с диалогов
570
673
if ( ! this . peerType . includes ( 'dialogs' ) && this . loadedWhat . contacts ) {
571
674
peerIds = await filterAsync ( peerIds , ( peerId ) => {
@@ -582,8 +685,12 @@ export default class AppSelectPeers {
582
685
} ) ;
583
686
584
687
if ( this . multiSelect ) {
688
+
585
689
const selected = this . selected . has ( peerId ) ;
586
- const checkboxField = new CheckboxField ( ) ;
690
+ const checkboxField = new CheckboxField ( { round : true } ) ;
691
+ if ( ! this . toggleableMultiSelectState ) {
692
+ checkboxField . hide ( ) ;
693
+ }
587
694
588
695
if ( selected ) {
589
696
// dom.listEl.classList.add('active');
@@ -610,7 +717,7 @@ export default class AppSelectPeers {
610
717
// console.trace('add');
611
718
this . selected . add ( key ) ;
612
719
613
- if ( ! this . multiSelect ) {
720
+ if ( ! this . multiSelect || ( this . multiSelect && this . toggleableMultiSelect && ! this . toggleableMultiSelectState ) ) {
614
721
this . onChange ( this . selected . size ) ;
615
722
return ;
616
723
}
@@ -641,7 +748,7 @@ export default class AppSelectPeers {
641
748
}
642
749
643
750
if ( title ) {
644
- if ( typeof ( title ) === 'string' ) {
751
+ if ( typeof ( title ) === 'string' ) {
645
752
div . innerHTML = title ;
646
753
} else {
647
754
replaceContent ( div , title ) ;
@@ -650,8 +757,12 @@ export default class AppSelectPeers {
650
757
}
651
758
652
759
div . insertAdjacentElement ( 'afterbegin' , avatarEl ) ;
760
+ try {
761
+ this . selectedContainer . insertBefore ( div , this . input ) ;
762
+ } catch ( e ) {
763
+ this . selectedContainer . append ( div ) ;
764
+ }
653
765
654
- this . selectedContainer . insertBefore ( div , this . input ) ;
655
766
// this.selectedScrollable.scrollTop = this.selectedScrollable.scrollHeight;
656
767
this . onChange ?.( this . selected . size ) ;
657
768
0 commit comments