@designcrowd/library.brand-page 5.6.3 → 5.6.4-fluid-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/lib/src/brand-page/components/banner-section/BannerSection.mixin.js +5 -0
  2. package/lib/src/brand-page/components/payment/__fixtures__/request-payment.d.ts +3 -0
  3. package/lib/src/brand-page/components/payment/__fixtures__/support-me.d.ts +3 -0
  4. package/lib/src/brand-page/models/brand-page-context.model.d.ts +3 -0
  5. package/lib/src/brand-page/utils/migration/migration-03.d.ts +5 -0
  6. package/lib/src/brand-page/utils/migration/migration-03.js +28 -0
  7. package/lib/src/brand-page/utils/migration.util.js +5 -0
  8. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +1 -1
  9. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.js +37 -2
  10. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +1 -0
  11. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.js +11 -0
  12. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.js +3 -0
  13. package/lib/src/brand-page-maker/convertors/text-size-value.convertor.js +3 -3
  14. package/lib/src/brand-page-maker/utils/text.util.d.ts +2 -2
  15. package/lib/src/brand-page-maker/utils/text.util.js +3 -3
  16. package/lib/src/clients/user-media-client.d.ts +1 -2
  17. package/lib/src/clients/user-media-client.js +5 -19
  18. package/lib/src/css/library.brand-page-brandCrowd.min.css +19 -47
  19. package/lib/src/css/library.brand-page-brandPage.min.css +19 -47
  20. package/lib/src/css/library.brand-page-designCom.min.css +19 -47
  21. package/lib/src/css/library.brand-page-preview.min.css +19 -47
  22. package/lib/src/index.mjs +11068 -11101
  23. package/lib/src/shared/my-uploads/MyMedia.mixin.d.ts +0 -4
  24. package/lib/src/shared/my-uploads/MyMedia.mixin.js +5 -24
  25. package/lib/src/shared/my-uploads/MyUploads.mixin.js +1 -1
  26. package/lib/src/shared/my-uploads/models.d.ts +0 -1
  27. package/package.json +1 -1
  28. package/src/brand-page/components/banner-section/BannerSection.mixin.ts +5 -0
  29. package/src/brand-page/components/page/Page.vue +5 -1
  30. package/src/brand-page/components/text-wrapper/TextWrapper.vue +156 -12
  31. package/src/brand-page/models/brand-page-context.model.ts +3 -0
  32. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +26 -26
  33. package/src/brand-page/utils/migration/migration-03.ts +40 -0
  34. package/src/brand-page/utils/migration.util.ts +5 -0
  35. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +1 -1
  36. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.ts +43 -3
  37. package/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.ts +13 -0
  38. package/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.ts +3 -0
  39. package/src/brand-page-maker/convertors/text-size-value.convertor.ts +3 -3
  40. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +12 -3
  41. package/src/brand-page-maker/utils/text.util.ts +3 -3
  42. package/src/clients/user-media-client.ts +15 -32
  43. package/src/shared/my-uploads/MyMedia.mixin.ts +8 -36
  44. package/src/shared/my-uploads/MyMedia.vue +10 -9
  45. package/src/shared/my-uploads/MyUploads.mixin.ts +0 -1
  46. package/src/shared/my-uploads/models.ts +0 -1
  47. package/lib/src/brand-page/components/text-wrapper/TextWrapper.mixin.d.ts +0 -44
  48. package/lib/src/brand-page/components/text-wrapper/TextWrapper.mixin.js +0 -38
  49. package/src/brand-page/components/text-wrapper/TextWrapper.mixin.ts +0 -41
  50. package/src/shared/my-uploads/MyMediaSelectionItem.vue +0 -137
