@saooti/octopus-sdk 40.1.21 → 40.2.0

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 (43) hide show
  1. package/index.ts +8 -1
  2. package/package.json +1 -1
  3. package/plateform.conf +1 -1
  4. package/src/components/composable/player/usePlayerLive.ts +1 -1
  5. package/src/components/display/accessibility/AccessibilityModal.vue +7 -7
  6. package/src/components/display/comments/CommentSection.vue +1 -1
  7. package/src/components/display/emission/EmissionPlayerItem.vue +1 -1
  8. package/src/components/display/list/SwiperList.vue +1 -1
  9. package/src/components/display/live/LiveList.vue +1 -1
  10. package/src/components/display/live/RadioList.vue +1 -1
  11. package/src/components/display/podcasts/PodcastImage.vue +17 -9
  12. package/src/components/display/podcasts/PodcastItemInfo.vue +1 -1
  13. package/src/components/display/podcasts/PodcastModuleBox.vue +121 -118
  14. package/src/components/display/podcasts/PodcastRawTranscript.vue +5 -18
  15. package/src/components/display/sharing/PlayerAnonymousModal.vue +54 -0
  16. package/src/components/display/sharing/QrCode.vue +36 -35
  17. package/src/components/display/sharing/ShareAnonymous.vue +290 -0
  18. package/src/components/display/sharing/ShareDistribution.vue +8 -27
  19. package/src/components/display/sharing/ShareNewsletter.vue +205 -0
  20. package/src/components/display/sharing/ShareSocialsButtons.vue +110 -0
  21. package/src/components/display/sharing/SubscribeButtons.vue +2 -0
  22. package/src/components/form/ClassicCopyButton.vue +65 -0
  23. package/src/components/misc/ClassicNav.vue +9 -4
  24. package/src/components/misc/ClassicPopover.vue +1 -1
  25. package/src/components/misc/modal/ClipboardModal.vue +9 -11
  26. package/src/components/misc/modal/NewsletterModal.vue +14 -192
  27. package/src/components/misc/modal/QrCodeModal.vue +2 -1
  28. package/src/components/pages/EmissionPage.vue +47 -61
  29. package/src/components/pages/ParticipantPage.vue +17 -7
  30. package/src/components/pages/PlaylistPage.vue +13 -9
  31. package/src/components/pages/PodcastPage.vue +15 -38
  32. package/src/components/pages/RadioPage.vue +12 -7
  33. package/src/components/pages/VideoPage.vue +0 -3
  34. package/src/locale/de.ts +5 -2
  35. package/src/locale/en.ts +5 -2
  36. package/src/locale/es.ts +5 -2
  37. package/src/locale/fr.ts +9 -6
  38. package/src/locale/it.ts +5 -2
  39. package/src/locale/sl.ts +5 -2
  40. package/src/router/router.ts +3 -8
  41. package/src/stores/class/config/commentsConfig.ts +2 -2
  42. package/src/style/bootstrap.scss +6 -14
  43. package/src/components/display/sharing/ShareButtons.vue +0 -351
@@ -1,6 +1,21 @@
1
1
  <template>
2
2
  <div class="d-flex flex-column align-items-center">
3
- <qrcode-vue
3
+ <div class="d-flex align-items-center mb-3">
4
+ <div class="form-label me-3">
5
+ {{ $t("Color of the QR Code") }}
6
+ </div>
7
+ <VSwatches
8
+ v-model:model-value="color"
9
+ class="c-hand"
10
+ show-fallback
11
+ fallback-input-type="color"
12
+ colors="text-advanced"
13
+ popover-to="right"
14
+ popover-y="bottom"
15
+ :data-color="color"
16
+ />
17
+ </div>
18
+ <qrcode-svg
4
19
  :value="url"
5
20
  :size="size"
6
21
  level="H"
@@ -8,14 +23,7 @@
8
23
  class="myQrCode"
9
24
  :margin="2"
10
25
  />
