Skip to content

Commit db836e0

Browse files
nd0utEgor Didenko
andauthored
feat: social sources redesign (#750)
* feat: new social sources * chore: extract types to separate file * chore: reduce theme variables list * chore: reset selection state on unmount * chore: add social sourcesdemo page * chore: fix activity width * chore: disable scale for social source demo * chore: increase modal size & refactor modal css & remove dialog api fallback * chore: fix upload list height on safari * chore: added l10n * chore: cleanup * chore(l10n): add missing keys for all the locales --------- Co-authored-by: nd0ut <[email protected]> Co-authored-by: Egor Didenko <[email protected]>
1 parent a67c0ad commit db836e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+686
-521
lines changed

blocks/CameraSource/CameraSource.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ export class CameraSource extends UploaderBlock {
6666
* @param {'granted' | 'denied' | 'prompt'} state
6767
*/
6868
_setPermissionsState = debounce((state) => {
69-
this.classList.toggle('uc-initialized', state === 'granted');
70-
7169
if (state === 'granted') {
7270
this.set$({
7371
videoHidden: false,

blocks/CameraSource/camera-source.css

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,9 @@ uc-camera-source {
1010
border-radius: var(--uc-radius);
1111
}
1212

13-
[uc-modal] uc-camera-source {
14-
width: min(calc(var(--uc-dialog-max-width) - var(--uc-padding) * 2), calc(100vw - var(--uc-padding) * 2));
15-
height: 100vh;
16-
max-height: var(--modal-max-content-height);
17-
}
18-
19-
uc-camera-source.uc-initialized {
20-
height: max-content;
21-
}
22-
23-
@media only screen and (max-width: 430px) {
24-
uc-camera-source {
25-
width: calc(100vw - var(--uc-padding) * 2);
26-
height: var(--modal-content-height-fill, 100%);
27-
}
13+
[uc-modal] > dialog:has(uc-camera-source[active]) {
14+
width: 100%;
15+
height: 100%;
2816
}
2917

3018
uc-camera-source video {
@@ -52,6 +40,7 @@ uc-camera-source .uc-content {
5240
flex: 1;
5341
justify-content: center;
5442
width: 100%;
43+
height: 100%;
5544
padding: var(--uc-padding);
5645
padding-top: 0;
5746
overflow: hidden;

blocks/CloudImageEditorActivity/index.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ uc-cloud-image-editor-activity {
77
background-color: var(--uc-background);
88
}
99

10-
[uc-modal] uc-cloud-image-editor-activity {
11-
width: min(calc(var(--uc-dialog-max-width) - var(--uc-padding) * 2), calc(100vw - var(--uc-padding) * 2));
12-
height: var(--modal-content-height-fill, 100%);
10+
[uc-modal] > dialog:has(uc-cloud-image-editor-activity[active]) {
11+
width: 100%;
12+
height: 100%;
1313
}

blocks/ExternalSource/ExternalSource.js

Lines changed: 96 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,12 @@ import { ActivityBlock } from '../../abstract/ActivityBlock.js';
55
import { UploaderBlock } from '../../abstract/UploaderBlock.js';
66
import { stringToArray } from '../../utils/stringToArray.js';
77
import { 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';
1010
import { 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-
3314
export 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

250251
ExternalSource.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

Comments
 (0)