@@ -231,8 +231,48 @@ export const SampleBanner = (): any => {
231
231
  const brandPageMakerStore = useBrandPageMakerStore();
232
232
 
233
233
  const bannerContent = createInitialBannerSectionContent();
234
- bannerContent.description = 'Lorem ipsum dolor sit amet';
235
-
234
+ bannerContent.title = 'LAUNCHING SOON';
235
+ bannerContent.description = 'This Is Going To Be Epic';
236
+ bannerContent.buttonText = 'CONTACT US';
237
+ bannerContent.titleStyle.textStyleType = undefined;
238
+ bannerContent.titleStyle.fontSize = '6.25em';
239
+ bannerContent.titleStyle.fontFamily = 'Ubuntu';
240
+ bannerContent.titleStyle.lineHeight = '1';
241
+ bannerContent.titleStyle.textAlign = 'left';
242
+ bannerContent.displayStyle = 'left-aligned';
243
+
244
+ bannerContent.descriptionStyle.textStyleType = undefined;
245
+ bannerContent.descriptionStyle.fontFamily = 'Work Sans';
246
+ bannerContent.descriptionStyle.fontSize = '2.6875em';
247
+ bannerContent.descriptionStyle.textAlign = 'left';
248
+
249
+ bannerContent.buttonTextStyle.textStyleType = undefined;
250
+ bannerContent.buttonTextStyle.fontFamily = 'Ubuntu';
251
+ bannerContent.buttonTextStyle.fontSize = '1.5em';
252
+ bannerContent.buttonTextStyle.textAlign = 'left';
253
+
254
+ console.log('bannerContent', bannerContent);
255
+
256
+ const bannerContent2 = createInitialBannerSectionContent();
257
+ bannerContent2.title = 'LAUNCHING SOON';
258
+ bannerContent2.description = 'This Is Going To Be Epic2';
259
+ bannerContent2.buttonText = 'CONTACT US';
260
+ bannerContent2.titleStyle.textStyleType = undefined;
261
+ bannerContent2.titleStyle.fontSize = '6.25em';
262
+ bannerContent2.titleStyle.fontFamily = 'Ubuntu';
263
+ bannerContent2.titleStyle.lineHeight = '1';
264
+ bannerContent2.titleStyle.textAlign = 'left';
265
+ bannerContent2.displayStyle = 'left-aligned';
266
+
267
+ bannerContent2.descriptionStyle.textStyleType = undefined;
268
+ bannerContent2.descriptionStyle.fontFamily = 'Work Sans';
269
+ bannerContent2.descriptionStyle.fontSize = '2.6875em';
270
+ bannerContent2.descriptionStyle.textAlign = 'left';
271
+
272
+ bannerContent2.buttonTextStyle.textStyleType = undefined;
273
+ bannerContent2.buttonTextStyle.fontFamily = 'Ubuntu';
274
+ bannerContent2.buttonTextStyle.fontSize = '1.5em';
275
+ bannerContent2.buttonTextStyle.textAlign = 'left';
236
276
  brandPageMakerStore.setViewport({
237
277
  viewport: VIEWPORTS.desktop,
238
278
  });
@@ -250,7 +290,7 @@ export const SampleBanner = (): any => {
250
290
  {
251
291
  title: 'Home',
252
292
  slug: 'home',
253
- contents: [bannerContent],
293
+ contents: [bannerContent, bannerContent2],
254
294
  },
255
295
  ],
256
296
  brandPageType: BRAND_PAGE_TYPES.brandSite,
@@ -106,7 +106,13 @@ export default defineComponent({
106
106
  };
107
107
  },
108
108
  },