11
- <ClassicCheckbox
12
- v-if="'#000000' !== otherColor"
13
- v-model:text-init="isNotBlack"
14
- class="flex-shrink-0"
15
- id-checkbox="is-black-qr-code"
16
- :label="$t('Use organization color')"
17
- />
18
- <button class="btn m-3" @click="download">
26
+ <button class="btn btn-primary my-3" @click="download">
19
27
  {{ $t("Download") }}
20
28
  </button>
21
29
  <SnackBar ref="snackbar" position="bottom-left" />
@@ -23,63 +31,56 @@
23
31
  </template>
24
32
 
25
33
  <script lang="ts">
26
- import ClassicCheckbox from "../../form/ClassicCheckbox.vue";
34
+ import { VSwatches } from "vue3-swatches";
35
+ import "vue3-swatches/dist/style.css";
27
36
  import SnackBar from "../../misc/SnackBar.vue";
28
- import QrcodeVue from "qrcode.vue";
37
+ import { QrcodeSvg } from "qrcode.vue";
29
38
  import { useSaveFetchStore } from "../../../stores/SaveFetchStore";
30
- import { useAuthStore } from "../../../stores/AuthStore";
31
- import { mapState, mapActions } from "pinia";
39
+ import { mapActions } from "pinia";
32
40
  import { defineComponent } from "vue";
