@saooti/octopus-sdk 41.0.13 → 41.0.14
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/eslint.config.mjs +5 -3
- package/index.ts +7 -1
- package/package.json +1 -2
- package/src/components/composable/player/usePlayerLogic.ts +2 -1
- package/src/components/composable/route/useSimplePageParam.ts +6 -1
- package/src/components/display/emission/EmissionList.vue +2 -1
- package/src/components/display/filter/AdvancedSearch.vue +2 -2
- package/src/components/display/organisation/OrganisationChooserLight.vue +34 -36
- package/src/components/display/podcasts/PodcastPlayButton.vue +2 -0
- package/src/components/form/ClassicCheckbox.vue +29 -4
- package/src/components/form/ClassicInputText.vue +11 -6
- package/src/components/misc/ClassicLazy.vue +25 -14
- package/src/components/misc/ClassicNav.vue +3 -0
- package/src/components/misc/FooterSection.vue +18 -20
- package/src/components/misc/TopBarMainContent.vue +8 -10
- package/src/components/misc/modal/ClassicModal.vue +4 -0
- package/src/components/misc/player/PlayerCompact.vue +1 -0
- package/src/components/misc/player/PlayerComponent.vue +1 -0
- package/src/components/pages/HomePage.vue +4 -3
- package/src/components/pages/PodcastsPage.vue +1 -1
- package/src/helper/equals.ts +26 -0
- package/src/router/router.ts +10 -74
- package/src/router/utils.ts +112 -0
- package/src/stores/AuthStore.ts +5 -0
- package/src/stores/FilterStore.ts +126 -71
- package/src/stores/PlayerStore.ts +5 -0
package/eslint.config.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import eslint from '@eslint/js';
|
|
2
|
-
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
3
2
|
import eslintPluginVue from 'eslint-plugin-vue';
|
|
4
3
|
import globals from 'globals';
|
|
5
4
|
import typescriptEslint from 'typescript-eslint';
|
|
@@ -23,7 +22,10 @@ export default typescriptEslint.config(
|
|
|
23
22
|
},
|
|
24
23
|
rules: {
|
|
25
24
|
// your rules
|
|
25
|
+
"curly": ['error'],
|
|
26
|
+
"no-console": ['warn', { allow: ['warn', 'error'] }],
|
|
27
|
+
"no-warning-comments": ['warn'],
|
|
28
|
+
"no-duplicate-imports": ['warn']
|
|
26
29
|
},
|
|
27
|
-
}
|
|
28
|
-
eslintConfigPrettier
|
|
30
|
+
}
|
|
29
31
|
);
|
package/index.ts
CHANGED
|
@@ -120,6 +120,7 @@ import cookiesHelper from "./src/helper/cookiesHelper.ts";
|
|
|
120
120
|
import downloadHelper from "./src/helper/downloadHelper.ts";
|
|
121
121
|
import displayHelper from "./src/helper/displayHelper.ts";
|
|
122
122
|
import debounce from "./src/helper/debounceHelper.ts";
|
|
123
|
+
import { deepEqual } from "./src/helper/equals.ts";
|
|
123
124
|
|
|
124
125
|
//stores
|
|
125
126
|
import {useVastStore} from "./src/stores/VastStore.ts";
|
|
@@ -147,6 +148,9 @@ export const getRadiolineIcon = () => import("./src/components/icons/RadiolineIc
|
|
|
147
148
|
export const getTuninIcon = () => import("./src/components/icons/TuninIcon.vue");
|
|
148
149
|
export const getXIcon = () => import("./src/components/icons/XIcon.vue");
|
|
149
150
|
|
|
151
|
+
// Routing
|
|
152
|
+
import { setupRouter } from './src/router/utils';
|
|
153
|
+
|
|
150
154
|
export {
|
|
151
155
|
useResizePhone,
|
|
152
156
|
useTagOf,
|
|
@@ -175,6 +179,8 @@ export {
|
|
|
175
179
|
ModuleApi,
|
|
176
180
|
classicApi,
|
|
177
181
|
cookiesHelper,
|
|
182
|
+
deepEqual,
|
|
178
183
|
downloadHelper,
|
|
179
|
-
displayHelper
|
|
184
|
+
displayHelper,
|
|
185
|
+
setupRouter
|
|
180
186
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saooti/octopus-sdk",
|
|
3
|
-
"version": "41.0.
|
|
3
|
+
"version": "41.0.14",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Javascript SDK for using octopus",
|
|
6
6
|
"author": "Saooti",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"axios": "^1.9.0",
|
|
33
33
|
"dayjs": "^1.11.13",
|
|
34
34
|
"emoji-mart-vue-fast": "^15.0.4",
|
|
35
|
-
"eslint-config-prettier": "^10.1.5",
|
|
36
35
|
"express": "^5.1.0",
|
|
37
36
|
"globals": "^16.2.0",
|
|
38
37
|
"hls.js": "^1.6.5",
|
|
@@ -11,7 +11,8 @@ import fetchHelper from "../../../helper/fetchHelper";
|
|
|
11
11
|
import classicApi from "../../../api/classicApi";
|
|
12
12
|
import dayjs from "dayjs";
|
|
13
13
|
import { FetchParam } from "@/stores/class/general/fetchParam";
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
export const usePlayerLogic = (forceHide: Ref<boolean, boolean>) => {
|
|
15
16
|
const hlsReady= ref(false);
|
|
16
17
|
|
|
17
18
|
const { listenTime, onPlay, setDownloadId, onTimeUpdateProgress, playLive, endingLive, playRadio} = usePlayerLive(hlsReady);
|
|
@@ -29,11 +29,16 @@ export const useSimplePageParam = (props: {readonly [key:string]: string|number}
|
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
// When changing global organisation, update organisation here
|
|
33
|
+
watch(() => filterStore.filterOrgaId, () => {
|
|
34
|
+
organisationId.value = filterStore.filterOrgaId;
|
|
35
|
+
});
|
|
36
|
+
|
|
32
37
|
onMounted(() => {
|
|
33
38
|
initOrga();
|
|
34
39
|
initSearchPattern();
|
|
35
40
|
isInit.value = true;
|
|
36
|
-
})
|
|
41
|
+
});
|
|
37
42
|
|
|
38
43
|
function getMinSize(param:string){
|
|
39
44
|
return param.length>3 ?param : ""
|
|
@@ -122,8 +122,9 @@ const displayArray = computed(() => {
|
|
|
122
122
|
});
|
|
123
123
|
const displayRubriquage = computed(() => state.emissionsPage.rubriquage);
|
|
124
124
|
const changePaginate = computed(() => `${props.first}|${props.size}`);
|
|
125
|
+
/** Computed property to track for configuration changes */
|
|
125
126
|
const changed = computed(() => {
|
|
126
|
-
return `${props.organisationId}|${props.query}|${props.monetisable}|${props.includeHidden}
|
|
127
|
+
return `${props.organisationId}|${props.query}|${props.monetisable}|${props.includeHidden}|\
|
|
127
128
|
${props.iabId}|${props.rubriqueId}|${props.rubriquageId}|${props.before}|${props.after}|${props.sort}|${props.noRubriquageId}`;
|
|
128
129
|
});
|
|
129
130
|
const sortText = computed(() => {
|
|
@@ -173,7 +173,7 @@ const isSelectValidity = computed(() => {
|
|
|
173
173
|
|
|
174
174
|
//Watch
|
|
175
175
|
watch(organisation, async () => {
|
|
176
|
-
const hidden =undefined !== organisation.value && organisationRight.value
|
|
176
|
+
const hidden = undefined !== organisation.value && organisationRight.value && !props.isEmission;
|
|
177
177
|
if (hidden !== props.includeHidden) {
|
|
178
178
|
updateIncludeHidden(hidden);
|
|
179
179
|
}
|
|
@@ -190,7 +190,6 @@ watch(()=>props.searchPattern, (value: string) => {
|
|
|
190
190
|
});
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
-
|
|
194
193
|
//Methods
|
|
195
194
|
function updateMonetisable(value: string): void {
|
|
196
195
|
emit("update:monetisable", value);
|
|
@@ -253,6 +252,7 @@ function clickShowFilters(): void {
|
|
|
253
252
|
showFilters.value = !showFilters.value;
|
|
254
253
|
}
|
|
255
254
|
</script>
|
|
255
|
+
|
|
256
256
|
<style lang="scss">
|
|
257
257
|
.octopus-app {
|
|
258
258
|
.advanced-search-container {
|
|
@@ -1,66 +1,64 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
<ClassicSelect
|
|
3
|
+
v-if="init && organisation"
|
|
4
|
+
:text-init="actual"
|
|
5
|
+
:display-label="false"
|
|
6
|
+
id-select="organisation-chooser-footer"
|
|
7
|
+
:label="t('select productor')"
|
|
8
|
+
:transparent="true"
|
|
9
|
+
:options="[
|
|
10
|
+
{ title: organisation.name, value: organisation.id },
|
|
11
|
+
{ title: t('No organisation filter'), value: 'NONE' },
|
|
12
|
+
]"
|
|
13
|
+
class="my-1"
|
|
14
|
+
@update:text-init="updateOrganisation"
|
|
15
|
+
/>
|
|
15
16
|
</template>
|
|
16
17
|
|
|
17
18
|
<script setup lang="ts">
|
|
18
19
|
import ClassicSelect from "../../form/ClassicSelect.vue";
|
|
19
20
|
import { Organisation } from "@/stores/class/general/organisation";
|
|
20
21
|
import { useSaveFetchStore } from "../../../stores/SaveFetchStore";
|
|
21
|
-
import {
|
|
22
|
+
import { computed, ref, watch } from "vue";
|
|
22
23
|
import { useI18n } from "vue-i18n";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
//Props
|
|
26
|
-
const props = defineProps({
|
|
27
|
-
value: { default: undefined, type: String },
|
|
28
|
-
reset: { default: false, type: Boolean },
|
|
29
|
-
})
|
|
24
|
+
import { useFilterStore } from "../../../stores/FilterStore";
|
|
30
25
|
|
|
31
26
|
//Emits
|
|
32
27
|
const emit = defineEmits(["selected"]);
|
|
33
28
|
|
|
34
29
|
//Data
|
|
35
|
-
const
|
|
36
|
-
const organisation: Ref<Organisation | undefined> = ref(undefined);
|
|
30
|
+
const organisation = ref<Organisation|undefined>(undefined);
|
|
37
31
|
const init = ref(false);
|
|
38
32
|
|
|
39
33
|
//Composables
|
|
40
34
|
const { t } = useI18n();
|
|
41
35
|
const SaveFetchStore = useSaveFetchStore();
|
|
36
|
+
const filterStore = useFilterStore();
|
|
42
37
|
|
|
38
|
+
// Computed
|
|
39
|
+
const actual = computed(() => {
|
|
40
|
+
if (filterStore.filterOrgaId) {
|
|
41
|
+
return filterStore.filterOrgaId;
|
|
42
|
+
} else {
|
|
43
|
+
return 'NONE';
|
|
44
|
+
}
|
|
45
|
+
});
|
|
43
46
|
|
|
44
47
|
//Watch
|
|
45
|
-
watch(()=>
|
|
46
|
-
|
|
47
|
-
fetchOrganisation();
|
|
48
|
-
}
|
|
48
|
+
watch(()=>filterStore.realOrgaId, async () => {
|
|
49
|
+
fetchOrganisation();
|
|
49
50
|
}, {deep: true, immediate: true});
|
|
50
|
-
watch(()=>props.reset, async () => {
|
|
51
|
-
actual.value = "NONE";
|
|
52
|
-
});
|
|
53
|
-
watch(actual, async () => {
|
|
54
|
-
emit("selected","NONE" === actual.value ? undefined : organisation.value);
|
|
55
|
-
});
|
|
56
51
|
|
|
57
52
|
//Methods
|
|
58
53
|
async function fetchOrganisation(): Promise<void> {
|
|
59
|
-
if (!
|
|
54
|
+
if (!filterStore.realOrgaId) {
|
|
60
55
|
return;
|
|
61
56
|
}
|
|
62
|
-
organisation.value = await SaveFetchStore.getOrgaData(
|
|
63
|
-
actual.value = organisation.value.id;
|
|
57
|
+
organisation.value = await SaveFetchStore.getOrgaData(filterStore.realOrgaId);
|
|
64
58
|
init.value = true;
|
|
65
59
|
}
|
|
60
|
+
|
|
61
|
+
function updateOrganisation(value: string): void {
|
|
62
|
+
emit("selected", "NONE" === value ? undefined : organisation.value);
|
|
63
|
+
}
|
|
66
64
|
</script>
|
|
@@ -228,6 +228,8 @@ function play(isVideo: boolean): void {
|
|
|
228
228
|
position: absolute;
|
|
229
229
|
inset: 0;
|
|
230
230
|
background-color:var(--octopus-background-transparent);
|
|
231
|
+
// Allow pointer events to go through (allow click on image beneath blur)
|
|
232
|
+
pointer-events: none;
|
|
231
233
|
}
|
|
232
234
|
|
|
233
235
|
.live-image-status {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="d-flex flex-nowrap align-items-center octopus-form-item">
|
|
3
3
|
<div :class="isSwitch ? 'octopus-form-switch me-2' : ''">
|
|
4
4
|
<input
|
|
5
|
-
:id="
|
|
5
|
+
:id="computedIdCheckbox"
|
|
6
6
|
:checked="textInit"
|
|
7
7
|
type="checkbox"
|
|
8
8
|
:disabled="isDisabled"
|
|
@@ -16,26 +16,35 @@
|
|
|
16
16
|
v-if="isSwitch"
|
|
17
17
|
class="slider btn-transparent"
|
|
18
18
|
:title="label"
|
|
19
|
+
:disabled="isDisabled"
|
|
19
20
|
@click="clickSlider"
|
|
20
21
|
@keydown.space.prevent="clickSlider"
|
|
21
22
|
/>
|
|
22
23
|
</div>
|
|
23
24
|
<label
|
|
24
25
|
class="c-hand"
|
|
25
|
-
:class="[classLabel, displayLabel ? '' : 'd-none']"
|
|
26
|
-
:for="
|
|
27
|
-
>{{ label }}</label
|
|
26
|
+
:class="[classLabel, displayLabel ? '' : 'd-none', isDisabled ? 'disabled' : '']"
|
|
27
|
+
:for="computedIdCheckbox"
|
|
28
28
|
>
|
|
29
|
+
{{ label }}
|
|
30
|
+
</label>
|
|
29
31
|
</div>
|
|
30
32
|
</template>
|
|
31
33
|
|
|
32
34
|
<script setup lang="ts">
|
|
35
|
+
import { computed, getCurrentInstance } from 'vue';
|
|
36
|
+
|
|
33
37
|
//Props
|
|
34
38
|
const props = defineProps({
|
|
39
|
+
/** The ID for the checkbox input */
|
|
35
40
|
idCheckbox: { default: "", type: String },
|
|
41
|
+
/** The label to display with the checkbox */
|
|
36
42
|
label: { default: "", type: String },
|
|
43
|
+
/** Disables input */
|
|
37
44
|
isDisabled: { default: false, type: Boolean },
|
|
45
|
+
/** The value of the checkbox */
|
|
38
46
|
textInit: { default: false, type: Boolean },
|
|
47
|
+
/** If true, displays a switch instead of a checkbox */
|
|
39
48
|
isSwitch: { default: false, type: Boolean },
|
|
40
49
|
displayLabel: { default: true, type: Boolean },
|
|
41
50
|
classLabel: { default: "", type: String },
|
|
@@ -45,6 +54,11 @@ const props = defineProps({
|
|
|
45
54
|
//Emits
|
|
46
55
|
const emit = defineEmits(["update:textInit", "clickAction"]);
|
|
47
56
|
|
|
57
|
+
// Computed
|
|
58
|
+
const computedIdCheckbox = computed(() => {
|
|
59
|
+
return props.idCheckbox || 'checkbox-' + getCurrentInstance()?.uid;
|
|
60
|
+
});
|
|
61
|
+
|
|
48
62
|
//Methods
|
|
49
63
|
function emitClickAction(): void {
|
|
50
64
|
emit("clickAction");
|
|
@@ -59,6 +73,12 @@ function clickSlider() {
|
|
|
59
73
|
|
|
60
74
|
<style lang="scss">
|
|
61
75
|
.octopus-app {
|
|
76
|
+
|
|
77
|
+
label.disabled {
|
|
78
|
+
color: var(--octopus-text-disabled);
|
|
79
|
+
cursor: default;
|
|
80
|
+
}
|
|
81
|
+
|
|
62
82
|
.octopus-form-switch {
|
|
63
83
|
position: relative;
|
|
64
84
|
display: inline-block;
|
|
@@ -92,6 +112,11 @@ function clickSlider() {
|
|
|
92
112
|
border-radius: 50%;
|
|
93
113
|
}
|
|
94
114
|
|
|
115
|
+
.slider:disabled::before {
|
|
116
|
+
background-color: var(--octopus-text-disabled);
|
|
117
|
+
opacity: 0.6;
|
|
118
|
+
}
|
|
119
|
+
|
|
95
120
|
input:checked + .slider {
|
|
96
121
|
background-color: var(--octopus-primary);
|
|
97
122
|
}
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
<component
|
|
9
9
|
:is="isWysiwyg? 'div': 'label'"
|
|
10
10
|
:class="[classLabel, displayLabel ? '' : 'd-none']"
|
|
11
|
-
:for="isWysiwyg ? '':
|
|
11
|
+
:for="isWysiwyg ? '': computedInputId"
|
|
12
12
|
>{{ label }}
|
|
13
13
|
<AsteriskIcon v-if="displayRequired" :size="10" class="ms-1 mb-2" :title="t('Mandatory input')"/>
|
|
14
14
|
</component>
|
|
15
15
|
<slot name="afterTitle"/>
|
|
16
16
|
<template v-if="popover">
|
|
17
17
|
<button
|
|
18
|
-
:id="'popover' +
|
|
18
|
+
:id="'popover' + computedInputId"
|
|
19
19
|
:title="t('Help')"
|
|
20
20
|
class="btn-transparent"
|
|
21
21
|
>
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
</button>
|
|
24
24
|
|
|
25
25
|
<ClassicPopover
|
|
26
|
-
:target="'popover' +
|
|
26
|
+
:target="'popover' + computedInputId"
|
|
27
27
|
popover-class="popover-z-index"
|
|
28
28
|
:relative-class="popoverRelativeClass"
|
|
29
29
|
>
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<input
|
|
39
39
|
v-if="!isWysiwyg && !isTextarea"
|
|
40
40
|
v-show="showField"
|
|
41
|
-
:id="
|
|
41
|
+
:id="computedInputId"
|
|
42
42
|
ref="focusElement"
|
|
43
43
|
v-model="textValue"
|
|
44
44
|
:type="typeInput"
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
<textarea
|
|
60
60
|
v-else-if="isTextarea"
|
|
61
61
|
v-show="showField"
|
|
62
|
-
:id="
|
|
62
|
+
:id="computedInputId"
|
|
63
63
|
ref="focusElement"
|
|
64
64
|
v-model="textValue"
|
|
65
65
|
:data-selenium="dataSelenium"
|
|
@@ -113,10 +113,11 @@
|
|
|
113
113
|
</div>
|
|
114
114
|
</div>
|
|
115
115
|
</template>
|
|
116
|
+
|
|
116
117
|
<script setup lang="ts">
|
|
117
118
|
import AsteriskIcon from "vue-material-design-icons/Asterisk.vue";
|
|
118
119
|
import HelpCircleIcon from "vue-material-design-icons/HelpCircle.vue";
|
|
119
|
-
import { computed, defineAsyncComponent, onMounted, Ref, ref, useTemplateRef, watch } from "vue";
|
|
120
|
+
import { computed, defineAsyncComponent, onMounted, Ref, ref, useTemplateRef, watch, getCurrentInstance } from "vue";
|
|
120
121
|
import { useI18n } from "vue-i18n";
|
|
121
122
|
const ClassicPopover = defineAsyncComponent(
|
|
122
123
|
() => import("../misc/ClassicPopover.vue"),
|
|
@@ -132,6 +133,7 @@ const ClassicEmojiPicker = defineAsyncComponent(
|
|
|
132
133
|
const props = defineProps({
|
|
133
134
|
inputId: { default: "", type: String },
|
|
134
135
|
label: { default: "", type: String },
|
|
136
|
+
/** The input's value */
|
|
135
137
|
textInit: { default: undefined, type: String },
|
|
136
138
|
maxLength: { default: 0, type: Number },
|
|
137
139
|
errorText: { default: "", type: String },
|
|
@@ -141,6 +143,7 @@ const props = defineProps({
|
|
|
141
143
|
canBeNull: { default: false, type: Boolean },
|
|
142
144
|
inputMaxLengthField: { default: undefined, type: Number },
|
|
143
145
|
errorVariable: { default: true, type: Boolean },
|
|
146
|
+
/** Disable the text input */
|
|
144
147
|
isDisable: { default: false, type: Boolean },
|
|
145
148
|
indicText: { default: "", type: String },
|
|
146
149
|
dataSelenium: { default: "", type: String },
|
|
@@ -171,6 +174,7 @@ const focusElementRef = useTemplateRef('focusElement');
|
|
|
171
174
|
const { t } = useI18n();
|
|
172
175
|
|
|
173
176
|
//Computed
|
|
177
|
+
const computedInputId = computed(() => props.inputId || 'input-' + getCurrentInstance()?.uid);
|
|
174
178
|
const isError = computed(() => !valueTrimValid.value || !valueLengthValid.value || !valueRegexValid.value);
|
|
175
179
|
const countValue = computed(() => {
|
|
176
180
|
if (textValue.value) {
|
|
@@ -239,6 +243,7 @@ function addEmojiSelected(emoji: string) {
|
|
|
239
243
|
textValue.value = (textValue.value ?? "") + emoji;
|
|
240
244
|
}
|
|
241
245
|
</script>
|
|
246
|
+
|
|
242
247
|
<style lang="scss">
|
|
243
248
|
.octopus-app .classic-input-text {
|
|
244
249
|
.text-indic {
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
<slot v-else name="preview" />
|
|
5
5
|
</div>
|
|
6
6
|
</template>
|
|
7
|
+
|
|
7
8
|
<script setup lang="ts">
|
|
8
9
|
import { useIntersectionObserver } from "@vueuse/core";
|
|
9
|
-
import { ref, nextTick, watch } from "vue";
|
|
10
|
+
import { ref, nextTick, watch, onMounted } from "vue";
|
|
10
11
|
|
|
11
12
|
//Props
|
|
12
13
|
const props = defineProps({
|
|
@@ -37,8 +38,8 @@ const { pause, resume } = useIntersectionObserver(
|
|
|
37
38
|
if (isIntersecting) {
|
|
38
39
|
// perhaps the user re-scrolled to a component that was set to unrender. In that case stop the unrendering timer
|
|
39
40
|
clearTimeout(unrenderTimer);
|
|
40
|
-
// if we're dealing underndering lets add a waiting period of 200ms before rendering. If a component enters the viewport and also leaves it within 200ms it will not render at all. This saves work and improves performance when user scrolls very fast
|
|
41
41
|
|
|
42
|
+
// if we're dealing underndering lets add a waiting period of 200ms before rendering. If a component enters the viewport and also leaves it within 200ms it will not render at all. This saves work and improves performance when user scrolls very fast
|
|
42
43
|
renderTimer = setTimeout(
|
|
43
44
|
() => {
|
|
44
45
|
shouldRender.value = true;
|
|
@@ -46,6 +47,7 @@ const { pause, resume } = useIntersectionObserver(
|
|
|
46
47
|
},
|
|
47
48
|
props.unrender ? 200 : 0,
|
|
48
49
|
);
|
|
50
|
+
|
|
49
51
|
if (!props.unrender) {
|
|
50
52
|
pause();
|
|
51
53
|
}
|
|
@@ -64,18 +66,27 @@ const { pause, resume } = useIntersectionObserver(
|
|
|
64
66
|
);
|
|
65
67
|
|
|
66
68
|
//Logic
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
onMounted(() => {
|
|
70
|
+
if (props.initRenderDelay <= 0) {
|
|
71
|
+
// If there's no render delay, do not delay initialization
|
|
72
|
+
waitBeforeInit.value = false;
|
|
73
|
+
} else {
|
|
74
|
+
// Otherwise delay initialization
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
waitBeforeInit.value = false;
|
|
77
|
+
}, props.initRenderDelay);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (props.renderOnIdle) {
|
|
81
|
+
onIdle(() => {
|
|
82
|
+
shouldRender.value = true;
|
|
83
|
+
emit("isRender", true);
|
|
84
|
+
if (!props.unrender) {
|
|
85
|
+
pause();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
79
90
|
|
|
80
91
|
//Watch
|
|
81
92
|
watch(
|
|
@@ -5,11 +5,17 @@
|
|
|
5
5
|
role="contentinfo"
|
|
6
6
|
class="d-flex align-items-center justify-content-between border-top mt-auto"
|
|
7
7
|
>
|
|
8
|
-
<div
|
|
8
|
+
<div
|
|
9
|
+
v-if="!state.generalParameters.podcastmaker"
|
|
10
|
+
class="d-flex flex-column px-1"
|
|
11
|
+
>
|
|
9
12
|
<div class="text-dark my-1 special-select-align-magic-trick">
|
|
10
13
|
© Saooti 2025
|
|
11
14
|
</div>
|
|
12
|
-
<FooterGarSection
|
|
15
|
+
<FooterGarSection
|
|
16
|
+
v-if="authStore.isGarRole"
|
|
17
|
+
:auth-orga-id="authStore.authOrgaId"
|
|
18
|
+
/>
|
|
13
19
|
<nav :aria-label="t('Site menu')">
|
|
14
20
|
<ul class="p-0 m-0">
|
|
15
21
|
<li
|
|
@@ -43,13 +49,11 @@
|
|
|
43
49
|
class="my-1"
|
|
44
50
|
/>
|
|
45
51
|
<OrganisationChooserLight
|
|
46
|
-
v-if="!state.generalParameters.podcastmaker &&
|
|
52
|
+
v-if="!state.generalParameters.podcastmaker && authenticated"
|
|
47
53
|
page="footer"
|
|
48
54
|
width="auto"
|
|
49
55
|
class="my-1"
|
|
50
56
|
:defaultanswer="t('No organisation filter')"
|
|
51
|
-
:value="organisationId"
|
|
52
|
-
:reset="reset"
|
|
53
57
|
@selected="onOrganisationSelected"
|
|
54
58
|
/>
|
|
55
59
|
</div>
|
|
@@ -83,7 +87,7 @@ import { useFilterStore } from "../../stores/FilterStore";
|
|
|
83
87
|
import { useGeneralStore } from "../../stores/GeneralStore";
|
|
84
88
|
import { useAuthStore } from "../../stores/AuthStore";
|
|
85
89
|
import { Category } from "@/stores/class/general/category";
|
|
86
|
-
import { computed, defineAsyncComponent,
|
|
90
|
+
import { computed, defineAsyncComponent, ref, watch } from "vue";
|
|
87
91
|
import { Organisation } from "@/stores/class/general/organisation";
|
|
88
92
|
import { useI18n } from "vue-i18n";
|
|
89
93
|
import { useRoute, useRouter } from "vue-router";
|
|
@@ -98,8 +102,6 @@ const { t, locale } = useI18n();
|
|
|
98
102
|
|
|
99
103
|
//Data
|
|
100
104
|
const language = ref(locale);
|
|
101
|
-
const reset = ref(false);
|
|
102
|
-
const organisationId: Ref<string | undefined> = ref(undefined);
|
|
103
105
|
|
|
104
106
|
//Composables
|
|
105
107
|
const generalStore = useGeneralStore();
|
|
@@ -127,15 +129,6 @@ const routerLinkSecondArray = computed(() => {
|
|
|
127
129
|
|
|
128
130
|
//Watch
|
|
129
131
|
watch(language, () => changeLanguage());
|
|
130
|
-
watch(()=>filterStore.filterOrgaId, () => {
|
|
131
|
-
if (filterStore.filterOrgaId) {
|
|
132
|
-
organisationId.value = filterStore.filterOrgaId;
|
|
133
|
-
} else {
|
|
134
|
-
reset.value = !reset.value;
|
|
135
|
-
}
|
|
136
|
-
}, {immediate: true});
|
|
137
|
-
|
|
138
|
-
|
|
139
132
|
|
|
140
133
|
//Methods
|
|
141
134
|
function changeLanguage(): void {
|
|
@@ -164,15 +157,20 @@ function changeLanguage(): void {
|
|
|
164
157
|
}
|
|
165
158
|
});
|
|
166
159
|
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Select another organisation
|
|
163
|
+
* @param organisation The new organisation to focus on, or undefined to remove focus
|
|
164
|
+
*/
|
|
167
165
|
async function onOrganisationSelected( organisation: Organisation | undefined): Promise<void> {
|
|
166
|
+
// TODO use router utils
|
|
168
167
|
if (organisation?.id) {
|
|
169
168
|
router.push({
|
|
170
|
-
query: { ...route.query, ...{ productor: organisation.id, o:undefined } },
|
|
169
|
+
query: { ...route.query, ...{ productor: organisation.id, o:undefined, displayAll: "false" } },
|
|
171
170
|
});
|
|
172
171
|
}else{
|
|
173
|
-
organisationId.value = undefined;
|
|
174
172
|
router.push({
|
|
175
|
-
query: { ...route.query, ...{ productor: undefined } },
|
|
173
|
+
query: { ...route.query, ...{ productor: undefined, displayAll: "true" } },
|
|
176
174
|
});
|
|
177
175
|
}
|
|
178
176
|
}
|
|
@@ -13,22 +13,20 @@
|
|
|
13
13
|
:src="logoUrl"
|
|
14
14
|
aria-hidden="true"
|
|
15
15
|
alt=""
|
|
16
|
-
|
|
17
16
|
width="140"
|
|
18
17
|
height="50"
|
|
19
18
|
title="Logo"
|
|
20
19
|
:class="generalStore.platformEducation ? 'education-logo' : 'octopus-logo'"
|
|
21
|
-
|
|
20
|
+
>
|
|
22
21
|
<img
|
|
23
22
|
v-else
|
|
24
23
|
:src="useProxyImageUrl(imgUrl, '', '80')"
|
|
25
24
|
aria-hidden="true"
|
|
26
25
|
alt=""
|
|
27
|
-
|
|
28
26
|
class="client-logo"
|
|
29
27
|
title="Logo"
|
|
30
28
|
:class="generalStore.platformEducation ? 'education-logo' : ''"
|
|
31
|
-
|
|
29
|
+
>
|
|
32
30
|
</router-link>
|
|
33
31
|
<h1 v-if="titleIsDisplayed" class="text-truncate m-0 align-self-center">
|
|
34
32
|
{{ titleDisplay }}
|
|
@@ -46,14 +44,13 @@
|
|
|
46
44
|
v-if="authStore.isGarRole"
|
|
47
45
|
:src="logoUrl"
|
|
48
46
|
aria-hidden="true"
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
alt=""
|
|
51
48
|
width="100"
|
|
52
49
|
height="29"
|
|
53
50
|
class="ms-2"
|
|
54
51
|
title="Logo"
|
|
55
52
|
:class="generalStore.platformEducation ? 'education-logo' : 'octopus-logo'"
|
|
56
|
-
|
|
53
|
+
>
|
|
57
54
|
<a
|
|
58
55
|
v-else
|
|
59
56
|
href="https://www.saooti.com/"
|
|
@@ -64,14 +61,14 @@
|
|
|
64
61
|
<img
|
|
65
62
|
:src="logoUrl"
|
|
66
63
|
aria-hidden="true"
|
|
67
|
-
|
|
64
|
+
alt=""
|
|
68
65
|
|
|
69
66
|
title="Saooti"
|
|
70
67
|
width="100"
|
|
71
68
|
height="29"
|
|
72
69
|
class="ms-2"
|
|
73
70
|
:class="generalStore.platformEducation ? 'education-logo' : 'octopus-logo'"
|
|
74
|
-
|
|
71
|
+
>
|
|
75
72
|
</a>
|
|
76
73
|
</template>
|
|
77
74
|
<div role="navigation" class="d-flex align-items-center justify-content-end flex-grow-1">
|
|
@@ -128,10 +125,11 @@
|
|
|
128
125
|
{{ link.title }}
|
|
129
126
|
</router-link>
|
|
130
127
|
</li>
|
|
131
|
-
|
|
128
|
+
</template>
|
|
132
129
|
</ul>
|
|
133
130
|
</nav>
|
|
134
131
|
</ClassicPopover>
|
|
132
|
+
|
|
135
133
|
<MobileMenu
|
|
136
134
|
:is-education="generalStore.platformEducation"
|
|
137
135
|
:show="mobileMenuDisplay"
|
|
@@ -48,8 +48,11 @@ import { useI18n } from "vue-i18n";
|
|
|
48
48
|
//Props
|
|
49
49
|
defineProps({
|
|
50
50
|
idModal: { default: undefined, type: String },
|
|
51
|
+
/** The title of the modal */
|
|
51
52
|
titleModal: { default: undefined, type: String },
|
|
53
|
+
/** If false, the modal won't display a close button (default: true) */
|
|
52
54
|
closable: { default: true, type: Boolean },
|
|
55
|
+
/** If true, the modal can be reduced to show only the header (default: false) */
|
|
53
56
|
canBeReduced: { default: false, type: Boolean },
|
|
54
57
|
})
|
|
55
58
|
|
|
@@ -79,6 +82,7 @@ function closePopup(): void {
|
|
|
79
82
|
emit("close");
|
|
80
83
|
}
|
|
81
84
|
</script>
|
|
85
|
+
|
|
82
86
|
<style lang="scss">
|
|
83
87
|
|
|
84
88
|
.octopus-app .octopus-modal.octopus-modal-top-layer{
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
:button-text="t('All podcast button', { name: c.name })"
|
|
15
15
|
/>
|
|
16
16
|
<template #preview>
|
|
17
|
-
<div style="min-height: 650px"
|
|
17
|
+
<div style="min-height: 650px" />
|
|
18
18
|
</template>
|
|
19
19
|
</ClassicLazy>
|
|
20
20
|
</template>
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
:button-text="t('All podcast button', { name: r.name })"
|
|
34
34
|
/>
|
|
35
35
|
<template #preview>
|
|
36
|
-
<div style="min-height: 650px"
|
|
36
|
+
<div style="min-height: 650px" />
|
|
37
37
|
</template>
|
|
38
38
|
</ClassicLazy>
|
|
39
39
|
<template v-if="rubriqueDisplay && rubriqueDisplay.length > 0">
|
|
@@ -129,8 +129,9 @@ const categories = computed(() => {
|
|
|
129
129
|
});
|
|
130
130
|
} else {
|
|
131
131
|
arrayCategories = generalStore.storedCategories.filter((c: Category) => {
|
|
132
|
-
if (state.generalParameters.podcastmaker)
|
|
132
|
+
if (state.generalParameters.podcastmaker) {
|
|
133
133
|
return c.podcastOrganisationCount;
|
|
134
|
+
}
|
|
134
135
|
return c.podcastCount;
|
|
135
136
|
});
|
|
136
137
|
}
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
/>
|
|
39
39
|
</section>
|
|
40
40
|
</template>
|
|
41
|
+
|
|
41
42
|
<script setup lang="ts">
|
|
42
43
|
import PodcastList from "../display/podcasts/PodcastList.vue";
|
|
43
44
|
import ProductorSearch from "../display/filter/ProductorSearch.vue";
|
|
@@ -85,7 +86,6 @@ const {
|
|
|
85
86
|
isInit
|
|
86
87
|
} = useAdvancedParamInit(props, false);
|
|
87
88
|
|
|
88
|
-
|
|
89
89
|
//Computed
|
|
90
90
|
const orgaArray = computed(() => organisationId.value ? [organisationId.value] : []);
|
|
91
91
|
const withVideo = computed(() => false === onlyVideo.value ? undefined : true);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function deepEqual(obj1: any, obj2: any) {
|
|
2
|
+
|
|
3
|
+
if(obj1 === obj2) // it's just the same object. No need to compare.
|
|
4
|
+
{return true;}
|
|
5
|
+
|
|
6
|
+
if(isPrimitive(obj1) && isPrimitive(obj2)) // compare primitives
|
|
7
|
+
{return obj1 === obj2;}
|
|
8
|
+
|
|
9
|
+
if(Object.keys(obj1).length !== Object.keys(obj2).length) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// compare objects with same number of keys
|
|
14
|
+
for(const key in obj1)
|
|
15
|
+
{
|
|
16
|
+
if(!(key in obj2)) {return false;} //other object doesn't have this prop
|
|
17
|
+
if(!deepEqual(obj1[key], obj2[key])) {return false;}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//check if value is primitive
|
|
24
|
+
function isPrimitive(obj: unknown) {
|
|
25
|
+
return (obj !== Object(obj));
|
|
26
|
+
}
|
package/src/router/router.ts
CHANGED
|
@@ -4,12 +4,10 @@ import {
|
|
|
4
4
|
RouteLocationNormalized,
|
|
5
5
|
RouteRecordRaw,
|
|
6
6
|
} from "vue-router";
|
|
7
|
-
import { useFilterStore } from "../stores/FilterStore";
|
|
8
|
-
import { useSaveFetchStore } from "@/stores/SaveFetchStore";
|
|
9
|
-
import { Rubriquage } from "@/stores/class/rubrique/rubriquage";
|
|
10
7
|
import classicApi from "@/api/classicApi";
|
|
11
|
-
import {
|
|
8
|
+
import { AuthStore } from "../stores/AuthStore";
|
|
12
9
|
import fetchHelper from "@/helper/fetchHelper";
|
|
10
|
+
import { setupRouter } from "./utils";
|
|
13
11
|
|
|
14
12
|
/*--------------------------------------------------------------------------
|
|
15
13
|
Composants publics
|
|
@@ -334,13 +332,16 @@ const router = createRouter({
|
|
|
334
332
|
history: createWebHistory(),
|
|
335
333
|
routes: routes,
|
|
336
334
|
scrollBehavior(to, from) {
|
|
337
|
-
if (to.name === from.name && to.meta.noScroll)
|
|
338
|
-
|
|
335
|
+
if (to.name === from.name && to.meta.noScroll) {
|
|
336
|
+
return false;
|
|
337
|
+
} else {
|
|
338
|
+
return { left: 0, top: 0 };
|
|
339
|
+
}
|
|
339
340
|
},
|
|
340
341
|
});
|
|
341
342
|
|
|
342
343
|
//Do in frontoffice but not podcastmakers
|
|
343
|
-
async function getMyOrgaActive(authStore:
|
|
344
|
+
async function getMyOrgaActive(authStore: AuthStore): Promise<string>{
|
|
344
345
|
const orgaActive = await classicApi.fetchData<string>({
|
|
345
346
|
api: 3,
|
|
346
347
|
path: "user/active"
|
|
@@ -352,72 +353,7 @@ async function getMyOrgaActive(authStore: any): Promise<string>{
|
|
|
352
353
|
}
|
|
353
354
|
return orgaActive;
|
|
354
355
|
}
|
|
355
|
-
async function changeOrgaFilter(orgaFilter: string, filterStore: any){
|
|
356
|
-
const saveStore = useSaveFetchStore();
|
|
357
|
-
const response = await saveStore.getOrgaData(orgaFilter);
|
|
358
|
-
const data = await classicApi.fetchData<Array<Rubriquage>>({
|
|
359
|
-
api: 0,
|
|
360
|
-
path: "rubriquage/find/" + orgaFilter,
|
|
361
|
-
parameters: {
|
|
362
|
-
sort: "HOMEPAGEORDER",
|
|
363
|
-
homePageOrder: true,
|
|
364
|
-
},
|
|
365
|
-
specialTreatement: true,
|
|
366
|
-
});
|
|
367
|
-
const isLive = await saveStore.getOrgaLiveEnabled(orgaFilter);
|
|
368
|
-
filterStore.filterUpdateOrga({
|
|
369
|
-
orgaId: orgaFilter,
|
|
370
|
-
imgUrl: response.imageUrl,
|
|
371
|
-
name: response.name,
|
|
372
|
-
rubriquageArray: data.filter((element: Rubriquage) => {
|
|
373
|
-
return element.rubriques.length;
|
|
374
|
-
}),
|
|
375
|
-
isLive: isLive,
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
let fetchMyOrgaActive = false;
|
|
379
|
-
router.beforeResolve(async () =>{
|
|
380
|
-
fetchMyOrgaActive = false;
|
|
381
|
-
});
|
|
382
|
-
router.beforeEach(async (to, from) => {
|
|
383
|
-
if ("/logout" === to.path && "/logout" !== from.path) {
|
|
384
|
-
setTimeout(() => {
|
|
385
|
-
window.location.reload(true);
|
|
386
|
-
}, 500);
|
|
387
|
-
}
|
|
388
|
-
const authStore = useAuthStore();
|
|
389
|
-
const filterStore = useFilterStore();
|
|
390
|
-
|
|
391
|
-
const isSamePath = to.matched[0]?.path === from.matched[0]?.path && to.path.includes(from.path);
|
|
392
|
-
let orgaToFocus = isSamePath ? (to.query.productor?.toString() ?? undefined) : undefined;
|
|
393
356
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
await getMyOrgaActive(authStore);
|
|
397
|
-
fetchMyOrgaActive = true;
|
|
398
|
-
}
|
|
399
|
-
if(undefined!==orgaToFocus){
|
|
400
|
-
orgaToFocus = authStore.authOrgaId;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (isSamePath && orgaToFocus !== from.query.productor) {
|
|
404
|
-
if (undefined === orgaToFocus) {
|
|
405
|
-
filterStore.filterUpdateOrga({ orgaId: undefined });
|
|
406
|
-
} else if (filterStore.filterOrgaId !== orgaToFocus) {
|
|
407
|
-
await changeOrgaFilter(orgaToFocus, filterStore);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
if (
|
|
411
|
-
"/logout" !== to.path &&
|
|
412
|
-
filterStore.filterOrgaId !== to.query.productor &&
|
|
413
|
-
undefined !== filterStore.filterOrgaId
|
|
414
|
-
) {
|
|
415
|
-
return {
|
|
416
|
-
path: to.path,
|
|
417
|
-
query: { ...to.query, ...{ productor: filterStore.filterOrgaId } },
|
|
418
|
-
params: to.params,
|
|
419
|
-
name: to.name,
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
});
|
|
357
|
+
setupRouter(router, getMyOrgaActive);
|
|
358
|
+
|
|
423
359
|
export default router;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Router } from "vue-router";
|
|
2
|
+
import { useFilterStore, FilterStore } from "../stores/FilterStore";
|
|
3
|
+
import { useSaveFetchStore } from "../stores/SaveFetchStore";
|
|
4
|
+
import { Rubriquage } from "../stores/class/rubrique/rubriquage";
|
|
5
|
+
import classicApi from "../api/classicApi";
|
|
6
|
+
import { useAuthStore, AuthStore } from "../stores/AuthStore";
|
|
7
|
+
import { deepEqual } from "../helper/equals";
|
|
8
|
+
|
|
9
|
+
async function changeOrgaFilter(orgaFilter: string, filterStore: FilterStore){
|
|
10
|
+
const saveStore = useSaveFetchStore();
|
|
11
|
+
const response = await saveStore.getOrgaData(orgaFilter);
|
|
12
|
+
const data = await classicApi.fetchData<Array<Rubriquage>>({
|
|
13
|
+
api: 0,
|
|
14
|
+
path: "rubriquage/find/" + orgaFilter,
|
|
15
|
+
parameters: {
|
|
16
|
+
sort: "HOMEPAGEORDER",
|
|
17
|
+
homePageOrder: true,
|
|
18
|
+
},
|
|
19
|
+
specialTreatement: true,
|
|
20
|
+
});
|
|
21
|
+
const isLive = await saveStore.getOrgaLiveEnabled(orgaFilter);
|
|
22
|
+
filterStore.filterUpdateOrga({
|
|
23
|
+
orgaId: orgaFilter,
|
|
24
|
+
imgUrl: response.imageUrl,
|
|
25
|
+
name: response.name,
|
|
26
|
+
rubriquageArray: data.filter((element: Rubriquage) => {
|
|
27
|
+
return element.rubriques.length;
|
|
28
|
+
}),
|
|
29
|
+
isLive: isLive,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let fetchMyOrgaActive = false;
|
|
34
|
+
/** Variable used to apply beforeEach redirect only once */
|
|
35
|
+
let resolved = false;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Utility function seting up the router with a custom beforeEach
|
|
39
|
+
*/
|
|
40
|
+
export function setupRouter(router: Router, getMyOrgaActive: (authStore: AuthStore) => Promise<string>): void {
|
|
41
|
+
router.beforeResolve(async () =>{
|
|
42
|
+
fetchMyOrgaActive = false;
|
|
43
|
+
// Reinit variable to allow one redirect
|
|
44
|
+
resolved = false;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Navigation guard that updates current organisation & may make redirects
|
|
48
|
+
router.beforeEach(async (to, from) => {
|
|
49
|
+
|
|
50
|
+
if ("/logout" === to.path && "/logout" !== from.path) {
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
window.location.reload(true);
|
|
53
|
+
}, 500);
|
|
54
|
+
}
|
|
55
|
+
const authStore = useAuthStore();
|
|
56
|
+
const filterStore = useFilterStore();
|
|
57
|
+
|
|
58
|
+
const isSamePath = to.matched[0]?.path === from.matched[0]?.path && to.path.includes(from.path);
|
|
59
|
+
let orgaToFocus = isSamePath ? (to.query.productor?.toString() ?? undefined) : undefined;
|
|
60
|
+
|
|
61
|
+
if(authStore.authProfile){
|
|
62
|
+
if(!isSamePath && !fetchMyOrgaActive){
|
|
63
|
+
await getMyOrgaActive(authStore);
|
|
64
|
+
fetchMyOrgaActive = true;
|
|
65
|
+
}
|
|
66
|
+
if(undefined!==orgaToFocus){
|
|
67
|
+
orgaToFocus = authStore.authOrgaId;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Update organisation
|
|
72
|
+
if (isSamePath && orgaToFocus !== from.query.productor) {
|
|
73
|
+
if (filterStore.filterOrgaId !== orgaToFocus && orgaToFocus !== undefined) {
|
|
74
|
+
await changeOrgaFilter(orgaToFocus, filterStore);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Only change target if not going to logout and not already resolved
|
|
79
|
+
if ("/logout" !== to.path && resolved !== true) {
|
|
80
|
+
resolved = true;
|
|
81
|
+
const newQuery = {
|
|
82
|
+
...to.query
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Set productor
|
|
86
|
+
if (to.query.productor) {
|
|
87
|
+
newQuery.productor = to.query.productor;
|
|
88
|
+
} else if (filterStore.filterOrgaId === undefined) {
|
|
89
|
+
delete newQuery.productor;
|
|
90
|
+
} else {
|
|
91
|
+
newQuery.productor = filterStore.filterOrgaId;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Enable 'displayAll' mode if already active
|
|
95
|
+
if ((from.query.displayAll === "true" || to.query.displayAll === "true") && to.query.displayAll !== "false") {
|
|
96
|
+
newQuery.displayAll = "true";
|
|
97
|
+
} else {
|
|
98
|
+
delete newQuery.displayAll;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// If the queries are different, update path
|
|
102
|
+
if (!deepEqual(newQuery, to.query)) {
|
|
103
|
+
return {
|
|
104
|
+
path: to.path,
|
|
105
|
+
query: { ...newQuery },
|
|
106
|
+
params: to.params,
|
|
107
|
+
name: to.name,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
package/src/stores/AuthStore.ts
CHANGED
|
@@ -5,12 +5,14 @@ import { defineStore } from "pinia";
|
|
|
5
5
|
import { KeycloakInfo } from "@/stores/class/user/person";
|
|
6
6
|
import { VideoConfig } from "@/stores/class/config/videoConfig";
|
|
7
7
|
import classicApi from "../api/classicApi";
|
|
8
|
+
|
|
8
9
|
interface AuthParam{
|
|
9
10
|
accessToken?: string;
|
|
10
11
|
refreshToken?: string;
|
|
11
12
|
expiration?: Date|string;
|
|
12
13
|
clientId?: string;
|
|
13
14
|
}
|
|
15
|
+
|
|
14
16
|
interface AuthState {
|
|
15
17
|
authReload: number;
|
|
16
18
|
authName: string;
|
|
@@ -231,3 +233,6 @@ export const useAuthStore = defineStore("AuthStore", {
|
|
|
231
233
|
},
|
|
232
234
|
},
|
|
233
235
|
});
|
|
236
|
+
|
|
237
|
+
/** Type for the AuthStore */
|
|
238
|
+
export type AuthStore = ReturnType<typeof useAuthStore>;
|
|
@@ -1,73 +1,128 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Rubriquage } from "@/stores/class/rubrique/rubriquage";
|
|
3
|
-
import { RubriquageFilter } from "@/stores/class/rubrique/rubriquageFilter";
|
|
4
|
-
import { Rubrique } from "@/stores/class/rubrique/rubrique";
|
|
5
|
-
import { defineStore } from "pinia";
|
|
1
|
+
import { computed, ref } from 'vue';
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
3
|
+
import { Category } from '@/stores/class/general/category';
|
|
4
|
+
import { Rubriquage } from '@/stores/class/rubrique/rubriquage';
|
|
5
|
+
import { RubriquageFilter } from '@/stores/class/rubrique/rubriquageFilter';
|
|
6
|
+
import { Rubrique } from '@/stores/class/rubrique/rubrique';
|
|
7
|
+
import { defineStore } from 'pinia';
|
|
8
|
+
import { useAuthStore } from './AuthStore';
|
|
9
|
+
import { useRoute } from 'vue-router';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Store managing data regarding the filters to apply to know which
|
|
13
|
+
* podcasts to show.
|
|
14
|
+
*/
|
|
15
|
+
export const useFilterStore = defineStore("FilterStore", () => {
|
|
16
|
+
const _filterOrgaId = ref<string|null>(null);
|
|
17
|
+
const filterImgUrl = ref<string>();
|
|
18
|
+
const filterName = ref<string>();
|
|
19
|
+
const filterRubriquage = ref<Array<Rubriquage>>([]);
|
|
20
|
+
const filterRubrique = ref<Array<RubriquageFilter>>([]);
|
|
21
|
+
const filterRubriqueDisplay = ref<Array<Rubrique>>([]);
|
|
22
|
+
const filterTypeMedia = ref<string>();
|
|
23
|
+
const filterSortOrder = ref<string>();
|
|
24
|
+
const filterSortField = ref<string>();
|
|
25
|
+
const filterLive = ref<boolean>(false);
|
|
26
|
+
const filterIab = ref<Category>();
|
|
27
|
+
|
|
28
|
+
const route = useRoute();
|
|
29
|
+
const authStore = useAuthStore();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* ID of the current organisation.
|
|
33
|
+
*/
|
|
34
|
+
const filterOrgaId = computed((): string|undefined => {
|
|
35
|
+
if (route?.query.displayAll === "true") {
|
|
36
|
+
return undefined;
|
|
37
|
+
} else if (route?.query.productor) {
|
|
38
|
+
return route.query.productor as string;
|
|
39
|
+
} else if(_filterOrgaId.value === null) {
|
|
40
|
+
return authStore.authOrgaId;
|
|
41
|
+
} else {
|
|
42
|
+
return _filterOrgaId.value ?? undefined;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The ID of the current organisation, regardless of other options.
|
|
48
|
+
* Use this if you want to know the organisation of the user even in
|
|
49
|
+
* unfocused mode (ie displayAll = true)
|
|
50
|
+
*/
|
|
51
|
+
const realOrgaId = computed(() => {
|
|
52
|
+
return _filterOrgaId.value ?? undefined;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
function filterUpdateOrga(filter: {
|
|
56
|
+
orgaId?: string;
|
|
57
|
+
imgUrl?: string;
|
|
58
|
+
name?: string;
|
|
59
|
+
rubriquageArray?: Array<Rubriquage>;
|
|
60
|
+
isLive?: boolean;
|
|
61
|
+
}) {
|
|
62
|
+
if (filter.imgUrl || !filter.orgaId) {
|
|
63
|
+
filterImgUrl.value = filter.imgUrl;
|
|
64
|
+
}
|
|
65
|
+
if (filter.name || !filter.orgaId) {
|
|
66
|
+
filterName.value = filter.name;
|
|
67
|
+
}
|
|
68
|
+
if (filter.rubriquageArray) {
|
|
69
|
+
filterRubriquage.value = filter.rubriquageArray;
|
|
70
|
+
}
|
|
71
|
+
filterLive.value = filter.isLive ?? false;
|
|
72
|
+
filterIab.value = undefined;
|
|
73
|
+
_filterOrgaId.value = filter.orgaId ?? null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function filterUpdateIab(iab?: Category) {
|
|
77
|
+
filterIab.value = iab;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function filterUpdateRubrique(rubriqueFilter: Array<RubriquageFilter>) {
|
|
81
|
+
filterRubrique.value = rubriqueFilter;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function filterUpdateRubriqueDisplay(rubriques: Array<Rubrique>) {
|
|
85
|
+
filterRubriqueDisplay.value = rubriques.filter(rubrique=> rubrique);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function filterUpdateMedia(filter: {
|
|
89
|
+
type?: string;
|
|
90
|
+
order?: string;
|
|
91
|
+
field?: string;
|
|
92
|
+
}) {
|
|
93
|
+
if (filter.type) {
|
|
94
|
+
filterTypeMedia.value = filter.type;
|
|
95
|
+
}
|
|
96
|
+
if (filter.order) {
|
|
97
|
+
filterSortOrder.value = filter.order;
|
|
98
|
+
}
|
|
99
|
+
if (filter.field) {
|
|
100
|
+
filterSortField.value = filter.field;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
filterOrgaId,
|
|
106
|
+
realOrgaId,
|
|
107
|
+
|
|
108
|
+
filterUpdateOrga,
|
|
109
|
+
filterUpdateIab,
|
|
110
|
+
filterUpdateRubrique,
|
|
111
|
+
filterUpdateRubriqueDisplay,
|
|
112
|
+
filterUpdateMedia,
|
|
113
|
+
|
|
114
|
+
filterImgUrl,
|
|
115
|
+
filterName,
|
|
116
|
+
filterRubriquage,
|
|
117
|
+
filterRubrique,
|
|
118
|
+
filterRubriqueDisplay,
|
|
119
|
+
filterTypeMedia,
|
|
120
|
+
filterSortOrder,
|
|
121
|
+
filterSortField,
|
|
122
|
+
filterLive,
|
|
123
|
+
filterIab
|
|
124
|
+
};
|
|
73
125
|
});
|
|
126
|
+
|
|
127
|
+
/** Type for the FilterStore */
|
|
128
|
+
export type FilterStore = ReturnType<typeof useFilterStore>;
|
|
@@ -141,6 +141,11 @@ export const usePlayerStore = defineStore("PlayerStore", {
|
|
|
141
141
|
},
|
|
142
142
|
},
|
|
143
143
|
actions: {
|
|
144
|
+
/**
|
|
145
|
+
* Start playing audio/video
|
|
146
|
+
* @param param The data
|
|
147
|
+
* @param isVideo If true, enable video mode
|
|
148
|
+
*/
|
|
144
149
|
async playerPlay(param?: any, isVideo = false) {
|
|
145
150
|
if (!param) {
|
|
146
151
|
this.playerCurrentChange = null;
|