109
+ mounted() {
110
+ this.dispatchViewPortChangeEvent();
111
+ },
109
112
  watch: {
113
+ viewport() {
114
+ this.dispatchViewPortChangeEvent();
115
+ },
110
116
  selectedItemId(itemId: string) {
111
117
  this.scrollToContentItemByItemId(itemId);
112
118
  },
@@ -134,6 +140,13 @@ export default defineComponent({
134
140
  },
135
141
  },
136
142
  methods: {
143
+ dispatchViewPortChangeEvent() {
144
+ window.dispatchEvent(
145
+ new CustomEvent('editor:viewport', {
146
+ detail: { viewport: this.viewport },
147
+ }),
148
+ );
149
+ },
137
150
  onDraggingStart(): void {
138
151
  this.isDraggingContent = true;
139
152
 
@@ -139,6 +139,9 @@ export default defineComponent({
139
139
  this.viewModel?.brandPageType === BRAND_PAGE_TYPES.brandSite,
140
140
  isBrandContact:
141
141
  this.viewModel?.brandPageType === BRAND_PAGE_TYPES.brandContact,
142
+ options: {
143
+ disableNavigationSideMenu: true,
144
+ },
142
145
  };
143
146
  },
144
147
 
@@ -1,6 +1,6 @@
1
1
  import type { ValueConvertor } from '../models';
2
2
  import { DEFAULT_FONT_SIZE } from '../../brand-page/constants/';
3
- import { pxToRem, remToPx } from '../utils/text.util';
3
+ import { pxToEm, emToPx } from '../utils/text.util';
4
4
 
5
5
  export const textSizeValueConvertor: ValueConvertor<BrandPageCssStyle, string> =
6
6
  {
@@ -8,13 +8,13 @@ export const textSizeValueConvertor: ValueConvertor<BrandPageCssStyle, string> =
8
8
 
9
9
  convert(value: BrandPageCssStyle): string {
10
10
  return (
11
- value.fontSize ? remToPx(value.fontSize) : DEFAULT_FONT_SIZE
11
+ value.fontSize ? emToPx(value.fontSize) : DEFAULT_FONT_SIZE
12
12
  ).toString();
13
13
  },
14
14
 
15
15
  convertBack(valuePx: string): BrandPageCssStyle {
16
16
  return {
17
- fontSize: pxToRem(valuePx),
17
+ fontSize: pxToEm(valuePx),
18
18
  };
19
19
  },
20
20
  };
@@ -472,13 +472,22 @@ describe('brand page maker store module', () => {
472
472
  // Assert
473
473
  expect(
474
474
  styleSettingsStore.textStyles.textStyleOptions.titles.style.fontSize,
475
- ).toBe(textStyles.textStyleOptions.titles.style.fontSize);
475
+ ).toBe(
476
+ textStyles.textStyleOptions.titles.style.fontSize?.replace('rem', 'em'),
477
+ );
476
478
  expect(
477
479
  styleSettingsStore.textStyles.textStyleOptions.headings.style.fontSize,
478
- ).toBe(textStyles.textStyleOptions.headings.style.fontSize);
480
+ ).toBe(
481
+ textStyles.textStyleOptions.headings.style.fontSize?.replace('rem', 'em'),
482
+ );
479
483
  expect(
480
484
  styleSettingsStore.textStyles.textStyleOptions.paragraphs.style.fontSize,
481
- ).toBe(textStyles.textStyleOptions.paragraphs.style.fontSize);
485
+ ).toBe(
486
+ textStyles.textStyleOptions.paragraphs.style.fontSize?.replace(
487
+ 'rem',
488
+ 'em',
489
+ ),
490
+ );
482
491
  });
483
492
 