33
41
  export default defineComponent({
34
42
  name: "QrCode",
35
43
 
36
44
  components: {
37
45
  SnackBar,
38
- QrcodeVue,
39
- ClassicCheckbox,
46
+ QrcodeSvg,
47
+ VSwatches,
40
48
  },
41
49
  props: {
42
50
  url: { default: "", type: String },
51
+ orgaForColor: { default: undefined, type: String },
43
52
  },
44
53
  data() {
45
54
  return {
46
55
  size: 200 as number,
47
56
  color: "#000000" as string,
48
- otherColor: "#000000" as string,
49
- isNotBlack: false as boolean,
50
57
  };
51
58
  },
52
- computed: {
53
- ...mapState(useAuthStore, ["authOrganisation", "authOrgaId"]),
54
- },
55
- watch: {
56
- isNotBlack() {
57
- this.color = this.isNotBlack ? this.otherColor : "#000000";
58
- },
59
- },
60
59
  created() {
61
- this.initColor();
60
+ this.initDefaultColor();
62
61
  },
63
62
  methods: {
64
63
  ...mapActions(useSaveFetchStore, ["getOrgaAttributes"]),
65
64
  download(): void {
66
- const link = document.createElement("a");
67
- link.download = "qrcode.png";
68
65
  const canvas = document.getElementsByClassName("myQrCode");
69
66
  if (canvas && canvas.length > 0 && canvas[0]) {
70
- link.href = (canvas[0] as HTMLCanvasElement).toDataURL();
71
- link.click();
67
+ var svgData = canvas[0].outerHTML;
68
+ var svgBlob = new Blob([svgData], {type:"image/svg+xml;charset=utf-8"});
69
+ var svgUrl = URL.createObjectURL(svgBlob);
70
+ var downloadLink = document.createElement("a");
71
+ downloadLink.href = svgUrl;
72
+ downloadLink.download = "qrcode.svg";
73
+ downloadLink.click();
72
74
  (this.$refs.snackbar as InstanceType<typeof SnackBar>).open(
73
75
  this.$t("Download started"),
74
76
  );
75
77
  }
76
78
  },
77
- async initColor(): Promise<void> {
78
- if (undefined === this.authOrgaId) return;
79
- const orgaId = this.authOrgaId;
80
- const attributes = await this.getOrgaAttributes(orgaId ?? "");
79
+ async initDefaultColor(): Promise<void> {
80
+ if (undefined === this.orgaForColor) return;
81
+ const attributes = await this.getOrgaAttributes(this.orgaForColor);
81
82
  if (Object.hasOwn(attributes, "COLOR")) {
82
- this.otherColor = attributes.COLOR as string;
83
+ this.color = attributes.COLOR as string;
83
84
  }
84
85
  },
85
86
  },
@@ -0,0 +1,290 @@
1
+ <template>
2
+ <div v-if="!isLoading && !noSharing">
3
+ <button
4
+ id="anonymous-share-button"
5
+ :class="btnClass"
6
+ :title="$t('Share')"
7
+ >
8
+ <DotsHorizontalIcon />
9
+ </button>
10
+ <ClassicPopover
11
+ target="anonymous-share-button"
12
+ :relative-class="relativeClass"
13
+ :is-fixed="true"
14
+ :left-pos="true"
15
+ :only-click="true"
16
+ >
17
+ <div
18
+ v-for="button in dropdownButtons"
19
+ :key="button.icon"
20
+ class="d-flex flex-column"
21
+ >
22
+ <button
23
+ v-if="button.condition"
24
+ :key="button.title"
25
+ class="btn-transparent d-flex flex-nowrap justify-content-start align-items-center octopus-dropdown-item py-2"
26
+ :title="button.title"
27
+ @mousedown="clickButton(button.emitName)"
28
+ @keydown.enter="clickButton(button.emitName)"
29
+ >
30
+ <component :is="button.icon" />
31
+ <div class="ms-1">{{ button.title }}</div>
32
+ </button>
33
+ </div>
34
+ </ClassicPopover>
35
+ <QrCodeModal
36
+ v-if="isQrCodeModal"
37
+ :url-page="urlPage"
38
+ :orga-for-color="organisationId"
39
+ @close="isQrCodeModal = false"
40
+ />
41
+ <PlayerAnonymousModal
42
+ v-if="isPlayerModal"
43
+ :podcast="podcast"
44
+ :emission="emission ?? podcast?.emission"
45
+ :exclusive="!playerCanBeSharedOthers"
46
+ :not-exclusive="playerCanBeSharedAnonymous"
47
+ @close="isPlayerModal = false"
48
+ />
49
+ <NewsletterModal
50
+ v-if="isNewsletterModal"
51
+ :closable="true"
52
+ :podcast="podcast"
53
+ :emission="emission"
54
+ :playlist="playlist"
55
+ @close="isNewsletterModal = false"
56
+ />
57
+ <ClipboardModal
58
+ v-if="isRssModal"
59
+ :link="rssUrl"
60
+ @close="isRssModal = false"
61
+ />
62
+ <SnackBar
63
+ v-if="lazyLoadingSnackbar"
64
+ ref="snackbar"
65
+ position="bottom-left"
66
+ />
67
+ </div>
68
+ </template>
69
+
70
+ <script lang="ts">
71
+ import QrcodeIcon from "vue-material-design-icons/Qrcode.vue";
72
+ import LinkVariantIcon from "vue-material-design-icons/LinkVariant.vue";
73
+ import DotsHorizontalIcon from "vue-material-design-icons/DotsHorizontal.vue";
74
+ import ClassicPopover from "../../misc/ClassicPopover.vue";
75
+ import displayHelper from "../../../helper/displayHelper";
76
+ import { defineAsyncComponent, defineComponent } from "vue";
77
+ import { useApiStore } from "../../../stores/ApiStore";
78
+ import { useSaveFetchStore } from "../../../stores/SaveFetchStore";
79
+ import { useAuthStore } from "../../../stores/AuthStore";
80
+ import { Podcast } from "@/stores/class/general/podcast";
81
+ import { Emission } from "@/stores/class/general/emission";
82
+ import { Playlist } from "@/stores/class/general/playlist";
83
+ import { mapActions, mapState } from "pinia";
84
+ import { state } from "../../../stores/ParamSdkStore";
85
+ import classicApi from "../../../api/classicApi";
86
+ const SnackBar = defineAsyncComponent(() => import("../../misc/SnackBar.vue"));
87
+ const NewsletterModal = defineAsyncComponent(
88
+ () => import("../../misc/modal/NewsletterModal.vue"),
89
+ );
90
+ const QrCodeModal = defineAsyncComponent(
91
+ () => import("../../misc/modal/QrCodeModal.vue"),
92
+ );
93
+ const ClipboardModal = defineAsyncComponent(
94
+ () => import("../../misc/modal/ClipboardModal.vue"),
95
+ );
96
+ const PlayerAnonymousModal = defineAsyncComponent(
97
+ () => import("../sharing/PlayerAnonymousModal.vue"),
98
+ );
99
+ const CodeTagsIcon = defineAsyncComponent(
100
+ () => import("vue-material-design-icons/CodeTags.vue"),
101
+ );
102
+ const EmailNewsletterIcon = defineAsyncComponent(
103
+ () => import("vue-material-design-icons/EmailNewsletter.vue"),
104
+ );
105
+ const RssIcon = defineAsyncComponent(
106
+ () => import("vue-material-design-icons/Rss.vue"),
107
+ );
108
+ export default defineComponent({
109
+ name: "PodcastShareAnonymous",
110
+ components: {
111
+ ClassicPopover,
112
+ DotsHorizontalIcon,
113
+ LinkVariantIcon,
114
+ EmailNewsletterIcon,
115
+ QrcodeIcon,
116
+ CodeTagsIcon,
117
+ RssIcon,
118
+ SnackBar,
119
+ NewsletterModal,
120
+ QrCodeModal,
121
+ ClipboardModal,
122
+ PlayerAnonymousModal
123
+ },
124
+
125
+ props: {
126
+ podcast: { default: undefined, type: Object as () => Podcast },
127
+ emission: { default: undefined, type: Object as () => Emission },
128
+ playlist: { default: undefined, type: Object as () => Playlist },
129
+ participantId: { default: undefined, type: Number },
130
+ organisationId: { default: undefined, type: String },
131
+ relativeClass: { default: "page-element", type: String },
132
+ btnClass: { default: "btn btn-transparent", type: String },
133
+ },
134
+
135
+ data() {
136
+ return {
137
+ lazyLoadingSnackbar: false as boolean,
138
+ isNewsletterModal: false as boolean,
139
+ isQrCodeModal: false as boolean,
140
+ displayRss: false as boolean,
141
+ noSharing: true as boolean,
142
+ playerCanBeSharedAnonymous: false as boolean,
143
+ playerCanBeSharedOthers: false as boolean,
144
+ isLoading: true as boolean,
145
+ isRssModal: false as boolean,
146
+ isPlayerModal: false as boolean
147
+ };
148
+ },
149
+ computed:{
150
+ ...mapState(useAuthStore, ["isGarRole","authOrgaId"]),
151
+ ...mapState(useApiStore, ["apiUrl"]),
152
+ urlPage(): string {
153
+ return window.location.href;
154
+ },
155
+ isPodcastmaker(): boolean {
156
+ return state.generalParameters.podcastmaker as boolean;
157
+ },
158
+ rssUrl(): string {
159
+ const api = this.apiUrl + "rss/";
160
+ if (
161
+ (!this.isPodcastmaker && this.playlist) ||
162
+ this.podcast ||
163
+ this.emission
164
+ ) {
165
+ return "";
166
+ }
167
+ if (this.participantId) {
168
+ return api + "participant/" + this.participantId + ".rss";
169
+ }
170
+ if (this.playlist) {
171
+ return api + "playlist/" + this.playlist.playlistId + ".rss";
172
+ }
173
+ if (this.organisationId) {
174
+ return api + "productor/" + this.organisationId + ".rss";
175
+ }
176
+ return "";
177
+ },
178
+ titleRssButton(): string {
179
+ if (this.participantId) {
180
+ return this.$t("Subscribe to this participant");
181
+ }
182
+ if (this.emission) {
183
+ return this.$t("Subscribe to this emission");
184
+ }
185
+ return this.$t("Subscribe to this RSS feed");
186
+ },
187
+ dropdownButtons() {
188
+ return [
189
+ {
190
+ title: this.$t("Copy this page URL"),
191
+ icon: "LinkVariantIcon",
192
+ condition:true,
193
+ emitName: "link",
194
+ },
195
+ {
196
+ title: this.$t("Share the player"),
197
+ icon: "CodeTagsIcon",
198
+ condition: !this.isPodcastmaker && (this.playerCanBeSharedAnonymous || (this.playerCanBeSharedOthers && this.authOrgaId)),
199
+ emitName: "player",
200
+ },
201
+ {
202
+ title: this.$t("Share newsletter"),
203
+ icon: "EmailNewsletterIcon",
204
+ condition: this.podcast || this.emission || this.playlist,
205
+ emitName: "newsletter",
206
+ },
207
+ {
208
+ title: this.$t("Share QR Code"),
209
+ icon: "QrcodeIcon",
210
+ condition: true,
211
+ emitName: "qrcode",
212
+ },
213
+ {
214
+ title: this.titleRssButton,
215
+ icon: "RssIcon",
216
+ condition: '' !== this.rssUrl && this.displayRss && !this.isGarRole,
217
+ emitName: "rss",
218
+ },
219
+ ];
220
+ },
221
+ },
222
+ created() {
223
+ this.initShareButtons();
224
+ },
225
+ methods:{
226
+ ...mapActions(useSaveFetchStore, ["getOrgaAttributes"]),
227
+ async initShareButtons() {
228
+ if (undefined !== this.participantId) {
229
+ this.displayRss = await classicApi.fetchData<boolean>({
230
+ api: 0,
231
+ path: `rss/participants/allowed/${this.organisationId}`,
232
+ isNotAuth: true,
233
+ });
234
+ } else {
235
+ this.displayRss = true;
236
+ }
237
+ this.determinePlayerCanBeShared();
238
+ if (!this.organisationId) {
239
+ return;
240
+ }
241
+ const attributes = await this.getOrgaAttributes(this.organisationId);
242
+ this.noSharing = "true" === attributes.noSharing;
243
+ this.isLoading = false;
244
+ },
245
+ determinePlayerCanBeShared() {
246
+ const emissionAnnot = this.podcast?.emission.annotations ?? this.emission?.annotations;
247
+ if (!emissionAnnot) { return }
248
+ if (emissionAnnot.exclusive) {
249
+ this.playerCanBeSharedOthers = "true" !== emissionAnnot.exclusive;
250
+ }
251
+ if (emissionAnnot.notExclusive) {
252
+ this.playerCanBeSharedAnonymous = "true" === emissionAnnot.notExclusive;
253
+ }
254
+ },
255
+ clickButton(name: string) {
256
+ switch (name) {
257
+ case "link":
258
+ displayHelper.onCopyCode(this.urlPage, this.afterCopy);
259
+ break;
260
+ case "newsletter":
261
+ this.isNewsletterModal = true;
262
+ break;
263
+ case "qrcode":
264
+ this.isQrCodeModal = true;
265
+ break;
266
+ case "rss":
267
+ this.isRssModal = true;
268
+ break;
269
+ case "player":
270
+ this.isPlayerModal = true;
271
+ break;
272
+ default:
273
+ break;
274
+ }
275
+ },
276
+ afterCopy(): void {
277
+ if (!this.lazyLoadingSnackbar) {
278
+ this.lazyLoadingSnackbar = true;
279
+ setTimeout(() => {
280
+ this.afterCopy();
281
+ }, 500);
282
+ } else {
283
+ (this.$refs.snackbar as InstanceType<typeof SnackBar>).open(
284
+ this.$t("Link in clipboard"),
285
+ );
286
+ }
287
+ },
288
+ }
289
+ });
290
+ </script>
@@ -8,9 +8,12 @@
8
8
  <div class="text-primary hide-small-screen text-break">
9
9
  {{ rss }}
10
10
  </div>
11
- <button class="btn btn-primary" @click="onCopyCode(rss, afterCopy)">
12
- {{ $t("Copy") }}
13
- </button>
11
+ <ClassicCopyButton
12
+ :text="$t('Copy')"
13
+ :text-after-copy="$t('Copied!')"
14
+ :data-to-copy="rss"
15
+ :snackbar-text="$t('Link in clipboard')"
16
+ />
14
17
  </div>
15
18
  <RssSection v-if="emission" :emission="emission" />
16
19
  <div class="sharing-distribution-container">
@@ -27,15 +30,11 @@
27
30
  />{{ platform.title }}
28
31
  </router-link>
29
32
  </div>
30
- <SnackBar
31
- v-if="lazyLoadingSnackbar"
32
- ref="snackbar"
33
- position="bottom-left"
34
- />
35
33
  </section>
36
34
  </template>
37
35
 
38
36
  <script lang="ts">
37
+ import ClassicCopyButton from "../../form/ClassicCopyButton.vue";
39
38
  import RadiolineIcon from "../../icons/RadiolineIcon.vue";
40
39
  import TuninIcon from "../../icons/TuninIcon.vue";
41
40
  import PodcastAddictIcon from "../../icons/PodcastAddictIcon.vue";
@@ -49,8 +48,6 @@ import YoutubeIcon from "vue-material-design-icons/Youtube.vue";
49
48
  import SpotifyIcon from "vue-material-design-icons/Spotify.vue";
50
49
  import { useApiStore } from "../../../stores/ApiStore";
51
50
  import classicApi from "../../../api/classicApi";
52
- import SnackBar from "../../misc/SnackBar.vue";
53
- import displayHelper from "../../../helper/displayHelper";
54
51
  import { Emission } from "@/stores/class/general/emission";
55
52
 
56
53
  import { defineComponent, defineAsyncComponent } from "vue";
@@ -60,7 +57,6 @@ const RssSection = defineAsyncComponent(
60
57
  );
61
58
  export default defineComponent({
62
59
  components: {
63
- SnackBar,
64
60
  RssSection,
65
61
  SpotifyIcon,
66
62
  YoutubeIcon,
@@ -73,6 +69,7 @@ export default defineComponent({
73
69
  PodcastAddictIcon,
74
70
  TuninIcon,
75
71
  RadiolineIcon,
72
+ ClassicCopyButton
76
73
  },
77
74
  props: {
78
75
  emissionId: { default: undefined, type: Number },
@@ -82,7 +79,6 @@ export default defineComponent({
82
79
  return {
83
80
  emission: undefined as Emission | undefined,
84
81
  rss: "" as string,
85
- lazyLoadingSnackbar: false as boolean,
86
82
  };
87
83
  },
88
84
  computed: {
@@ -159,9 +155,6 @@ export default defineComponent({
159
155
  },
160
156
 
161
157
  methods: {
162
- onCopyCode(link: string, callback: () => void){
163
- displayHelper.onCopyCode(link, callback);
164
- },
165
158
  getUrl(platform: string): string {
166
159
  return `/main/priv/distribution/${platform}/${this.emissionId}`;
167
160
  },
@@ -175,18 +168,6 @@ export default defineComponent({
175
168
  if (!this.$props.emissionId || this.$props.emissionId <= 0) return;
176
169
  this.rss = `${this.apiUrl}rss/emission/${this.emissionId}.rss`;
177
170
  },
178
- afterCopy(): void {
179
- if (!this.lazyLoadingSnackbar) {
180
- this.lazyLoadingSnackbar = true;
181
- setTimeout(() => {
182
- this.afterCopy();
183
- }, 500);
184
- } else {
185
- (this.$refs.snackbar as InstanceType<typeof SnackBar>).open(
186
- this.$t("Link in clipboard"),
187
- );
188
- }
189
- },
190
171
  },
191
172
  });
192
173
  </script>