@designcrowd/library.brand-page 5.6.2 → 5.6.3

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.
@@ -1,5 +1,6 @@
1
1
  import type { PropType } from 'vue';
2
2
  import type { UserMediaItemWithDimensions } from './models';
3
+ import { UserMediaClient } from '../../clients/user-media-client';
3
4
  declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
5
  isDeleteEnabled: {
5
6
  type: BooleanConstructor;
@@ -61,6 +62,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
61
62
  isDeleting: boolean;
62
63
  selectedDeleteMediaItem: UserMediaItemWithDimensions | null;
63
64
  userMedia: UserMediaItemWithDimensions[];
65
+ userMediaClient: UserMediaClient | null;
64
66
  loading: boolean;
65
67
  }, {
66
68
  filteredUserMedia(): UserMediaItemWithDimensions[];
@@ -74,6 +76,8 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
74
76
  onMediaDeleteConfirmClick(image: UserMediaItemWithDimensions): Promise<void>;
75
77
  deleteSelectedDeleteMediaItem(): Promise<void>;
76
78
  isSvg(imageUrl: string | undefined): boolean;
79
+ onMediaStatusUpdatedToDone(data: any): Promise<void>;
80
+ isImageUploaded(image: any): boolean;
77
81
  }, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
78
82
  isDeleteEnabled: {
79
83
  type: BooleanConstructor;
@@ -1,6 +1,6 @@
1
1
  import { UI_EVENTS } from '../../brand-page-maker/constants';
2
2
  import { computed, defineComponent } from 'vue';
3
- import { getInstance as getUserMediaClientInstance } from '../../clients/user-media-client';
3
+ import { getInstance as getUserMediaClientInstance, UserMediaClient, } from '../../clients/user-media-client';
4
4
  import { preloadImage } from '../../brand-page/utils';
5
5
  import { useUiStore } from '../../brand-page-maker/stores';
6
6
  import { MEDIA_TYPES } from '../../brand-page/models/brand-page-context.model';
@@ -33,6 +33,7 @@ export default defineComponent({
33
33
  isDeleting: false,
34
34
  selectedDeleteMediaItem: null,
35
35
  userMedia: [],
36
+ userMediaClient: null,
36
37
  loading: false,
37
38
  };
38
39
  },
@@ -51,6 +52,7 @@ export default defineComponent({
51
52
  },
52
53
  },
53
54
  async mounted() {
55
+ this.userMediaClient = getUserMediaClientInstance(this.endpoints);
54
56
  await this.loadUserMedia();
55
57
  if (window) {
56
58
  window.addEventListener(UI_EVENTS.refreshUserMediaEvent, this.onRefreshMedia);
@@ -64,8 +66,7 @@ export default defineComponent({
64
66
  methods: {
65
67
  async loadUserMedia() {
66
68
  this.loading = true;
67
- const userMediaClient = getUserMediaClientInstance(this.endpoints);
68
- const images = await userMediaClient.getUserMediaAsync(this.supportedFileExtensions, true, this.mediaType);
69
+ const images = await this.userMediaClient.getUserMediaAsync(this.supportedFileExtensions, true, this.mediaType);
69
70
  if (Array.isArray(images)) {
70
71
  await Promise.all(images.map(async (x) => {
71
72
  const image = await preloadImage(x.thumbnailUrl || x.url);
@@ -114,8 +115,7 @@ export default defineComponent({
114
115
  }
115
116
  this.isDeleting = true;
116
117
  try {
117
- const userMediaClient = getUserMediaClientInstance(this.endpoints);
118
- const deleted = await userMediaClient.deleteUserMediaByUserMediaToken(this.selectedDeleteMediaItem.media.token);
118
+ const deleted = await this.userMediaClient.deleteUserMediaByUserMediaToken(this.selectedDeleteMediaItem.media.token);
119
119
  if (deleted) {
120
120
  this.selectedDeleteMediaItem = null;
121
121
  await this.onRefreshMedia();
@@ -128,5 +128,24 @@ export default defineComponent({
128
128
  isSvg(imageUrl) {
129
129
  return imageUrl?.endsWith('.svg') || false;
130
130
  },
131
+ async onMediaStatusUpdatedToDone(data) {
132
+ const newMedia = this.userMedia.map((m) => {
133
+ if (m.media.token === data.media.token) {
134
+ return {
135
+ imagePreviewSrc: data.thumbnailUrl,
136
+ imageSrc: data.url,
137
+ ...data,
138
+ };
139
+ }
140
+ return m;
141
+ });
142
+ this.userMedia = newMedia;
143
+ },
144
+ isImageUploaded(image) {
145
+ if (image.media && image.media.status) {
146
+ return image.media.status === 'done';
147
+ }
148
+ return true;
149
+ },
131
150
  },
132
151
  });
@@ -137,7 +137,7 @@ export default defineComponent({
137
137
  async onDropzoneImageUploaderSuccess(file) {
138
138
  const { mediaToken, name, type } = file;
139
139
  if (this.userMediaClient) {
140
- await this.userMediaClient.insertUserMediaAsync(mediaToken, name, type, true, undefined, this.mediaType);
140
+ await this.userMediaClient.insertUserMediaAsync(mediaToken, name, type, true, undefined, this.mediaType, true);
141
141
  }
142
142
  this.dropzoneImageUploaderRef?.removeAllFiles();
143
143
  // Dispatch the refresh event in the dropzone success event handler
@@ -4,6 +4,7 @@ interface UserMediaItemMedia {
4
4
  name: string;
5
5
  mimeType: string;
6
6
  createdOn: string;
7
+ status: string;
7
8
  }
8
9
  export interface UserMediaItem {
9
10
  url: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@designcrowd/library.brand-page",
3
3
  "description": "A collection of Maker/Designer applications",
4
- "version": "5.6.2",
4
+ "version": "5.6.3",
5
5
  "author": "Design.com Eng",
6
6
  "license": "UNLICENSED",
7
7
  "type": "module",
@@ -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}`,
27
+ `${this.endpoints.userPolicyDataEndpoint}?isPublic=${isPublic}&isUpload=true`,
28
28
  );
29
29
 
30
30
  if (response.status !== 200) {
@@ -56,19 +56,21 @@ export class UserMediaClient {
56
56
  isPublic: boolean,
57
57
  relatedAssetToken: string | undefined,
58
58
  mediaType: string,
59
+ hasTriggeredProcessing: boolean,
59
60
  ): Promise<UserMediaItem | undefined> {
60
61
  try {
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
- );
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
+ });
72
74
 
73
75
  return response.data;
74
76
  } catch (e) {
@@ -118,6 +120,7 @@ export class UserMediaClient {
118
120
  true,
119
121
  relatedAssetToken,
120
122
  file.mediaType,
123
+ false,
121
124
  );
122
125
 
123
126
  return newMedia?.url;
@@ -132,9 +135,7 @@ export class UserMediaClient {
132
135
 
133
136
  file.mediaToken = mediaToken;
134
137
 
135
- const key = `${this.policyData.keyPrefix}${
136
- file.mediaToken
137
- }${getExtensionFromMimeType(file.type)}`;
138
+ const key = `${this.policyData.keyPrefix}${file.mediaToken}`;
138
139
 
139
140
  file.key = key;
140
141
  formData.append('key', key);
@@ -178,6 +179,22 @@ export class UserMediaClient {
178
179
  }
179
180
  }
180
181
 
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
+
181
198
  async deleteUserMediaByUserMediaToken(
182
199
  userMediaToken: string,
183
200
  ): Promise<boolean> {
@@ -2,7 +2,10 @@ 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 { getInstance as getUserMediaClientInstance } from '../../clients/user-media-client';
5
+ import {
6
+ getInstance as getUserMediaClientInstance,
7
+ UserMediaClient,
8
+ } from '../../clients/user-media-client';
6
9
  import { preloadImage } from '../../brand-page/utils';
7
10
  import { useUiStore } from '../../brand-page-maker/stores';
8
11
  import { MEDIA_TYPES } from '../../brand-page/models/brand-page-context.model';
@@ -37,6 +40,7 @@ export default defineComponent({
37
40
  isDeleting: false as boolean,
38
41
  selectedDeleteMediaItem: null as UserMediaItemWithDimensions | null,
39
42
  userMedia: [] as UserMediaItemWithDimensions[],
43
+ userMediaClient: null as UserMediaClient | null,
40
44
  loading: false,
41
45
  };
42
46
  },
@@ -56,6 +60,8 @@ export default defineComponent({
56
60
  },
57
61
  },
58
62
  async mounted() {
63
+ this.userMediaClient = getUserMediaClientInstance(this.endpoints);
64
+
59
65
  await this.loadUserMedia();
60
66
 
61
67
  if (window) {
@@ -76,8 +82,7 @@ export default defineComponent({
76
82
  methods: {
77
83
  async loadUserMedia() {
78
84
  this.loading = true;
79
- const userMediaClient = getUserMediaClientInstance(this.endpoints);
80
- const images = await userMediaClient.getUserMediaAsync(
85
+ const images = await this.userMediaClient.getUserMediaAsync(
81
86
  this.supportedFileExtensions,
82
87
  true,
83
88
  this.mediaType,
@@ -142,11 +147,10 @@ export default defineComponent({
142
147
  this.isDeleting = true;
143
148
 
144
149
  try {
145
- const userMediaClient = getUserMediaClientInstance(this.endpoints);
146
-
147
- const deleted = await userMediaClient.deleteUserMediaByUserMediaToken(
148
- this.selectedDeleteMediaItem.media.token,
149
- );
150
+ const deleted =
151
+ await this.userMediaClient.deleteUserMediaByUserMediaToken(
152
+ this.selectedDeleteMediaItem.media.token,
153
+ );
150
154
 
151
155
  if (deleted) {
152
156
  this.selectedDeleteMediaItem = null;
@@ -161,5 +165,29 @@ export default defineComponent({
161
165
  isSvg(imageUrl: string | undefined): boolean {
162
166
  return imageUrl?.endsWith('.svg') || false;
163
167
  },
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
+ },
164
192
  },
165
193
  });
@@ -25,15 +25,12 @@
25
25
  class="c-media-btn tw-p-0 tw-border-none tw-cursor-pointer"
26
26
  @click="setSelectedMediaItem(image)"
27
27
  >
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
- }"
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"
37
34
  />
38
35
  </button>
39
36
  <!-- End media image -->
@@ -70,7 +67,7 @@
70
67
  <!-- End media delete confirmation -->
71
68
  <!-- Start media delete button -->
72
69
  <button
73
- v-else-if="isDeleteEnabled"
70
+ v-else-if="isDeleteEnabled && isImageUploaded(image)"
74
71
  class="tw-absolute tw-bottom-15 tw-right-2 tw-bg-white tw-rounded tw-p-1 tw-border-none tw-cursor-pointer"
75
72
  @click="onMediaDeleteButtonClick(image)"
76
73
  >
@@ -93,12 +90,14 @@
93
90
  <script>
94
91
  import { Button, Icon, Loader } from '@designcrowd/fe-shared-lib';
95
92
  import MyMediaMixin from './MyMedia.mixin.ts';
93
+ import MyMediaSelectionItem from './MyMediaSelectionItem.vue';
96
94
 
97
95
  export default {
98
96
  components: {
99
97
  Button,
100
98
  Icon,
101
99
  Loader,
100
+ appMediaSelectionItem: MyMediaSelectionItem,
102
101
  },
103
102
  mixins: [MyMediaMixin],
104
103
  setup(props, context) {
@@ -0,0 +1,137 @@
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>
@@ -191,6 +191,7 @@ export default defineComponent({
191
191
  true,
192
192
  undefined,
193
193
  this.mediaType,
194
+ true,
194
195
  );
195
196
  }
196
197
 
@@ -4,6 +4,7 @@ interface UserMediaItemMedia {
4
4
  name: string;
5
5
  mimeType: string;
6
6
  createdOn: string;
7
+ status: string;
7
8
  }
8
9
 
9
10
  export interface UserMediaItem {