484
493
  test('applySelectedTemplate should not set design template token when applying selected template and is admin', async () => {
@@ -1,10 +1,10 @@
1
- export const pxToRem = (px: string) => {
1
+ export const pxToEm = (px: string) => {
2
2
  const value = parseFloat(px) / 16;
3
3
 
4
- return `${value}rem`;
4
+ return `${value}em`;
5
5
  };
6
6
 
7
- export const remToPx = (rem: string) => {
7
+ export const emToPx = (rem: string) => {
8
8
  const value = parseFloat(rem) * 16;
9
9
 
10
10
  return `${value}`;
@@ -24,7 +24,7 @@ export class UserMediaClient {
24
24
 
25
25
  try {
26
26
  const response = await axios.get<PolicyData>(
27
- `${this.endpoints.userPolicyDataEndpoint}?isPublic=${isPublic}&isUpload=true`,
27
+ `${this.endpoints.userPolicyDataEndpoint}?isPublic=${isPublic}`,
28
28
  );
29
29
 
30
30
  if (response.status !== 200) {
@@ -56,21 +56,19 @@ export class UserMediaClient {
56
56
  isPublic: boolean,
57
57
  relatedAssetToken: string | undefined,
58
58
  mediaType: string,
59
- hasTriggeredProcessing: boolean,
60
59
  ): Promise<UserMediaItem | undefined> {
61
60
  try {
62
- const reqUrl = hasTriggeredProcessing
63
- ? `${this.endpoints.createUserMediaEndpoint}/${mediaToken}?hasTriggeredProcessing=${hasTriggeredProcessing}`
64
- : `${this.endpoints.createUserMediaEndpoint}/${mediaToken}`;
65
-
66
- const response = await axios.put<UserMediaItem>(reqUrl, {
67
- mediaToken,
68
- mimeType: type,
69
- name,
70
- isPublic,
71
- relatedAssetToken,
72
- mediaType,
73
- });
61
+ const response = await axios.put<UserMediaItem>(
62
+ `${this.endpoints.createUserMediaEndpoint}/${mediaToken}`,
63
+ {
64
+ mediaToken,
65
+ mimeType: type,
66
+ name,
67
+ isPublic,
68
+ relatedAssetToken,
69
+ mediaType,
70
+ },
71
+ );
74
72
 
75
73
  return response.data;
76
74
  } catch (e) {
@@ -120,7 +118,6 @@ export class UserMediaClient {
120
118
  true,
121
119
  relatedAssetToken,
122
120
  file.mediaType,
123
- false,
124
121
  );
125
122
 
126
123
  return newMedia?.url;
@@ -135,7 +132,9 @@ export class UserMediaClient {
135
132
 
136
133
  file.mediaToken = mediaToken;
137
134
 
138
- const key = `${this.policyData.keyPrefix}${file.mediaToken}`;
135
+ const key = `${this.policyData.keyPrefix}${
136
+ file.mediaToken
137
+ }${getExtensionFromMimeType(file.type)}`;
139
138
 
140
139
  file.key = key;
141
140
  formData.append('key', key);
@@ -179,22 +178,6 @@ export class UserMediaClient {
179
178
  }
180
179
  }
181
180
 
182
- async getUserMediaByTokenAsync(
183
- mediaToken: string,
184
- ): Promise<UserMediaItem | undefined> {
185
- try {
186
- const response = await axios.get(
187
- `${this.endpoints.getAllUserMediaEndpoint}/${mediaToken}?isPublic=true`,
188
- );
189
-
190
- return response.data;
191
- } catch (e) {
192
- if (process.env.NODE_ENV !== 'production') {
193
- console.error(e);
194
- }
195
- }
196
- }
197
-
198
181
  async deleteUserMediaByUserMediaToken(
199
182
  userMediaToken: string,
200
183
  ): Promise<boolean> {
@@ -2,10 +2,7 @@ import { UI_EVENTS } from '../../brand-page-maker/constants';
2
2
  import type { PropType } from 'vue';
3
3
  import { computed, defineComponent } from 'vue';
4
4
  import type { UserMediaItemWithDimensions } from './models';
5
- import {
6
- getInstance as getUserMediaClientInstance,
7
- UserMediaClient,
8
- } from '../../clients/user-media-client';
5
+ import { getInstance as getUserMediaClientInstance } from '../../clients/user-media-client';
9
6
  import { preloadImage } from '../../brand-page/utils';
10
7
  import { useUiStore } from '../../brand-page-maker/stores';
11
8
  import { MEDIA_TYPES } from '../../brand-page/models/brand-page-context.model';
@@ -40,7 +37,6 @@ export default defineComponent({
40
37
  isDeleting: false as boolean,
41
38
  selectedDeleteMediaItem: null as UserMediaItemWithDimensions | null,
42
39
  userMedia: [] as UserMediaItemWithDimensions[],
43
- userMediaClient: null as UserMediaClient | null,
44
40
  loading: false,
45
41
  };
46
42
  },
@@ -60,8 +56,6 @@ export default defineComponent({
60
56
  },
61
57
  },
62
58
  async mounted() {
63
- this.userMediaClient = getUserMediaClientInstance(this.endpoints);
64
-
65
59
  await this.loadUserMedia();
66
60
 
67
61
  if (window) {
@@ -82,7 +76,8 @@ export default defineComponent({
82
76
  methods: {
83
77
  async loadUserMedia() {
84
78
  this.loading = true;
85
- const images = await this.userMediaClient.getUserMediaAsync(
79
+ const userMediaClient = getUserMediaClientInstance(this.endpoints);
80
+ const images = await userMediaClient.getUserMediaAsync(
86
81
  this.supportedFileExtensions,
87
82
  true,
88
83
  this.mediaType,
@@ -147,10 +142,11 @@ export default defineComponent({
147
142
  this.isDeleting = true;
148
143
 
149
144
  try {
150
- const deleted =
151
- await this.userMediaClient.deleteUserMediaByUserMediaToken(
152
- this.selectedDeleteMediaItem.media.token,
153
- );
145
+ const userMediaClient = getUserMediaClientInstance(this.endpoints);
146
+
147
+ const deleted = await userMediaClient.deleteUserMediaByUserMediaToken(
148
+ this.selectedDeleteMediaItem.media.token,
149
+ );
154
150
 
155
151
  if (deleted) {
156
152
  this.selectedDeleteMediaItem = null;
@@ -165,29 +161,5 @@ export default defineComponent({
165
161
  isSvg(imageUrl: string | undefined): boolean {
166
162
  return imageUrl?.endsWith('.svg') || false;
167
163
  },
168
-
169
- async onMediaStatusUpdatedToDone(data) {
170
- const newMedia = this.userMedia.map((m) => {
171
- if (m.media.token === data.media.token) {
172
- return {
173
- imagePreviewSrc: data.thumbnailUrl,
174
- imageSrc: data.url,
175
- ...data,
176
- };
177
- }
178
-
179
- return m;
180
- });
181
-
182
- this.userMedia = newMedia as UserMediaItemWithDimensions[];
183
- },
184
-
185
- isImageUploaded(image) {
186
- if (image.media && image.media.status) {
187
- return image.media.status === 'done';
188
- }
189
-
190
- return true;
191
- },
192
164
  },
193
165
  });
@@ -25,12 +25,15 @@
25
25
  class="c-media-btn tw-p-0 tw-border-none tw-cursor-pointer"
26
26
  @click="setSelectedMediaItem(image)"
27
27
  >
28
- <!-- new image item here -->
29
- <app-media-selection-item
30
- :image-test-tag="`user-media-${idx}`"
31
- :item="image"
32
- :media-client="userMediaClient"
33
- @on-media-done="onMediaStatusUpdatedToDone"
28
+ <img
29
+ :data-test-tag="`user-media-${idx}`"
30
+ :src="image.thumbnailUrl || image.url"
31
+ alt=""
32
+ class="c-media-btn__img hover:!tw-shadow-[0_0_0_3px] hover:!tw-shadow-info-500"
33
+ :class="{
34
+ 'tw-bg-grayscale-600': isSvg(image.url),
35
+ 'tw-bg-white': !isSvg(image.url),
36
+ }"
34
37
  />
35
38
  </button>
36
39
  <!-- End media image -->
@@ -67,7 +70,7 @@
67
70
  <!-- End media delete confirmation -->
68
71
  <!-- Start media delete button -->
69
72
  <button
70
- v-else-if="isDeleteEnabled && isImageUploaded(image)"
73
+ v-else-if="isDeleteEnabled"
71
74
  class="tw-absolute tw-bottom-15 tw-right-2 tw-bg-white tw-rounded tw-p-1 tw-border-none tw-cursor-pointer"
72
75
  @click="onMediaDeleteButtonClick(image)"
73
76
  >
@@ -90,14 +93,12 @@
90
93
  <script>
91
94
  import { Button, Icon, Loader } from '@designcrowd/fe-shared-lib';
92
95
  import MyMediaMixin from './MyMedia.mixin.ts';
93
- import MyMediaSelectionItem from './MyMediaSelectionItem.vue';
94
96
 
95
97
  export default {
96
98
  components: {
97
99
  Button,
98
100
  Icon,
99
101
  Loader,
100
- appMediaSelectionItem: MyMediaSelectionItem,
101
102
  },
102
103
  mixins: [MyMediaMixin],
103
104
  setup(props, context) {
@@ -191,7 +191,6 @@ export default defineComponent({
191
191
  true,
192
192
  undefined,
193
193
  this.mediaType,
194
- true,
195
194
  );
196
195
  }
197
196
 
@@ -4,7 +4,6 @@ interface UserMediaItemMedia {
4
4
  name: string;
5
5
  mimeType: string;
6
6
  createdOn: string;
7
- status: string;
8
7
  }
9
8
 
10
9
  export interface UserMediaItem {
@@ -1,44 +0,0 @@
1
- import type { PropType } from 'vue';
2
- declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
3
- textStyle: {
4
- type: PropType<BrandPageCssStyle>;
5
- required: false;
6
- default: undefined;
7
- };
8
- text: {
9
- type: StringConstructor;
10
- required: true;
11
- };
12
- element: {
13
- type: StringConstructor;
14
- required: false;
15
- default: () => string;
16
- };
17
- }>, {}, {
18
- isCollapsed: boolean;
19
- }, {
20
- textStyles(): {
21
- backgroundColor: any;
22
- };
23
- style(): any;
24
- padding(): "" | "tw-py-2 tw-px-4";
25
- }, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
26
- textStyle: {
27
- type: PropType<BrandPageCssStyle>;
28
- required: false;
29
- default: undefined;
30
- };
31
- text: {
32
- type: StringConstructor;
33
- required: true;
34
- };
35
- element: {
36
- type: StringConstructor;
37
- required: false;
38
- default: () => string;
39
- };
40
- }>> & Readonly<{}>, {
41
- textStyle: BrandPageCssStyle;
42
- element: string;
43
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
44
- export default _default;
@@ -1,38 +0,0 @@
1
- import { defineComponent } from 'vue';
2
- export default defineComponent({
3
- props: {
4
- textStyle: {
5
- type: Object,
6
- required: false,
7
- default: undefined,
8
- },
9
- text: {
10
- type: String,
11
- required: true,
12
- },
13
- element: {
14
- type: String,
15
- required: false,
16
- default: () => 'p',
17
- },
18
- },
19
- data: () => ({
20
- isCollapsed: false,
21
- }),
22
- computed: {
23
- textStyles() {
24
- return {
25
- backgroundColor: this.textStyle.backgroundColor,
26
- };
27
- },
28
- style() {
29
- const { backgroundColor, ...textStyle } = this.textStyle;
30
- return textStyle;
31
- },
32
- padding() {
33
- return this.textStyle.backgroundColor ? 'tw-py-2 tw-px-4' : '';
34
- },
35
- },
36
- watch: {},
37
- methods: {},
38
- });
@@ -1,41 +0,0 @@
1
- import { defineComponent } from 'vue';
2
- import type { PropType } from 'vue';
3
-
4
- export default defineComponent({
5
- props: {
6
- textStyle: {
7
- type: Object as PropType<BrandPageCssStyle>,
8
- required: false,
9
- default: undefined,
10
- },
11
- text: {
12
- type: String,
13
- required: true,
14
- },
15
- element: {
16
- type: String,
17
- required: false,
18
- default: () => 'p',
19
- },
20
- },
21
- data: () => ({
22
- isCollapsed: false as boolean,
23
- }),
24
- computed: {
25
- textStyles() {
26
- return {
27
- backgroundColor: this.textStyle.backgroundColor,
28
- };
29
- },
30
- style() {
31
- const { backgroundColor, ...textStyle } = this.textStyle;
32
-
33
- return textStyle;
34
- },
35
- padding() {
36
- return this.textStyle.backgroundColor ? 'tw-py-2 tw-px-4' : '';
37
- },
38
- },
39
- watch: {},
40
- methods: {},
41
- });
@@ -1,137 +0,0 @@
1
- <template>
2
- <img
3
- v-if="isDone"
4
- :data-test-tag="props.imageTestTag"
5
- :src="previewSrc"
6
- alt=""
7
- class="c-media-btn__img hover:!tw-shadow-[0_0_0_3px] hover:!tw-shadow-info-500"
8
- :class="{
9
- 'tw-bg-grayscale-600': isSvg(previewSrc),
10
- 'tw-bg-white': !isSvg(previewSrc),
11
- }"
12
- />
13
- <div v-else class="tw-w-full tw-h-full">
14
- <div
15
- v-if="error"
16
- class="tw-flex tw-h-full tw-justify-center tw-items-center"
17
- >
18
- <Icon
19
- name="error"
20
- size="lg"
21
- color="secondary-300"
22
- classes="tw-self-center"
23
- />
24
- </div>
25
- <div v-else class="tw-flex tw-h-full tw-justify-center tw-items-center">
26
- <Loader />
27
- </div>
28
- </div>
29
- </template>
30
-
31
- <script setup lang="ts">
32
- import { ref, onMounted, defineProps, defineEmits } from 'vue';
33
- import { Icon, Loader } from '@designcrowd/fe-shared-lib';
34
- import type { UserMediaClient } from 'src/clients/user-media-client';
35
- import type { UserMediaItemWithDimensions } from './models';
36
-
37
- const MAX_POLL = 5;
38
- const EXTRA_WAIT_TIME_EVERY_10_POLL = 2000;
39
- const POLL_WAIT = 5000;
40
-
41
- const props = defineProps<{
42
- imageTestTag: string;
43
- item: UserMediaItemWithDimensions;
44
- mediaClient: UserMediaClient;
45
- }>();
46
-
47
- const emit = defineEmits<{
48
- (e: string, payload: any): void;
49
- }>();
50
-
51
- const isDone = ref(false);
52
- const previewSrc = ref<string>('');
53
- const error = ref(false);
54
- const currentPoll = ref(0);
55
-
56
- function isSvg(imageUrl: string | null): boolean {
57
- return imageUrl?.endsWith('.svg') || false;
58
- }
59
-
60
- async function checkStatusAsync(): Promise<boolean> {
61
- currentPoll.value += 1;
62
-
63
- const data = await props.mediaClient.getUserMediaByTokenAsync(
64
- props.item.media.token,
65
- );
66
-
67
- if (data) {
68
- const status = data.media.status;
69
-
70
- if (status === 'inprogress') {
71
- return true;
72
- }
73
-
74
- if (status === 'done') {
75
- emit('on-media-done', data);
76
- previewSrc.value = data.thumbnailUrl ?? data.url ?? '';
77
- isDone.value = true;
78
- } else if (status === 'failed') {
79
- error.value = true;
80
- }
81
- }
82
-
83
- return false;
84
- }
85
-
86
- async function handleProgressAsync() {
87
- let repeat = true;
88
-
89
- try {
90
- do {
91
- repeat = await checkStatusAsync();
92
-
93
- if (currentPoll.value >= MAX_POLL) {
94
- error.value = true;
95
- repeat = false;
96
- }
97
-
98
- // For every 10th poll, add extra wait time
99
- const everyTenth = Math.floor(9 / 10);
100
- const timeToWait = POLL_WAIT + everyTenth * EXTRA_WAIT_TIME_EVERY_10_POLL;
101
-
102
- await new Promise((r) => setTimeout(r, timeToWait));
103
- } while (repeat);
104
- } catch {
105
- error.value = true;
106
- }
107
- }
108
-
109
- onMounted(() => {
110
- const status = props.item.media.status;
111
-
112
- if (status === 'inprogress') {
113
- handleProgressAsync();
114
- } else if (status === 'failed') {
115
- error.value = true;
116
- } else {
117
- // If it's already done or has no status, just set up the image
118
- previewSrc.value = props.item.thumbnailUrl ?? props.item.url ?? '';
119
- isDone.value = true;
120
- }
121
- });
122
- </script>
123
-
124
- <style scoped>
125
- .c-media-btn__img {
126
- @apply tw-transition-shadow tw-duration-200 tw-ease-out tw-w-full tw-h-full tw-rounded-md tw-object-cover;
127
- }
128
- .c-media-btn__img:hover {
129
- box-shadow: 0 0 0 3px rgb(0, 151, 215);
130
- }
131
- .tw-w-full {
132
- width: 100%;
133
- }
134
- .tw-h-full {
135
- height: 100%;
136
- }
137
- </style>