@saooti/octopus-sdk 38.0.31 → 38.0.33
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.
- package/package.json +1 -1
- package/src/assets/general.scss +1 -0
- package/src/assets/progressbar.scss +13 -0
- package/src/components/display/sharing/ShareButtons.vue +229 -31
- package/src/components/misc/ClassicPopover.vue +9 -3
- package/src/components/misc/HomeDropdown.vue +12 -2
- package/src/components/misc/ProgressBar.vue +54 -4
- package/src/components/misc/player/ChapteringModal.vue +95 -0
- package/src/components/misc/player/PlayerChaptering.vue +86 -0
- package/src/components/misc/player/PlayerCompact.vue +3 -0
- package/src/components/misc/player/PlayerComponent.vue +19 -1
- package/src/components/misc/player/PlayerLarge.vue +3 -0
- package/src/components/mixins/player/playerLogicProgress.ts +1 -1
- package/src/components/mixins/player/playerTranscript.ts +39 -8
- package/src/components/pages/Error403Page.vue +21 -1
- package/src/components/pages/RadioPage.vue +2 -1
- package/src/helper/duration.ts +6 -8
- package/src/locale/de.ts +3 -3
- package/src/locale/en.ts +3 -3
- package/src/locale/es.ts +3 -3
- package/src/locale/fr.ts +3 -3
- package/src/locale/it.ts +3 -3
- package/src/locale/sl.ts +3 -3
- package/src/stores/ParamSdkStore.ts +11 -11
- package/src/stores/PlayerStore.ts +52 -7
- package/src/stores/class/chaptering/chaptering.ts +23 -0
- package/src/components/display/sharing/ShareButtonsIntern.vue +0 -273
package/package.json
CHANGED
package/src/assets/general.scss
CHANGED
|
@@ -20,6 +20,19 @@
|
|
|
20
20
|
background-color: $octopus-primary-color;
|
|
21
21
|
transition: width 0.6s ease;
|
|
22
22
|
}
|
|
23
|
+
.octopus-chapter{
|
|
24
|
+
position: absolute;
|
|
25
|
+
background: transparent;
|
|
26
|
+
background-clip: content-box;
|
|
27
|
+
padding: 0 5px;
|
|
28
|
+
box-shadow: inset -2px 1px 0px 0px rgb(0 0 0),
|
|
29
|
+
inset 2px 1px 0px 0px rgb(0 0 0);
|
|
30
|
+
|
|
31
|
+
&:hover{
|
|
32
|
+
background: rgba(255, 255, 255, 0.4);
|
|
33
|
+
box-shadow: -4px 1px 0px 0px rgb(0 0 0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
23
36
|
&,.octopus-progress-bar{
|
|
24
37
|
height: 4px;
|
|
25
38
|
@media (max-width: 960px) {
|
|
@@ -1,51 +1,151 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div v-if="!isLoading && (authenticated || !noSharing)" class="module-box">
|
|
3
|
-
<div class="d-flex align-items-center
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
<div class="d-flex align-items-center justify-content-between">
|
|
4
|
+
<div v-if="!isGarStudent && !noSharing" class="d-flex flex-column me-2">
|
|
5
|
+
<div class="h4 mb-2">
|
|
6
|
+
{{ $t("Share in one click") }}
|
|
7
|
+
</div>
|
|
8
|
+
<div class="d-flex align-items-center">
|
|
9
|
+
<template v-for="button in arrayShareButtons" :key="button.title">
|
|
10
|
+
<a
|
|
11
|
+
v-if="button.condition"
|
|
12
|
+
rel="noopener"
|
|
13
|
+
target="_blank"
|
|
14
|
+
:href="button.url"
|
|
15
|
+
:class="getClass(button.className)"
|
|
16
|
+
class="me-2"
|
|
17
|
+
:title="button.title"
|
|
18
|
+
>
|
|
19
|
+
<div :class="button.icon" />
|
|
20
|
+
</a>
|
|
21
|
+
</template>
|
|
22
|
+
<button
|
|
23
|
+
:class="getClass()"
|
|
24
|
+
class="saooti-link"
|
|
25
|
+
:title="$t('Copy this page URL')"
|
|
26
|
+
@click="onCopyCode(urlPage, afterCopy)"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div v-if="podcast || emission || playlist" class="d-flex flex-column me-2">
|
|
31
|
+
<div class="h4 mb-2">
|
|
32
|
+
{{ $t("Newsletter") }}
|
|
33
|
+
</div>
|
|
34
|
+
<div class="d-flex align-items-center justify-content-center">
|
|
35
|
+
<button
|
|
36
|
+
:class="getClass()"
|
|
37
|
+
class="saooti-newsletter"
|
|
38
|
+
:title="$t('Share newsletter')"
|
|
39
|
+
@click="newsletter = true"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="d-flex flex-column me-2">
|
|
44
|
+
<div class="h4 mb-2">
|
|
45
|
+
{{ $t("QR Code") }}
|
|
46
|
+
</div>
|
|
47
|
+
<div class="d-flex align-items-center justify-content-center">
|
|
48
|
+
<button
|
|
49
|
+
:class="getClass()"
|
|
50
|
+
:title="$t('Share QR Code')"
|
|
51
|
+
class="saooti-qrcode"
|
|
52
|
+
@click="qrCode = true"
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div v-if="'' !== rssUrl && displayRss" class="d-flex flex-column me-2">
|
|
57
|
+
<div class="h4 mb-2">
|
|
58
|
+
{{ $t("Rss feed") }}
|
|
59
|
+
</div>
|
|
60
|
+
<div class="d-flex align-items-center justify-content-center">
|
|
61
|
+
<a
|
|
62
|
+
rel="noopener"
|
|
63
|
+
target="_blank"
|
|
64
|
+
class="saooti-rss"
|
|
65
|
+
:class="getClass()"
|
|
66
|
+
:href="rssUrl"
|
|
67
|
+
:title="titleRssButton"
|
|
68
|
+
@click.prevent="openPopup()"
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div v-if="!isPodcastmaker && authenticated && podcast && isProduction" class="d-flex flex-column me-2">
|
|
73
|
+
<div class="h4 mb-2">
|
|
74
|
+
{{ $t("Generate a social media post (with AI)") }}
|
|
75
|
+
</div>
|
|
76
|
+
<div class="d-flex align-items-center justify-content-center">
|
|
77
|
+
<router-link
|
|
78
|
+
:class="getClass()"
|
|
79
|
+
:title="$t('Generate a social media post (with AI)')"
|
|
80
|
+
:to="{
|
|
81
|
+
name: 'advancedShare',
|
|
82
|
+
params: { podcastId: podcast.podcastId },
|
|
83
|
+
}"
|
|
84
|
+
>
|
|
85
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-stars" viewBox="0 0 16 16">
|
|
86
|
+
<path d="M7.657 6.247c.11-.33.576-.33.686 0l.645 1.937a2.89 2.89 0 0 0 1.829 1.828l1.936.645c.33.11.33.576 0 .686l-1.937.645a2.89 2.89 0 0 0-1.828 1.829l-.645 1.936a.361.361 0 0 1-.686 0l-.645-1.937a2.89 2.89 0 0 0-1.828-1.828l-1.937-.645a.361.361 0 0 1 0-.686l1.937-.645a2.89 2.89 0 0 0 1.828-1.828l.645-1.937zM3.794 1.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387A1.734 1.734 0 0 0 4.593 5.69l-.387 1.162a.217.217 0 0 1-.412 0L3.407 5.69A1.734 1.734 0 0 0 2.31 4.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387A1.734 1.734 0 0 0 3.407 2.31l.387-1.162zM10.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732L9.1 2.137a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L10.863.1z"/>
|
|
87
|
+
</svg>
|
|
88
|
+
</router-link>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<ClipboardModal
|
|
93
|
+
v-if="dataRSSSave"
|
|
94
|
+
:link="rssUrl"
|
|
95
|
+
:emission="emission"
|
|
96
|
+
@close="dataRSSSave = false"
|
|
97
|
+
@copy="afterCopy"
|
|
98
|
+
/>
|
|
99
|
+
<NewsletterModal
|
|
100
|
+
v-if="newsletter"
|
|
101
|
+
:closable="true"
|
|
102
|
+
:podcast="podcast"
|
|
103
|
+
:emission="emission"
|
|
104
|
+
:playlist="playlist"
|
|
105
|
+
@close="newsletter = false"
|
|
14
106
|
/>
|
|
15
|
-
<
|
|
16
|
-
v-if="
|
|
17
|
-
|
|
18
|
-
:
|
|
19
|
-
|
|
20
|
-
|
|
107
|
+
<QrCodeModal
|
|
108
|
+
v-if="qrCode"
|
|
109
|
+
:closable="true"
|
|
110
|
+
:url-page="urlPage"
|
|
111
|
+
@close="qrCode = false"
|
|
112
|
+
/>
|
|
113
|
+
<SnackBar
|
|
114
|
+
v-if="lazyLoadingSnackbar"
|
|
115
|
+
ref="snackbar"
|
|
116
|
+
position="bottom-left"
|
|
21
117
|
/>
|
|
22
118
|
</div>
|
|
23
|
-
<ShareButtonsIntern
|
|
24
|
-
:no-sharing="noSharing"
|
|
25
|
-
:podcast="podcast"
|
|
26
|
-
:emission="emission"
|
|
27
|
-
:playlist="playlist"
|
|
28
|
-
:participant-id="participantId"
|
|
29
|
-
:organisation-id="organisationId"
|
|
30
|
-
/>
|
|
31
119
|
</div>
|
|
32
120
|
</template>
|
|
33
121
|
|
|
34
122
|
<script lang="ts">
|
|
35
123
|
import { useSaveFetchStore } from "@/stores/SaveFetchStore";
|
|
36
|
-
import { mapActions } from "pinia";
|
|
124
|
+
import { mapActions, mapState } from "pinia";
|
|
37
125
|
import { Emission } from "@/stores/class/general/emission";
|
|
38
126
|
import { Podcast } from "@/stores/class/general/podcast";
|
|
39
127
|
import { state } from "../../../stores/ParamSdkStore";
|
|
128
|
+
import octopusApi from "@saooti/octopus-api";
|
|
40
129
|
import displayMethods from "../../mixins/displayMethods";
|
|
41
|
-
import
|
|
42
|
-
import ShareButtonsIntern from "./ShareButtonsIntern.vue";
|
|
43
|
-
import { defineComponent } from "vue";
|
|
130
|
+
import { defineAsyncComponent, defineComponent } from "vue";
|
|
44
131
|
import { Playlist } from "@/stores/class/general/playlist";
|
|
132
|
+
import { useAuthStore } from "@/stores/AuthStore";
|
|
133
|
+
const ClipboardModal = defineAsyncComponent(
|
|
134
|
+
() => import("../../misc/modal/ClipboardModal.vue"),
|
|
135
|
+
);
|
|
136
|
+
const NewsletterModal = defineAsyncComponent(
|
|
137
|
+
() => import("../../misc/modal/NewsletterModal.vue"),
|
|
138
|
+
);
|
|
139
|
+
const QrCodeModal = defineAsyncComponent(
|
|
140
|
+
() => import("../../misc/modal/QrCodeModal.vue"),
|
|
141
|
+
);
|
|
142
|
+
const SnackBar = defineAsyncComponent(() => import("../../misc/SnackBar.vue"));
|
|
45
143
|
export default defineComponent({
|
|
46
144
|
components: {
|
|
47
|
-
|
|
48
|
-
|
|
145
|
+
ClipboardModal,
|
|
146
|
+
NewsletterModal,
|
|
147
|
+
QrCodeModal,
|
|
148
|
+
SnackBar,
|
|
49
149
|
},
|
|
50
150
|
mixins: [displayMethods],
|
|
51
151
|
props: {
|
|
@@ -59,14 +159,94 @@ export default defineComponent({
|
|
|
59
159
|
return {
|
|
60
160
|
noSharing: true as boolean,
|
|
61
161
|
isLoading: true as boolean,
|
|
162
|
+
dataRSSSave: false as boolean,
|
|
163
|
+
newsletter: false as boolean,
|
|
164
|
+
qrCode: false as boolean,
|
|
165
|
+
displayRss: false as boolean,
|
|
166
|
+
lazyLoadingSnackbar: false as boolean,
|
|
62
167
|
};
|
|
63
168
|
},
|
|
64
169
|
computed: {
|
|
170
|
+
...mapState(useAuthStore, ["isGarStudent"]),
|
|
65
171
|
authenticated(): boolean {
|
|
66
172
|
return state.generalParameters.authenticated as boolean;
|
|
67
173
|
},
|
|
174
|
+
titleRssButton(): string {
|
|
175
|
+
if (this.participantId) {
|
|
176
|
+
return this.$t("Subscribe to this participant");
|
|
177
|
+
}
|
|
178
|
+
if (this.emission) {
|
|
179
|
+
return this.$t("Subscribe to this emission");
|
|
180
|
+
}
|
|
181
|
+
return this.$t("Subscribe to this RSS feed");
|
|
182
|
+
},
|
|
183
|
+
arrayShareButtons() {
|
|
184
|
+
return [
|
|
185
|
+
{
|
|
186
|
+
title: "Facebook",
|
|
187
|
+
icon: "saooti-facebook",
|
|
188
|
+
className: "btn-facebook",
|
|
189
|
+
url: `https://www.facebook.com/sharer/sharer.php?u=${this.urlPage}`,
|
|
190
|
+
condition: true,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
title: "X",
|
|
194
|
+
icon: "saooti-twitter",
|
|
195
|
+
className: "btn-twitter",
|
|
196
|
+
url: `https://twitter.com/intent/tweet?text=${this.urlPage}`,
|
|
197
|
+
condition: true,
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
title: "Linkedin",
|
|
201
|
+
icon: "saooti-linkedin",
|
|
202
|
+
className: "btn-linkedin",
|
|
203
|
+
url: `https://www.linkedin.com/sharing/share-offsite/?url=${this.urlPage}`,
|
|
204
|
+
condition: true,
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
title: "Whatsapp",
|
|
208
|
+
icon: "saooti-Whatsapp",
|
|
209
|
+
className: "btn-whatsapp",
|
|
210
|
+
url: `whatsapp://send?text=${this.urlPage}`,
|
|
211
|
+
condition: window.matchMedia("(hover: none)").matches,
|
|
212
|
+
},
|
|
213
|
+
];
|
|
214
|
+
},
|
|
215
|
+
urlPage(): string {
|
|
216
|
+
return window.location.href;
|
|
217
|
+
},
|
|
218
|
+
isProduction(): boolean {
|
|
219
|
+
return state.generalParameters.isProduction as boolean;
|
|
220
|
+
},
|
|
221
|
+
isPodcastmaker(): boolean {
|
|
222
|
+
return state.generalParameters.podcastmaker as boolean;
|
|
223
|
+
},
|
|
224
|
+
rssUrl(): string {
|
|
225
|
+
let api = state.generalParameters.ApiUri + "rss/";
|
|
226
|
+
if (this.emission) {
|
|
227
|
+
return api + "emission/" + this.emission.emissionId + ".rss";
|
|
228
|
+
}
|
|
229
|
+
if (this.participantId) {
|
|
230
|
+
return api + "participant/" + this.participantId + ".rss";
|
|
231
|
+
}
|
|
232
|
+
if (this.playlist) {
|
|
233
|
+
return api + "playlist/" + this.playlist.playlistId + ".rss";
|
|
234
|
+
}
|
|
235
|
+
if (this.organisationId) {
|
|
236
|
+
return api + "productor/" + this.organisationId + ".rss";
|
|
237
|
+
}
|
|
238
|
+
return "";
|
|
239
|
+
},
|
|
68
240
|
},
|
|
69
241
|
async created() {
|
|
242
|
+
if (undefined !== this.participantId) {
|
|
243
|
+
this.displayRss = await octopusApi.fetchDataPublic<boolean>(
|
|
244
|
+
0,
|
|
245
|
+
`rss/participants/allowed/${this.organisationId}`,
|
|
246
|
+
);
|
|
247
|
+
} else {
|
|
248
|
+
this.displayRss = true;
|
|
249
|
+
}
|
|
70
250
|
if (!this.organisationId) {
|
|
71
251
|
return;
|
|
72
252
|
}
|
|
@@ -76,6 +256,24 @@ export default defineComponent({
|
|
|
76
256
|
},
|
|
77
257
|
methods: {
|
|
78
258
|
...mapActions(useSaveFetchStore, ["getOrgaAttributes"]),
|
|
259
|
+
getClass(className = "btn-rss"): string {
|
|
260
|
+
return `btn ${className} share-btn mb-2 text-dark`;
|
|
261
|
+
},
|
|
262
|
+
openPopup(): void {
|
|
263
|
+
this.dataRSSSave = !this.dataRSSSave;
|
|
264
|
+
},
|
|
265
|
+
afterCopy(): void {
|
|
266
|
+
if (!this.lazyLoadingSnackbar) {
|
|
267
|
+
this.lazyLoadingSnackbar = true;
|
|
268
|
+
setTimeout(() => {
|
|
269
|
+
this.afterCopy();
|
|
270
|
+
}, 500);
|
|
271
|
+
} else {
|
|
272
|
+
(this.$refs.snackbar as InstanceType<typeof SnackBar>).open(
|
|
273
|
+
this.$t("Link in clipboard"),
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
},
|
|
79
277
|
},
|
|
80
278
|
});
|
|
81
279
|
</script>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
ref="popover"
|
|
6
6
|
tabindex="0"
|
|
7
7
|
class="octopus-popover"
|
|
8
|
-
:class="onlyClick ? 'octopus-dropdown' : ''"
|
|
8
|
+
:class="[onlyClick ? 'octopus-dropdown' : '', popoverClass]"
|
|
9
9
|
:style="positionInlineStyle"
|
|
10
10
|
@blur="clearDataBlur"
|
|
11
11
|
@mouseenter="overPopover=true"
|
|
@@ -30,10 +30,12 @@ export default defineComponent({
|
|
|
30
30
|
target: { type: String, required: true },
|
|
31
31
|
disable: { type: Boolean, default: false },
|
|
32
32
|
onlyClick: { type: Boolean, default: false },
|
|
33
|
+
onlyMouse: { type: Boolean, default: false },
|
|
33
34
|
isFixed: { type: Boolean, default: false },
|
|
34
35
|
relativeClass: { type: String, default: undefined },
|
|
35
36
|
leftPos: { type: Boolean, default: false },
|
|
36
37
|
topPos: { type: Boolean, default: false },
|
|
38
|
+
popoverClass: { type: String, default: undefined },
|
|
37
39
|
},
|
|
38
40
|
data() {
|
|
39
41
|
return {
|
|
@@ -70,7 +72,9 @@ export default defineComponent({
|
|
|
70
72
|
);
|
|
71
73
|
this.targetElement.addEventListener("mouseleave", this.clearDataTimeout);
|
|
72
74
|
}
|
|
73
|
-
|
|
75
|
+
if (!this.onlyMouse) {
|
|
76
|
+
this.targetElement.addEventListener("click", this.setPopoverData);
|
|
77
|
+
}
|
|
74
78
|
this.targetElement.addEventListener("blur", this.clearDataBlur);
|
|
75
79
|
}
|
|
76
80
|
},
|
|
@@ -83,7 +87,9 @@ export default defineComponent({
|
|
|
83
87
|
);
|
|
84
88
|
this.targetElement.removeEventListener("mouseleave", this.clearDataTimeout);
|
|
85
89
|
}
|
|
86
|
-
|
|
90
|
+
if (!this.onlyMouse) {
|
|
91
|
+
this.targetElement.removeEventListener("click", this.setPopoverData);
|
|
92
|
+
}
|
|
87
93
|
this.targetElement.addEventListener("blur", this.clearDataBlur);
|
|
88
94
|
}
|
|
89
95
|
},
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
</template>
|
|
61
61
|
</template>
|
|
62
62
|
<hr />
|
|
63
|
-
<a class="octopus-dropdown-item"
|
|
63
|
+
<a class="octopus-dropdown-item c-hand" @click="logoutFunction" >
|
|
64
64
|
{{ $t("Logout") }}
|
|
65
65
|
</a>
|
|
66
66
|
</template>
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
</template>
|
|
73
73
|
|
|
74
74
|
<script lang="ts">
|
|
75
|
+
import crudApi from "@/api/classicCrud";
|
|
75
76
|
import { state } from "../../stores/ParamSdkStore";
|
|
76
77
|
import ClassicPopover from "../misc/ClassicPopover.vue";
|
|
77
78
|
import { useAuthStore } from "@/stores/AuthStore";
|
|
@@ -148,8 +149,17 @@ export default defineComponent({
|
|
|
148
149
|
},
|
|
149
150
|
},
|
|
150
151
|
methods:{
|
|
152
|
+
async logoutFunction(){
|
|
153
|
+
try {
|
|
154
|
+
await crudApi.postData(4, '/logout', undefined);
|
|
155
|
+
await this.$router.push({ path: '/' });
|
|
156
|
+
location.reload();
|
|
157
|
+
} catch (error) {
|
|
158
|
+
//Do nothing
|
|
159
|
+
}
|
|
160
|
+
},
|
|
151
161
|
goToAdministration(){
|
|
152
|
-
if("
|
|
162
|
+
if("backoffice" !== this.$route.name){
|
|
153
163
|
this.$router.push("/main/priv/backoffice");
|
|
154
164
|
}else if (window.history.length > 1) {
|
|
155
165
|
this.$router.go(-1);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="octopus-progress">
|
|
2
|
+
<div v-if="display" id="test-menu-dropdown" class="octopus-progress">
|
|
3
3
|
<div
|
|
4
4
|
v-if="secondaryProgress"
|
|
5
5
|
class="octopus-progress-bar bg-light"
|
|
@@ -48,18 +48,50 @@
|
|
|
48
48
|
/>
|
|
49
49
|
<div
|
|
50
50
|
v-if="isProgressCursor"
|
|
51
|
-
class="progress-bar-cursor"
|
|
51
|
+
class="octopus-progress-bar-cursor"
|
|
52
52
|
:style="'left:' + mainProgress + '%'"
|
|
53
53
|
/>
|
|
54
|
+
<template v-if="playerChapteringPercent">
|
|
55
|
+
<template v-for="chapter in playerChapteringPercent" :key="chapter">
|
|
56
|
+
<div
|
|
57
|
+
:id="'chapter-' + chapter.startPercent"
|
|
58
|
+
class="octopus-progress-bar octopus-chapter"
|
|
59
|
+
role="progressbar"
|
|
60
|
+
aria-valuenow="0"
|
|
61
|
+
aria-valuemin="0"
|
|
62
|
+
aria-valuemax="100"
|
|
63
|
+
:style="{
|
|
64
|
+
left: chapter.startPercent + '%',
|
|
65
|
+
right: 100 - chapter.endPercent + '%',
|
|
66
|
+
}"
|
|
67
|
+
/>
|
|
68
|
+
<Teleport to="#octopus-player-component">
|
|
69
|
+
<ClassicPopover
|
|
70
|
+
:target="'chapter-' + chapter.startPercent"
|
|
71
|
+
:is-fixed="true"
|
|
72
|
+
relative-class="player-container"
|
|
73
|
+
:only-mouse="true"
|
|
74
|
+
popover-class="octopus-small-popover"
|
|
75
|
+
:content="chapter.title"
|
|
76
|
+
/>
|
|
77
|
+
</Teleport>
|
|
78
|
+
</template>
|
|
79
|
+
</template>
|
|
54
80
|
</div>
|
|
55
81
|
</template>
|
|
56
82
|
|
|
57
83
|
<script lang="ts">
|
|
58
84
|
import { usePlayerStore } from "@/stores/PlayerStore";
|
|
59
85
|
import { mapState } from "pinia";
|
|
60
|
-
import { defineComponent } from "vue";
|
|
86
|
+
import { defineAsyncComponent, defineComponent } from "vue";
|
|
87
|
+
const ClassicPopover = defineAsyncComponent(
|
|
88
|
+
() => import("../misc/ClassicPopover.vue"),
|
|
89
|
+
);
|
|
61
90
|
export default defineComponent({
|
|
62
91
|
name: "ProgressBar",
|
|
92
|
+
components: {
|
|
93
|
+
ClassicPopover,
|
|
94
|
+
},
|
|
63
95
|
props: {
|
|
64
96
|
alertBar: { default: undefined, type: Number },
|
|
65
97
|
mainProgress: { default: 0, type: Number },
|
|
@@ -73,7 +105,14 @@ export default defineComponent({
|
|
|
73
105
|
};
|
|
74
106
|
},
|
|
75
107
|
computed: {
|
|
76
|
-
...mapState(usePlayerStore, [
|
|
108
|
+
...mapState(usePlayerStore, [
|
|
109
|
+
"playerMedia",
|
|
110
|
+
"playerChapteringPercent",
|
|
111
|
+
"playerStatus",
|
|
112
|
+
]),
|
|
113
|
+
display() {
|
|
114
|
+
return "STOPPED" !== this.playerStatus;
|
|
115
|
+
},
|
|
77
116
|
},
|
|
78
117
|
watch: {
|
|
79
118
|
playerMedia: {
|
|
@@ -104,4 +143,15 @@ export default defineComponent({
|
|
|
104
143
|
|
|
105
144
|
<style lang="scss">
|
|
106
145
|
@import "../../assets/progressbar.scss";
|
|
146
|
+
.octopus-app .player-container {
|
|
147
|
+
.octopus-small-popover {
|
|
148
|
+
font-size: 0.7rem;
|
|
149
|
+
background: #282828;
|
|
150
|
+
color: white;
|
|
151
|
+
border: 0;
|
|
152
|
+
.p-2 {
|
|
153
|
+
padding: 0.2rem !important;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
107
157
|
</style>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ClassicModal
|
|
3
|
+
id-modal="chaptering-modal"
|
|
4
|
+
:title-modal="$t('Chaptering')"
|
|
5
|
+
@close="closePopup"
|
|
6
|
+
>
|
|
7
|
+
<template #body>
|
|
8
|
+
<div class="d-flex flex-column">
|
|
9
|
+
<button
|
|
10
|
+
v-for="(chapter, index) in playerChapteringPercent"
|
|
11
|
+
:key="chapter"
|
|
12
|
+
class="btn d-flex flex-nowrap align-items-center p-2 mt-1 c-hand text-truncate mb-1"
|
|
13
|
+
:class="actualChapter === index ? 'chapter-selected':'border'"
|
|
14
|
+
@click="goToChapter(index)"
|
|
15
|
+
>
|
|
16
|
+
<div class="d-flex align-items-center me-auto">
|
|
17
|
+
<svg v-if="actualChapter === index" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-soundwave" viewBox="0 0 16 16">
|
|
18
|
+
<path fill-rule="evenodd" d="M8.5 2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-1 0v-11a.5.5 0 0 1 .5-.5m-2 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m4 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m-6 1.5A.5.5 0 0 1 5 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m8 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m-10 1A.5.5 0 0 1 3 7v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5m12 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5"/>
|
|
19
|
+
</svg>
|
|
20
|
+
<div v-else>{{ index + 1 }}</div>
|
|
21
|
+
<div class="ms-2">{{ "- " + chapter.title }}</div>
|
|
22
|
+
</div>
|
|
23
|
+
<div>{{ chapter.startDisplay }}</div>
|
|
24
|
+
</button>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
<template #footer>
|
|
28
|
+
<button class="btn m-1" @click="closePopup">
|
|
29
|
+
{{ $t("Close") }}
|
|
30
|
+
</button>
|
|
31
|
+
</template>
|
|
32
|
+
</ClassicModal>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import { usePlayerStore } from "@/stores/PlayerStore";
|
|
37
|
+
import { mapState, mapActions } from "pinia";
|
|
38
|
+
import ClassicModal from "../modal/ClassicModal.vue";
|
|
39
|
+
import { defineComponent } from "vue";
|
|
40
|
+
export default defineComponent({
|
|
41
|
+
name: "ChapteringModal",
|
|
42
|
+
components: {
|
|
43
|
+
ClassicModal,
|
|
44
|
+
},
|
|
45
|
+
props: {actualChapter: {default: -1, type: Number}},
|
|
46
|
+
emits: ["close"],
|
|
47
|
+
data() {
|
|
48
|
+
return {
|
|
49
|
+
audioPlayer: null as HTMLAudioElement | null,
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
computed: {
|
|
53
|
+
...mapState(usePlayerStore, [
|
|
54
|
+
"playerPodcast",
|
|
55
|
+
"playerLive",
|
|
56
|
+
"playerChapteringPercent",
|
|
57
|
+
"playerTotal",
|
|
58
|
+
"playerElapsed",
|
|
59
|
+
]),
|
|
60
|
+
},
|
|
61
|
+
created() {
|
|
62
|
+
this.audioPlayer = document.querySelector("#audio-player");
|
|
63
|
+
},
|
|
64
|
+
methods: {
|
|
65
|
+
...mapActions(usePlayerStore, [
|
|
66
|
+
"playerUpdateSeekTime",
|
|
67
|
+
"playerUpdateElapsed",
|
|
68
|
+
]),
|
|
69
|
+
closePopup(): void {
|
|
70
|
+
this.$emit("close");
|
|
71
|
+
},
|
|
72
|
+
goToChapter(index: number) {
|
|
73
|
+
if (!this.playerChapteringPercent || !this.audioPlayer) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const seekTime =
|
|
77
|
+
this.playerTotal *
|
|
78
|
+
(this.playerChapteringPercent[index].startPercent / 100);
|
|
79
|
+
this.playerUpdateSeekTime(seekTime);
|
|
80
|
+
if (0 === seekTime) {
|
|
81
|
+
this.playerUpdateElapsed(0);
|
|
82
|
+
}
|
|
83
|
+
this.audioPlayer.currentTime = seekTime;
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
</script>
|
|
88
|
+
<style lang="scss">
|
|
89
|
+
@import "@scss/_variables.scss";
|
|
90
|
+
.octopus-app {
|
|
91
|
+
.chapter-selected{
|
|
92
|
+
border: $octopus-primary-color 3px solid;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
</style>
|