@@ -5,31 +5,12 @@ import { ActivityBlock } from '../../abstract/ActivityBlock.js';
55import { UploaderBlock } from '../../abstract/UploaderBlock.js' ;
66import { stringToArray } from '../../utils/stringToArray.js' ;
77import { wildcardRegexp } from '../../utils/wildcardRegexp.js' ;
8- import { buildStyles } from './buildStyles .js' ;
9- import { registerMessage , unregisterMessage } from './messages .js' ;
8+ import { buildThemeDefinition } from './buildThemeDefinition .js' ;
9+ import { MessageBridge } from './MessageBridge .js' ;
1010import { queryString } from './query-string.js' ;
1111
1212/** @typedef {{ externalSourceType: string } } ActivityParams */
1313
14- /**
15- * @typedef {{
16- * type: 'file-selected';
17- * obj_type: 'selected_file';
18- * filename: string;
19- * url: string;
20- * alternatives?: Record<string, string>;
21- * }} SelectedFileMessage
22- */
23-
24- /**
25- * @typedef {{
26- * type: 'embed-css';
27- * style: string;
28- * }} EmbedCssMessage
29- */
30-
31- /** @typedef {SelectedFileMessage | EmbedCssMessage } Message */
32-
3314export class ExternalSource extends UploaderBlock {
3415 couldBeCtxOwner = true ;
3516 activityType = ActivityBlock . activities . EXTERNAL ;
@@ -41,12 +22,20 @@ export class ExternalSource extends UploaderBlock {
4122 ...this . init$ ,
4223 activityIcon : '' ,
4324 activityCaption : '' ,
25+
26+ /** @type {import('./types.js').InputMessageMap['selected-files-change']['selectedFiles'] } */
4427 selectedList : [ ] ,
45- counter : 0 ,
46- multiple : false ,
28+ total : 0 ,
29+
30+ isSelectionReady : false ,
31+ couldSelectAll : false ,
32+ couldDeselectAll : false ,
33+ showSelectionStatus : false ,
34+ counterText : '' ,
35+
4736 onDone : ( ) => {
4837 for ( const message of this . $ . selectedList ) {
49- const url = this . extractUrlFromMessage ( message ) ;
38+ const url = this . extractUrlFromSelectedFile ( message ) ;
5039 const { filename } = message ;
5140 const { externalSourceType } = this . activityParams ;
5241 this . api . addFileFromUrl ( url , { fileName : filename , source : externalSourceType } ) ;
@@ -57,6 +46,14 @@ export class ExternalSource extends UploaderBlock {
5746 onCancel : ( ) => {
5847 this . historyBack ( ) ;
5948 } ,
49+
50+ onSelectAll : ( ) => {
51+ this . _messageBridge ?. send ( { type : 'select-all' } ) ;
52+ } ,
53+
54+ onDeselectAll : ( ) => {
55+ this . _messageBridge ?. send ( { type : 'deselect-all' } ) ;
56+ } ,
6057 } ;
6158 }
6259
@@ -69,12 +66,6 @@ export class ExternalSource extends UploaderBlock {
6966 throw new Error ( `External Source activity params not found` ) ;
7067 }
7168
72- /**
73- * @private
74- * @type {HTMLIFrameElement | null }
75- */
76- _iframe = null ;
77-
7869 initCallback ( ) {
7970 super . initCallback ( ) ;
8071 this . registerActivity ( this . activityType , {
@@ -108,106 +99,97 @@ export class ExternalSource extends UploaderBlock {
10899 this . unmountIframe ( ) ;
109100 }
110101 } ) ;
111- this . sub ( 'selectedList' , ( list ) => {
112- this . $ . counter = list . length ;
113- } ) ;
114102 this . subConfigValue ( 'multiple' , ( multiple ) => {
115- this . $ . multiple = multiple ;
103+ this . $ . showSelectionStatus = multiple ;
104+ } ) ;
105+
106+ this . subConfigValue ( 'localeName' , ( val ) => {
107+ this . setupL10n ( ) ;
116108 } ) ;
117109 }
118110
119111 /**
120112 * @private
121- * @param {SelectedFileMessage } message
113+ * @param {NonNullable<import('./types.js').InputMessageMap['selected-files-change']['selectedFiles']>[number] } selectedFile
122114 */
123- extractUrlFromMessage ( message ) {
124- if ( message . alternatives ) {
115+ extractUrlFromSelectedFile ( selectedFile ) {
116+ if ( selectedFile . alternatives ) {
125117 const preferredTypes = stringToArray ( this . cfg . externalSourcesPreferredTypes ) ;
126118 for ( const preferredType of preferredTypes ) {
127119 const regexp = wildcardRegexp ( preferredType ) ;
128- for ( const [ type , typeUrl ] of Object . entries ( message . alternatives ) ) {
120+ for ( const [ type , typeUrl ] of Object . entries ( selectedFile . alternatives ) ) {
129121 if ( regexp . test ( type ) ) {
130122 return typeUrl ;
131123 }
132124 }
133125 }
134126 }
135127
136- return message . url ;
128+ return selectedFile . url ;
137129 }
138130
139131 /**
140132 * @private
141- * @param {Message } message
133+ * @param {import('./types.js').InputMessageMap['selected-files-change'] } message
142134 */
143- sendMessage ( message ) {
144- this . _iframe ?. contentWindow ?. postMessage ( JSON . stringify ( message ) , '*' ) ;
145- }
146-
147- /**
148- * @private
149- * @param {SelectedFileMessage } message
150- */
151- async handleFileSelected ( message ) {
152- if ( ! this . $ . multiple && this . $ . selectedList . length ) {
135+ async handleSelectedFilesChange ( message ) {
136+ if ( this . cfg . multiple !== message . isMultipleMode ) {
137+ console . error ( 'Multiple mode mismatch' ) ;
153138 return ;
154139 }
155140
156- this . $ . selectedList = [ ...this . $ . selectedList , message ] ;
157-
158- if ( ! this . $ . multiple ) {
159- this . $ . onDone ( ) ;
160- }
141+ this . bindL10n ( 'counterText' , ( ) =>
142+ this . l10n ( 'selected-count' , {
143+ count : message . selectedCount ,
144+ total : message . total ,
145+ } ) ,
146+ ) ;
147+
148+ this . set$ ( {
149+ isSelectionReady : message . isReady ,
150+ showSelectionStatus : message . isMultipleMode && message . total > 0 ,
151+ couldSelectAll : message . selectedCount < message . total ,
152+ couldDeselectAll : message . selectedCount === message . total ,
153+ selectedList : message . selectedFiles ,
154+ } ) ;
161155 }
162156
163157 /** @private */
164158 handleIframeLoad ( ) {
165159 this . applyStyles ( ) ;
166- }
167-
168- /**
169- * @private
170- * @param {string } propName
171- */
172- getCssValue ( propName ) {
173- let style = window . getComputedStyle ( this ) ;
174- return style . getPropertyValue ( propName ) . trim ( ) ;
160+ this . setupL10n ( ) ;
175161 }
176162
177163 /** @private */
178164 applyStyles ( ) {
179- let colors = {
180- radius : this . getCssValue ( '--uc-radius' ) ,
181- backgroundColor : this . getCssValue ( '--uc-background' ) ,
182- textColor : this . getCssValue ( '--uc-foreground' ) ,
183- secondaryColor : this . getCssValue ( '--uc-secondary' ) ,
184- secondaryForegroundColor : this . getCssValue ( '--uc-secondary-foreground' ) ,
185- secondaryHover : this . getCssValue ( '--uc-secondary-hover' ) ,
186- linkColor : this . getCssValue ( '--uc-primary' ) ,
187- linkColorHover : this . getCssValue ( '--uc-primary-hover' ) ,
188- fontFamily : this . getCssValue ( '--uc-font-family' ) ,
189- fontSize : this . getCssValue ( '--uc-font-size' ) ,
190- } ;
165+ this . _messageBridge ?. send ( {
166+ type : 'set-theme-definition' ,
167+ theme : buildThemeDefinition ( this ) ,
168+ } ) ;
169+ }
191170
192- this . sendMessage ( {
193- type : 'embed-css' ,
194- style : buildStyles ( colors ) ,
171+ /** @private */
172+ setupL10n ( ) {
173+ this . _messageBridge ?. send ( {
174+ type : 'set-locale-definition' ,
175+ localeDefinition : this . cfg . localeName ,
195176 } ) ;
196177 }
197178
198179 /** @private */
199180 remoteUrl ( ) {
200- const { pubkey, remoteTabSessionKey, socialBaseUrl } = this . cfg ;
181+ const { pubkey, remoteTabSessionKey, socialBaseUrl, multiple } = this . cfg ;
201182 const { externalSourceType } = this . activityParams ;
202183 const lang = this . l10n ( 'social-source-lang' ) ?. split ( '-' ) ?. [ 0 ] || 'en' ;
203184 const params = {
204185 lang,
205186 public_key : pubkey ,
206187 images_only : false . toString ( ) ,
207- pass_window_open : false ,
208188 session_key : remoteTabSessionKey ,
189+ wait_for_theme : true ,
190+ multiple : multiple . toString ( ) ,
209191 } ;
210- const url = new URL ( `/window3 /${ externalSourceType } ` , socialBaseUrl ) ;
192+ const url = new URL ( `/window4 /${ externalSourceType } ` , socialBaseUrl ) ;
211193 url . search = queryString ( params ) ;
212194 return url . toString ( ) ;
213195 }
@@ -231,31 +213,43 @@ export class ExternalSource extends UploaderBlock {
231213 this . ref . iframeWrapper . innerHTML = '' ;
232214 this . ref . iframeWrapper . appendChild ( iframe ) ;
233215
234- registerMessage ( 'file-selected' , iframe . contentWindow , this . handleFileSelected . bind ( this ) ) ;
216+ if ( ! iframe . contentWindow ) {
217+ return ;
218+ }
219+
220+ this . _messageBridge ?. destroy ( ) ;
221+
222+ /** @private */
223+ this . _messageBridge = new MessageBridge ( iframe . contentWindow ) ;
224+ this . _messageBridge . on ( 'selected-files-change' , this . handleSelectedFilesChange . bind ( this ) ) ;
235225
236- this . _iframe = iframe ;
237- this . $ . selectedList = [ ] ;
226+ this . resetSelectionStatus ( ) ;
238227 }
239228
240229 /** @private */
241230 unmountIframe ( ) {
242- this . _iframe && unregisterMessage ( 'file-selected' , this . _iframe . contentWindow ) ;
231+ this . _messageBridge ?. destroy ( ) ;
232+ this . _messageBridge = undefined ;
243233 this . ref . iframeWrapper . innerHTML = '' ;
244- this . _iframe = null ;
245- this . $ . selectedList = [ ] ;
246- this . $ . counter = 0 ;
234+
235+ this . resetSelectionStatus ( ) ;
236+ }
237+
238+ /** @private */
239+ resetSelectionStatus ( ) {
240+ this . set$ ( {
241+ selectedList : [ ] ,
242+ total : 0 ,
243+ isSelectionReady : false ,
244+ couldSelectAll : false ,
245+ couldDeselectAll : false ,
246+ showSelectionStatus : false ,
247+ } ) ;
247248 }
248249}
249250
250251ExternalSource . template = /* HTML */ `
251252 <uc-activity-header>
252- <button type="button" class="uc-mini-btn" set="onclick: *historyBack" l10n="@title:back">
253- <uc-icon name="back"></uc-icon>
254- </button>
255- <div>
256- <uc-icon set="@name: activityIcon"></uc-icon>
257- <span>{{activityCaption}}</span>
258- </div>
259253 <button
260254 type="button"
261255 class="uc-mini-btn uc-close-btn"
@@ -269,12 +263,15 @@ ExternalSource.template = /* HTML */ `
269263 <div ref="iframeWrapper" class="uc-iframe-wrapper"></div>
270264 <div class="uc-toolbar">
271265 <button type="button" class="uc-cancel-btn uc-secondary-btn" set="onclick: onCancel" l10n="cancel"></button>
272- <div></div>
273- <div set="@hidden: !multiple" class="uc-selected-counter"><span l10n="selected-count"></span>{{counter}}</div>
266+ <div set="@hidden: !showSelectionStatus" class="uc-selection-status-box">
267+ <span>{{counterText}}</span>
268+ <button type="button" set="onclick: onSelectAll; @hidden: !couldSelectAll" l10n="select-all"></button>
269+ <button type="button" set="onclick: onDeselectAll; @hidden: !couldDeselectAll" l10n="deselect-all"></button>
270+ </div>
274271 <button
275272 type="button"
276273 class="uc-done-btn uc-primary-btn"
277- set="onclick: onDone; @disabled: !counter "
274+ set="onclick: onDone; @disabled: !isSelectionReady "
278275 l10n="done"
279276 ></button>
280277 </div>
0 commit comments