Many fix and improvements
This commit is contained in:
89
package-lock.json
generated
89
package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^7.4.47",
|
"@mdi/font": "^7.4.47",
|
||||||
"@stripe/stripe-js": "^3.0.10",
|
"@stripe/stripe-js": "^3.0.10",
|
||||||
|
"@vueuse/core": "^10.11.0",
|
||||||
"@xtiannyeto/vue-auth-social": "^0.1.9",
|
"@xtiannyeto/vue-auth-social": "^0.1.9",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
@@ -889,6 +890,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz",
|
||||||
"integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA=="
|
"integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/web-bluetooth": {
|
||||||
|
"version": "0.0.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
||||||
|
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
|
||||||
|
},
|
||||||
"node_modules/@ungap/structured-clone": {
|
"node_modules/@ungap/structured-clone": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
|
||||||
@@ -1012,6 +1018,89 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.33.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.33.tgz",
|
||||||
"integrity": "sha512-aoRY0jQk3A/cuvdkodTrM4NMfxco8n55eG4H7ML/CRy7OryHfiqvug4xrCBBMbbN+dvXAetDDwZW9DXWWjBntA=="
|
"integrity": "sha512-aoRY0jQk3A/cuvdkodTrM4NMfxco8n55eG4H7ML/CRy7OryHfiqvug4xrCBBMbbN+dvXAetDDwZW9DXWWjBntA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@vueuse/core": {
|
||||||
|
"version": "10.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz",
|
||||||
|
"integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/web-bluetooth": "^0.0.20",
|
||||||
|
"@vueuse/metadata": "10.11.0",
|
||||||
|
"@vueuse/shared": "10.11.0",
|
||||||
|
"vue-demi": ">=0.14.8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/core/node_modules/vue-demi": {
|
||||||
|
"version": "0.14.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||||
|
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/metadata": {
|
||||||
|
"version": "10.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz",
|
||||||
|
"integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/shared": {
|
||||||
|
"version": "10.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.0.tgz",
|
||||||
|
"integrity": "sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==",
|
||||||
|
"dependencies": {
|
||||||
|
"vue-demi": ">=0.14.8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/shared/node_modules/vue-demi": {
|
||||||
|
"version": "0.14.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||||
|
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xtiannyeto/vue-auth-social": {
|
"node_modules/@xtiannyeto/vue-auth-social": {
|
||||||
"version": "0.1.9",
|
"version": "0.1.9",
|
||||||
"resolved": "https://registry.npmjs.org/@xtiannyeto/vue-auth-social/-/vue-auth-social-0.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@xtiannyeto/vue-auth-social/-/vue-auth-social-0.1.9.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^7.4.47",
|
"@mdi/font": "^7.4.47",
|
||||||
"@stripe/stripe-js": "^3.0.10",
|
"@stripe/stripe-js": "^3.0.10",
|
||||||
|
"@vueuse/core": "^10.11.0",
|
||||||
"@xtiannyeto/vue-auth-social": "^0.1.9",
|
"@xtiannyeto/vue-auth-social": "^0.1.9",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
34
src/App.vue
34
src/App.vue
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app v-if="isUserLoaded">
|
<v-app>
|
||||||
<div class="m-0 flex flex-column h-screen">
|
<div class="m-0 flex flex-column h-screen">
|
||||||
<Header @toggle-sidebar="toggleSidebar" class="fixed w-full z-50 top-0 p-2"></Header>
|
<Header class="fixed w-full z-50 top-0 p-2"></Header>
|
||||||
<div class="flex flex-row relative">
|
<div class="flex flex-row relative">
|
||||||
<div
|
<div
|
||||||
@mouseenter="openSidebar"
|
@mouseenter="openSidebar"
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
></div>
|
></div>
|
||||||
|
|
||||||
<transition name="slide-fade">
|
<transition name="slide-fade">
|
||||||
<div v-show="!hideSideBar"
|
<div v-show="sideBarStore.visible"
|
||||||
@mouseleave="startCloseSidebarTimer"
|
@mouseleave="startCloseSidebarTimer"
|
||||||
@mouseenter="clearCloseSidebarTimer"
|
@mouseenter="clearCloseSidebarTimer"
|
||||||
class=" fixed h-full min-w-60 border-r-2 bg-white z-30 transition-transform duration-700">
|
class=" fixed h-full min-w-60 border-r-2 bg-white z-30 transition-transform duration-700">
|
||||||
@@ -31,38 +31,24 @@
|
|||||||
import Header from "@/views/main/Header.vue";
|
import Header from "@/views/main/Header.vue";
|
||||||
import Footer from "@/views/main/Footer.vue";
|
import Footer from "@/views/main/Footer.vue";
|
||||||
import SideBar from "@/views/main/SideBar.vue";
|
import SideBar from "@/views/main/SideBar.vue";
|
||||||
import {ref, onMounted, onUnmounted, onBeforeMount} from 'vue';
|
import {ref, onMounted, onUnmounted} from 'vue';
|
||||||
import {eventBus} from '@/eventBus.js';
|
import {useSideBarStore} from "@/stores/sideBarStore.js";
|
||||||
import {useUserStore} from "@/stores/user.js";
|
|
||||||
import {useClient} from "@/plugins/api.js";
|
|
||||||
|
|
||||||
const hideSideBar = ref(true);
|
|
||||||
const isUserLoaded = ref(false);
|
|
||||||
const showPopup = ref(false);
|
const showPopup = ref(false);
|
||||||
const popup = ref(null);
|
const popup = ref(null);
|
||||||
const popupButton = ref(null);
|
const popupButton = ref(null);
|
||||||
let closeSidebarTimer = null;
|
let closeSidebarTimer = null;
|
||||||
|
|
||||||
let client = useClient();
|
const sideBarStore = useSideBarStore()
|
||||||
let userStore = useUserStore();
|
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
|
||||||
await userStore.setCurrentUser(client);
|
|
||||||
isUserLoaded.value = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const toggleSidebar = () => {
|
|
||||||
hideSideBar.value = !hideSideBar.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const openSidebar = () => {
|
const openSidebar = () => {
|
||||||
clearCloseSidebarTimer();
|
clearCloseSidebarTimer();
|
||||||
hideSideBar.value = false;
|
sideBarStore.show()
|
||||||
};
|
};
|
||||||
|
|
||||||
const startCloseSidebarTimer = () => {
|
const startCloseSidebarTimer = () => {
|
||||||
closeSidebarTimer = setTimeout(() => {
|
closeSidebarTimer = setTimeout(() => {
|
||||||
hideSideBar.value = true;
|
sideBarStore.hide()
|
||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,18 +69,16 @@ const handleClickOutside = (event) => {
|
|||||||
!event.target.closest('.w-48') &&
|
!event.target.closest('.w-48') &&
|
||||||
!event.target.closest('.v-app-bar-nav-icon')
|
!event.target.closest('.v-app-bar-nav-icon')
|
||||||
) {
|
) {
|
||||||
hideSideBar.value = true;
|
sideBarStore.hide()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('click', handleClickOutside);
|
document.addEventListener('click', handleClickOutside);
|
||||||
eventBus.value.toggleSidebar = toggleSidebar;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
document.removeEventListener('click', handleClickOutside);
|
document.removeEventListener('click', handleClickOutside);
|
||||||
eventBus.value.toggleSidebar = null;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import { ref } from 'vue';
|
|
||||||
|
|
||||||
export const eventBus = ref({});
|
|
||||||
@@ -8,7 +8,6 @@ import 'vuetify/styles'
|
|||||||
import {createVuetify} from 'vuetify'
|
import {createVuetify} from 'vuetify'
|
||||||
import * as components from 'vuetify/components'
|
import * as components from 'vuetify/components'
|
||||||
import * as directives from 'vuetify/directives'
|
import * as directives from 'vuetify/directives'
|
||||||
import clientPlugin from './plugins/api.js'
|
|
||||||
import vueGoogleOauth from 'vue3-google-login'
|
import vueGoogleOauth from 'vue3-google-login'
|
||||||
|
|
||||||
const vuetify = createVuetify({
|
const vuetify = createVuetify({
|
||||||
@@ -17,7 +16,6 @@ const vuetify = createVuetify({
|
|||||||
});
|
});
|
||||||
|
|
||||||
createApp(App)
|
createApp(App)
|
||||||
.use(clientPlugin)
|
|
||||||
.use(createPinia())
|
.use(createPinia())
|
||||||
.use(vuetify)
|
.use(vuetify)
|
||||||
.use(router)
|
.use(router)
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import UserTransactionsModel from "@/models/userTransactionsModel.js";
|
|
||||||
import SocialNetworksModel from "@/models/socialNetworksModel.js";
|
|
||||||
import ProfileColorsModel from "@/models/profileColorsModel.js";
|
|
||||||
import StoredDataUrlsModel from "@/models/storedDataUrlsModel.js";
|
|
||||||
|
|
||||||
export default class MyUserModel
|
|
||||||
{
|
|
||||||
id = "";
|
|
||||||
creatorAlias = "";
|
|
||||||
alias = null;
|
|
||||||
firstName = "";
|
|
||||||
lastName = "";
|
|
||||||
userName = "";
|
|
||||||
occupation = "";
|
|
||||||
email = "";
|
|
||||||
phoneNumber = "";
|
|
||||||
birthDate = "";
|
|
||||||
country = "";
|
|
||||||
city = "";
|
|
||||||
address = "";
|
|
||||||
about = "";
|
|
||||||
description = "";
|
|
||||||
socialNetworks = new SocialNetworksModel();
|
|
||||||
profileColors = new ProfileColorsModel();
|
|
||||||
storedDataUrls = new StoredDataUrlsModel();
|
|
||||||
totalBalance = "";
|
|
||||||
userTransactions = [];
|
|
||||||
|
|
||||||
static createFromApiResult(apiResult){
|
|
||||||
const userModel = Object.assign(new MyUserModel(), apiResult);
|
|
||||||
|
|
||||||
const notMapperTransaction = Object.freeze(userModel.userTransactions);
|
|
||||||
userModel.userTransactions = [];
|
|
||||||
|
|
||||||
for (const transaction of notMapperTransaction) {
|
|
||||||
userModel.userTransactions.push(UserTransactionsModel.createFromApiResult(transaction))
|
|
||||||
}
|
|
||||||
|
|
||||||
return userModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getDefaultUser(){
|
|
||||||
const defaultUser = new MyUserModel();
|
|
||||||
defaultUser.userName = "Anonyme"
|
|
||||||
|
|
||||||
return defaultUser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
export default class ProfileColorsModel
|
|
||||||
{
|
|
||||||
bannerTop = "";
|
|
||||||
bannerBottom = "";
|
|
||||||
accent = "";
|
|
||||||
menu = "";
|
|
||||||
|
|
||||||
static createFromApiResult(apiResult){
|
|
||||||
return Object.assign(new ProfileColorsModel(), apiResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
export default class SocialNetworksModel
|
|
||||||
{
|
|
||||||
facebookUrl = "";
|
|
||||||
instagramUrl = "";
|
|
||||||
xUrl = "";
|
|
||||||
linkedInUrl = "";
|
|
||||||
tikTokUrl = "";
|
|
||||||
youtubeUrl = "";
|
|
||||||
redditUrl = "";
|
|
||||||
yourWebsiteUrl = "";
|
|
||||||
|
|
||||||
static createFromApiResult(apiResult){
|
|
||||||
return Object.assign(new SocialNetworksModel(), apiResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
export default class StoredDataUrlsModel
|
|
||||||
{
|
|
||||||
bannerPictureUrl = null;
|
|
||||||
profilePictureUrl = null;
|
|
||||||
websiteIconUrl = null;
|
|
||||||
|
|
||||||
static createFromApiResult(apiResult){
|
|
||||||
return Object.assign(new StoredDataUrlsModel(), apiResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios"
|
||||||
import {inject} from "vue";
|
import {useAuthStore} from "@/stores/authStore.js"
|
||||||
|
|
||||||
const key = Symbol("api");
|
export function useClient() {
|
||||||
|
|
||||||
export default function(app) {
|
|
||||||
if (!import.meta.env.VITE_API_URL) throw new Error("VITE_API_URL is not provided")
|
if (!import.meta.env.VITE_API_URL) throw new Error("VITE_API_URL is not provided")
|
||||||
|
|
||||||
// You create a .env.development file and a .env file
|
// You create a .env.development file and a .env file
|
||||||
@@ -13,23 +11,16 @@ export default function(app) {
|
|||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
const requestInterceptor = (config) => {
|
const requestInterceptor = (config) => {
|
||||||
const token = localStorage.getItem("jwt");
|
if (authStore.isAuthenticated) {
|
||||||
if (token) config.headers["Authorization"] = `Bearer ${token}`;
|
config.headers["Authorization"] = `Bearer ${authStore.accessToken}`
|
||||||
return config;
|
}
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
api.interceptors.request.use(requestInterceptor);
|
api.interceptors.request.use(requestInterceptor);
|
||||||
|
|
||||||
// This is a local injection, to use it in your components you can do this:
|
|
||||||
// const api = inject("api")
|
|
||||||
// api.get("/some-endpoint")
|
|
||||||
app.provide(key, api)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useClient() {
|
|
||||||
const api = inject(key)
|
|
||||||
if (!api) throw new Error("api is not provided")
|
|
||||||
return api;
|
return api;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import { useUserStore } from "@/stores/user.js";
|
|
||||||
import GuillaumeAime from '@/views/manualusers/GuillaumeAime.vue'
|
import GuillaumeAime from '@/views/manualusers/GuillaumeAime.vue'
|
||||||
import About from '@/views/documentation/About.vue'
|
import About from '@/views/documentation/About.vue'
|
||||||
import ContentPolicy from '@/views/documentation/ContentPolicy.vue'
|
import ContentPolicy from '@/views/documentation/ContentPolicy.vue'
|
||||||
@@ -22,6 +21,7 @@ import CreatorList from '../views/creators/CreatorList.vue'
|
|||||||
import CreatorPage from "@/views/creators/CreatorPage.vue";
|
import CreatorPage from "@/views/creators/CreatorPage.vue";
|
||||||
import ContentPage from "@/views/contents/ContentPage.vue";
|
import ContentPage from "@/views/contents/ContentPage.vue";
|
||||||
import PostContent from "@/views/contents/PostContent.vue";
|
import PostContent from "@/views/contents/PostContent.vue";
|
||||||
|
import {useAuthStore} from "@/stores/authStore.js";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@@ -114,18 +114,6 @@ const routes = [
|
|||||||
component: Join,
|
component: Join,
|
||||||
meta: { hideSideBar: true }
|
meta: { hideSideBar: true }
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
path: '/paymentcompleted',
|
|
||||||
name: 'PayementCompleted',
|
|
||||||
component: PaymentCompleted
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/profile',
|
|
||||||
name: 'profile',
|
|
||||||
component: Profile,
|
|
||||||
meta: { requiresAuth: true }
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/signup',
|
path: '/signup',
|
||||||
name: 'signup',
|
name: 'signup',
|
||||||
@@ -136,12 +124,23 @@ const routes = [
|
|||||||
name: 'login',
|
name: 'login',
|
||||||
component: LoginView
|
component: LoginView
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/paymentcompleted',
|
||||||
|
name: 'PayementCompleted',
|
||||||
|
component: PaymentCompleted,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/wallet',
|
path: '/wallet',
|
||||||
name: 'wallet',
|
name: 'wallet',
|
||||||
component: Wallet,
|
component: Wallet,
|
||||||
meta: { requiresAuth: true }
|
meta: { requiresAuth: true }
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
path: '/profile',
|
||||||
|
name: 'profile',
|
||||||
|
component: Profile,
|
||||||
|
meta: { requiresAuth: true }
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
@@ -151,11 +150,10 @@ const router = createRouter({
|
|||||||
|
|
||||||
// Navigation gards
|
// Navigation gards
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
const authStore = useUserStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
|
||||||
if (to.matched.some(record => record.meta.requiresAuth)) {
|
if (to.matched.some(record => record.meta.requiresAuth)) {
|
||||||
if (!authStore.user.value || !Object.keys(authStore.user.value).length) {
|
if (!authStore.isAuthenticated) {
|
||||||
next('/');
|
next('/');
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import { defineStore } from 'pinia';
|
|
||||||
|
|
||||||
const baseUrl = '/api';
|
|
||||||
|
|
||||||
export const auth = defineStore({
|
|
||||||
id: 'auth',
|
|
||||||
|
|
||||||
state: () => ({
|
|
||||||
user: "",
|
|
||||||
refreshTokenTimeout: 0
|
|
||||||
}),
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
// TODO: Fix login methods
|
|
||||||
|
|
||||||
async login(client, email, password) {
|
|
||||||
const requestBody = {
|
|
||||||
email: email,
|
|
||||||
password: password
|
|
||||||
};
|
|
||||||
const response = await client.post(`${baseUrl}/users/login`, requestBody)
|
|
||||||
this.user = {
|
|
||||||
accessToken: response.data.accessToken,
|
|
||||||
refreshToken: response.data.refreshToken,
|
|
||||||
}
|
|
||||||
localStorage.setItem('jwt', this.user.accessToken);
|
|
||||||
|
|
||||||
this.startRefreshTokenTimer();
|
|
||||||
},
|
|
||||||
|
|
||||||
async loginGoogle(client, accessToken) {
|
|
||||||
const response = await client.post(`${baseUrl}/google/sign-in`, {accessToken: accessToken})
|
|
||||||
this.user = {
|
|
||||||
accessToken: response.data.accessToken,
|
|
||||||
refreshToken: response.data.refreshToken,
|
|
||||||
email: response.data.email
|
|
||||||
}
|
|
||||||
localStorage.setItem('jwt', this.user.accessToken);
|
|
||||||
|
|
||||||
this.startRefreshTokenTimer();
|
|
||||||
},
|
|
||||||
|
|
||||||
logout() {
|
|
||||||
localStorage.setItem('jwt', '');
|
|
||||||
this.user = null;
|
|
||||||
|
|
||||||
this.stopRefreshTokenTimer();
|
|
||||||
},
|
|
||||||
|
|
||||||
async refreshToken(client) {
|
|
||||||
const response = await client.post(`${baseUrl}/users/refresh`);
|
|
||||||
this.user.accessToken = response.accessToken;
|
|
||||||
localStorage.setItem('jwt', this.user.accessToken);
|
|
||||||
|
|
||||||
this.startRefreshTokenTimer();
|
|
||||||
},
|
|
||||||
|
|
||||||
startRefreshTokenTimer() {
|
|
||||||
const timeout = 50 * 1000;
|
|
||||||
this.refreshTokenTimeout = setTimeout(this.refreshToken, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
stopRefreshTokenTimer() {
|
|
||||||
clearTimeout(this.refreshTokenTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
84
src/stores/authStore.js
Normal file
84
src/stores/authStore.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import {defineStore} from 'pinia';
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {useClient} from "@/plugins/api.js";
|
||||||
|
import {useSessionStorage} from "@vueuse/core";
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore(
|
||||||
|
'auth',
|
||||||
|
() => {
|
||||||
|
const clientApi = useClient()
|
||||||
|
const router = useRouter()
|
||||||
|
const accessToken = useSessionStorage('auth-accessToken', undefined)
|
||||||
|
const refreshToken = useSessionStorage('auth-refreshToken', undefined)
|
||||||
|
const isAuthenticated = computed(() => !!accessToken.value)
|
||||||
|
|
||||||
|
function updateTokens(data) {
|
||||||
|
accessToken.value = data.accessToken
|
||||||
|
refreshToken.value = data.refreshToken
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanTokens() {
|
||||||
|
updateTokens({
|
||||||
|
accessToken: undefined,
|
||||||
|
refreshToken: undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
cleanTokens()
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(email, password) {
|
||||||
|
try {
|
||||||
|
const response = await clientApi.post(
|
||||||
|
'api/users/login',
|
||||||
|
{
|
||||||
|
email: email,
|
||||||
|
password: password
|
||||||
|
})
|
||||||
|
updateTokens(response.data)
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
cleanTokens()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loginGoogle(accessToken) {
|
||||||
|
try {
|
||||||
|
const response = await clientApi.post(
|
||||||
|
'api/google/sign-in',
|
||||||
|
{
|
||||||
|
accessToken: accessToken
|
||||||
|
})
|
||||||
|
updateTokens(response.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
cleanTokens()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
try {
|
||||||
|
const response = await clientApi.post(
|
||||||
|
'api/users/refresh',
|
||||||
|
{
|
||||||
|
refreshToken: refreshToken
|
||||||
|
});
|
||||||
|
|
||||||
|
updateTokens({
|
||||||
|
accessToken: response.accessToken,
|
||||||
|
refreshToken: refreshToken
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
cleanTokens()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {accessToken, refreshToken, isAuthenticated, login, loginGoogle, logout}
|
||||||
|
})
|
||||||
|
|
||||||
23
src/stores/sideBarStore.js
Normal file
23
src/stores/sideBarStore.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import {computed, ref} from 'vue';
|
||||||
|
import {defineStore} from "pinia";
|
||||||
|
|
||||||
|
export const useSideBarStore = defineStore(
|
||||||
|
'sideBar',
|
||||||
|
() => {
|
||||||
|
const state = ref(false)
|
||||||
|
const visible = computed(() => state.value)
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
state.value = !state.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
state.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
state.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return {visible, toggle, show, hide}
|
||||||
|
})
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import { ref } from 'vue'
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import MyUserModel from "@/models/myUserModel.js";
|
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', () => {
|
|
||||||
const user = ref({});
|
|
||||||
const hasChanged = ref(false);
|
|
||||||
|
|
||||||
function getCurrentUser() {
|
|
||||||
return this.user.value;
|
|
||||||
}
|
|
||||||
async function setCurrentUser(client) {
|
|
||||||
try {
|
|
||||||
const myUser = await client.get("/api/GetMyUser");
|
|
||||||
this.user.value = MyUserModel.createFromApiResult(myUser.data);
|
|
||||||
this.hasChanged = false;
|
|
||||||
} catch (e){
|
|
||||||
this.user.value = MyUserModel.getDefaultUser();
|
|
||||||
console.log("User not logged.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateCurrentUser(client, myUserModel, profilePicture, bannerPicture, websiteIcon) {
|
|
||||||
await client.patch("/api/UpdateMyUser/profile", myUserModel)
|
|
||||||
|
|
||||||
if (typeof myUserModel.storedDataUrls.profilePictureUrl !== "object") {
|
|
||||||
const haveNewProfilePicture = profilePicture !== null && profilePicture.size !== 0;
|
|
||||||
const updateProfilePictureEndpoint = haveNewProfilePicture ? `/api/UpdateMyUser/profile-picture` : `/api/UpdateMyUser/profile-picture?url=${myUserModel.storedDataUrls.profilePictureUrl}`;
|
|
||||||
const response = await client.post(updateProfilePictureEndpoint, profilePicture, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': profilePicture?.type ?? "application/octet-stream",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (haveNewProfilePicture) {
|
|
||||||
this.user.value.storedDataUrls.profilePictureUrl = response.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof myUserModel.storedDataUrls.bannerPictureUrl !== "object") {
|
|
||||||
const haveNewBannerPicture = bannerPicture !== null && bannerPicture.size !== 0;
|
|
||||||
|
|
||||||
const updateBannerPictureEndpoint = haveNewBannerPicture ? `/api/UpdateMyUser/banner-picture` : `/api/UpdateMyUser/banner-picture?url=${myUserModel.storedDataUrls.bannerPictureUrl}`;
|
|
||||||
const response = await client.post(updateBannerPictureEndpoint, bannerPicture, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': bannerPicture?.type ?? "octet-stream",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (haveNewBannerPicture) {
|
|
||||||
this.user.value.storedDataUrls.bannerPictureUrl = response.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof myUserModel.storedDataUrls.websiteIconUrl !== "object") {
|
|
||||||
const haveNewWebsiteIcon = websiteIcon !== null && websiteIcon.size !== 0;
|
|
||||||
|
|
||||||
const updateWebsiteIconEndpoint = haveNewWebsiteIcon ? `/api/UpdateMyUser/website-icon` : `/api/UpdateMyUser/website-icon?url=${myUserModel.storedDataUrls.websiteIconUrl}`;
|
|
||||||
const response = await client.post(updateWebsiteIconEndpoint, websiteIcon, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': websiteIcon?.type ?? "application/octet-stream",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (haveNewWebsiteIcon) {
|
|
||||||
this.user.value.storedDataUrls.websiteIconUrl = response.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.user.value = myUserModel;
|
|
||||||
this.hasChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { user, getCurrentUser, setCurrentUser, updateCurrentUser }
|
|
||||||
})
|
|
||||||
77
src/stores/userStore.js
Normal file
77
src/stores/userStore.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import {computed, watch} from 'vue'
|
||||||
|
import {defineStore} from 'pinia'
|
||||||
|
import {useAuthStore} from "@/stores/authStore.js";
|
||||||
|
import {useClient} from "@/plugins/api.js";
|
||||||
|
import {useSessionStorage} from "@vueuse/core";
|
||||||
|
|
||||||
|
export const useUserStore = defineStore(
|
||||||
|
'user',
|
||||||
|
() => {
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const authWatcher = watch(
|
||||||
|
() => authStore.isAuthenticated,
|
||||||
|
async (newValue, oldValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
await fetchCurrentUserProfile()
|
||||||
|
} else {
|
||||||
|
user.value = undefined
|
||||||
|
creator.value = undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = useSessionStorage('user-user', {}, {writeDefaults: false})
|
||||||
|
const creator = useSessionStorage('user-creator', {}, {writeDefaults: false})
|
||||||
|
|
||||||
|
const alias = computed(() => {
|
||||||
|
if (user.value) {
|
||||||
|
return user.value.alias || `${user.value.firstName || ''} ${user.value.lastName || ''}`.trim() || 'Anonyme'
|
||||||
|
}
|
||||||
|
return 'Anonyme';
|
||||||
|
})
|
||||||
|
const portraitUrl = computed(() => {
|
||||||
|
return user.value && user.value.portraitUrl
|
||||||
|
? user.value.portraitUrl
|
||||||
|
: '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png'
|
||||||
|
})
|
||||||
|
|
||||||
|
async function fetchCurrentUserProfile() {
|
||||||
|
try {
|
||||||
|
const client = useClient()
|
||||||
|
const userResponse = await client.get("/api/GetMyUser");
|
||||||
|
user.value = userResponse.data
|
||||||
|
|
||||||
|
try {
|
||||||
|
const creatorId = userResponse.data.id
|
||||||
|
const creatorResponse = await client.get(`/api/creators/${creatorId}`)
|
||||||
|
creator.value = creatorResponse.data
|
||||||
|
} catch (error) {
|
||||||
|
creator.value = undefined
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
user.value = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateCurrentUser(userModel, profilePicture) {
|
||||||
|
const client = useClient()
|
||||||
|
await client.patch("/api/UpdateMyUser/profile", userModel)
|
||||||
|
|
||||||
|
if (typeof userModel.storedDataUrls.profilePictureUrl !== "object") {
|
||||||
|
const haveNewProfilePicture = profilePicture !== null && profilePicture.size !== 0;
|
||||||
|
const updateProfilePictureEndpoint = haveNewProfilePicture ? `/api/UpdateMyUser/profile-picture` : `/api/UpdateMyUser/profile-picture?url=${userModel.storedDataUrls.profilePictureUrl}`;
|
||||||
|
const response = await client.post(updateProfilePictureEndpoint, profilePicture, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': profilePicture?.type ?? "application/octet-stream",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (haveNewProfilePicture) {
|
||||||
|
this.user.value.portraitUrl = response.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.user.value = userModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {user, creator, alias, portraitUrl}
|
||||||
|
})
|
||||||
@@ -2,107 +2,107 @@
|
|||||||
<div class="hidden sm:block" style="height: 40px"></div>
|
<div class="hidden sm:block" style="height: 40px"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex flex-col lg:flex-row items-center justify-center">
|
<div class="flex flex-col lg:flex-row items-center justify-center">
|
||||||
<div class="max-w-[700px] min-w-[300px]">
|
<div class="max-w-[700px] min-w-[300px]">
|
||||||
<img class="rounded-none sm:rounded-2xl sm:w-full mr-8" src="/images/hutopymedia/loginpage/loginhutopy.png" alt="hutopy login">
|
<img class="rounded-none sm:rounded-2xl sm:w-full mr-8" src="/images/hutopymedia/loginpage/loginhutopy.png"
|
||||||
</div>
|
alt="hutopy login">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col items-center min-w-[300px] m-12">
|
<div class="flex flex-col items-center min-w-[300px] m-12">
|
||||||
<h1 class="text-center text-2xl font-bold mb-5">Connexion</h1>
|
<h1 class="text-center text-2xl font-bold mb-5">Connexion</h1>
|
||||||
|
|
||||||
<google-login class="w-full" :callback="googleCallback" popup-type="TOKEN">
|
<google-login class="w-full" :callback="googleCallback" popup-type="TOKEN">
|
||||||
<template #default>
|
<template #default>
|
||||||
<v-btn density="comfortable" class="mb-2 w-full">
|
<v-btn density="comfortable" class="mb-2 w-full">
|
||||||
<v-icon left>mdi-google</v-icon>
|
<v-icon left>mdi-google</v-icon>
|
||||||
Google
|
Google
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</google-login>
|
||||||
|
|
||||||
|
<!-- <v-btn density="comfortable" class="mb-2 w-full">-->
|
||||||
|
<!-- <v-icon left>mdi-facebook</v-icon>-->
|
||||||
|
<!-- Facebook-->
|
||||||
|
<!-- </v-btn>-->
|
||||||
|
|
||||||
|
<div class="w-full h-0.5 mt-4 mb-4" :style="{ backgroundColor: '#A30E79' }"></div>
|
||||||
|
|
||||||
|
<v-btn density="comfortable" class="mb-2 w-full" @click="showEmailForm = !showEmailForm">
|
||||||
|
<v-icon left>mdi-account</v-icon>
|
||||||
|
Utilisateur
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<div v-if="showEmailForm" class="w-full mt-2">
|
||||||
|
<v-text-field v-model="email"
|
||||||
|
label="Courriel"
|
||||||
|
variant="outlined"
|
||||||
|
dense
|
||||||
|
prepend-inner-icon="mdi-email"
|
||||||
|
color="transparent"
|
||||||
|
class="text-black"
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-text-field v-model="password"
|
||||||
|
label="Mot de passe"
|
||||||
|
:type="showPassword ? 'text' : 'password'"
|
||||||
|
variant="outlined"
|
||||||
|
dense
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
|
append-inner-icon="mdi-eye"
|
||||||
|
@click:append-inner="showPassword = !showPassword"
|
||||||
|
color="transparent"
|
||||||
|
class="text-black"
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-btn class="w-full text-center text-white" :style="{ backgroundColor: '#A30E79' }" @click="login">
|
||||||
|
Connecter
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
|
||||||
</google-login>
|
|
||||||
|
|
||||||
<!-- <v-btn density="comfortable" class="mb-2 w-full">-->
|
<p class="mt-4 text-sm text-center">
|
||||||
<!-- <v-icon left>mdi-facebook</v-icon>-->
|
Si vous n'avez pas de compte, <a href="/register" class="text-blue-500">cliquez ici</a> pour en créer un.
|
||||||
<!-- Facebook-->
|
</p>
|
||||||
<!-- </v-btn>-->
|
|
||||||
|
|
||||||
<div class="w-full h-0.5 mt-4 mb-4" :style="{ backgroundColor: '#A30E79' }"></div>
|
|
||||||
|
|
||||||
<v-btn density="comfortable" class="mb-2 w-full" @click="showEmailForm = !showEmailForm">
|
<div v-if="errorSnackBar" class="mb-4 text-red-600">
|
||||||
<v-icon left>mdi-account</v-icon>
|
Nom d'utilisateur ou mot de passe invalide.
|
||||||
Utilisateur
|
<button class="text-red-600 ml-4" @click="errorSnackBar = false">Fermer</button>
|
||||||
</v-btn>
|
</div>
|
||||||
|
|
||||||
<div v-if="showEmailForm" class="w-full mt-2">
|
|
||||||
<v-text-field v-model="user.email"
|
|
||||||
label="Courriel"
|
|
||||||
variant="outlined"
|
|
||||||
dense
|
|
||||||
prepend-inner-icon="mdi-email"
|
|
||||||
color="transparent"
|
|
||||||
class="text-black"
|
|
||||||
></v-text-field>
|
|
||||||
|
|
||||||
<v-text-field v-model="user.password"
|
|
||||||
label="Mot de passe"
|
|
||||||
:type="showPassword ? 'text' : 'password'"
|
|
||||||
variant="outlined"
|
|
||||||
dense
|
|
||||||
prepend-inner-icon="mdi-lock"
|
|
||||||
append-inner-icon="mdi-eye"
|
|
||||||
@click:append-inner="showPassword = !showPassword"
|
|
||||||
color="transparent"
|
|
||||||
class="text-black"
|
|
||||||
></v-text-field>
|
|
||||||
|
|
||||||
<v-btn class="w-full text-center text-white" :style="{ backgroundColor: '#A30E79' }" @click="login">Connecter</v-btn>
|
|
||||||
|
|
||||||
<p class="mt-4 text-sm text-center">
|
|
||||||
Si vous n'avez pas de compte, <a href="/register" class="text-blue-500">cliquez ici</a> pour en créer un.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div v-if="errorSnackBar" class="mb-4 text-red-600">
|
|
||||||
Nom d'utilisateur ou mot de passe invalide.
|
|
||||||
<button class="text-red-600 ml-4" @click="errorSnackBar = false">Fermer</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<selected-footer></selected-footer>
|
<selected-footer></selected-footer>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import {ref} from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import {useRouter} from 'vue-router';
|
||||||
import { useClient } from "@/plugins/api.js";
|
import {useAuthStore} from '@/stores/authStore.js';
|
||||||
import { auth } from '@/stores/auth.js';
|
import {GoogleLogin} from "vue3-google-login";
|
||||||
import { GoogleLogin } from "vue3-google-login";
|
|
||||||
// import { FacebookAuth } from '@xtiannyeto/vue-auth-social';
|
// import { FacebookAuth } from '@xtiannyeto/vue-auth-social';
|
||||||
import SelectedFooter from "@/views/main/SelectedFooter.vue";
|
import SelectedFooter from "@/views/main/SelectedFooter.vue";
|
||||||
|
|
||||||
const api = useClient();
|
const authStore = useAuthStore();
|
||||||
const store = auth();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const user = ref({});
|
const email = ref("");
|
||||||
|
const password = ref("");
|
||||||
|
|
||||||
const errorSnackBar = ref(false);
|
const errorSnackBar = ref(false);
|
||||||
const showEmailForm = ref(false);
|
const showEmailForm = ref(false);
|
||||||
const showPassword = ref(false);
|
const showPassword = ref(false);
|
||||||
|
|
||||||
async function login() {
|
async function login() {
|
||||||
try {
|
const result = await authStore.login(email.value, password.value);
|
||||||
await store.login(api, user.value.email, user.value.password);
|
if (result === true) {
|
||||||
await router.push('/');
|
await router.push('/')
|
||||||
window.location.reload();
|
|
||||||
} catch (error) {
|
|
||||||
errorSnackBar.value = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const googleCallback = async (response) => {
|
const googleCallback = async (response) => {
|
||||||
await store.loginGoogle(api, response["access_token"]);
|
await authStore.loginGoogle(response["access_token"]);
|
||||||
await router.push("/");
|
await router.push("/");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,4 +110,5 @@ const googleCallback = async (response) => {
|
|||||||
// const facebookCallback = (response) => {
|
// const facebookCallback = (response) => {
|
||||||
// console.log("User Successfully Logged In", response);
|
// console.log("User Successfully Logged In", response);
|
||||||
// };
|
// };
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
<button
|
<button
|
||||||
@click="openModal('ModalTikTok')"
|
@click="openModal('ModalTikTok')"
|
||||||
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full ">
|
class="HoverBtn active:bg-gray-300 py-2 px-4 border-gray-400 shadow flex items-center transition duration-200 ease-in-out w-full ">
|
||||||
<span class="flex-none pa-2 min-w-32 text-left"> <img src="/images/hutopymedia/icons/black/tiktokblack.png" class="w-5 h-5" ></span>
|
<span class="flex-none pa-2 min-w-32 text-left"> <img src="/images/externals/tiktok-black.png" class="w-5 h-5" ></span>
|
||||||
<span class="flex-auto text-left pr-6">TikTok</span>
|
<span class="flex-auto text-left pr-6">TikTok</span>
|
||||||
<span class="flex-none">
|
<span class="flex-none">
|
||||||
<v-icon>mdi-chevron-right</v-icon>
|
<v-icon>mdi-chevron-right</v-icon>
|
||||||
|
|||||||
@@ -1,45 +1,86 @@
|
|||||||
<template>
|
<script setup>
|
||||||
|
|
||||||
|
import {computed, reactive, onMounted} from 'vue';
|
||||||
|
import {time_ago} from "@/internal_time_ago.js";
|
||||||
|
import MessageList from "@/views/messages/MessageList.vue";
|
||||||
|
import PostMessage from "@/views/messages/PostMessage.vue";
|
||||||
|
|
||||||
|
import YoutubePlayer from './YoutubePlayer.vue';
|
||||||
|
import ImageViewer from './ImageViewer.vue';
|
||||||
|
// import VimeoPlayer from '@/components/VimeoPlayer.vue';
|
||||||
|
// import AudioPlayer from '@/components/AudioPlayer.vue';
|
||||||
|
// import BlogViewer from '@/components/BlogViewer.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
content: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasUrls = computed(() => !!props.content.urls && props.content.urls.length > 0);
|
||||||
|
|
||||||
|
|
||||||
|
const messages = reactive([]);
|
||||||
|
|
||||||
|
function addMessage(newMessage) {
|
||||||
|
messages.unshift(newMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComponent(url) {
|
||||||
|
if (url.includes('youtube.com') || url.includes('youtu.be')) {
|
||||||
|
return YoutubePlayer;
|
||||||
|
// } else if (url.includes('vimeo.com')) {
|
||||||
|
// return VimeoPlayer;
|
||||||
|
} else if (url.match(/\.(jpeg|jpg|gif|png)$/)) {
|
||||||
|
return ImageViewer;
|
||||||
|
// } else if (url.match(/\.(mp3|wav|ogg)$/)) {
|
||||||
|
// return 'AudioPlayer';
|
||||||
|
// } else {
|
||||||
|
// return 'BlogViewer';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
<div class="shadow-md rounded-lg bg-gray-50">
|
<div class="shadow-md rounded-lg bg-gray-50">
|
||||||
|
|
||||||
<div class="text-lg font-bold">
|
<v-card
|
||||||
{{ props.content.title }}
|
outlined
|
||||||
<span class="text-md-caption">
|
tile
|
||||||
{{ time_ago(props.content.createdAt) }}
|
>
|
||||||
|
<v-card-title>
|
||||||
|
{{ props.content.title }}
|
||||||
|
<span class="text-subtitle-2">
|
||||||
|
{{ time_ago(props.content.createdAt) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</v-card-title>
|
||||||
|
|
||||||
<div v-if="props.content.url !== null || props.content.uri !== null"
|
<v-carousel v-if="hasUrls">
|
||||||
class="h-48 object-cover bg-gray-300 rounded-md">
|
|
||||||
|
|
||||||
<v-img :src="props.content.url"
|
<v-carousel-item
|
||||||
v-if="!isHttpUrl">
|
v-for="url in props.content.urls"
|
||||||
</v-img>
|
:key="url"
|
||||||
|
>
|
||||||
|
<component :is="getComponent(url)"
|
||||||
|
:src="url"
|
||||||
|
></component>
|
||||||
|
</v-carousel-item>
|
||||||
|
|
||||||
<iframe v-if="isHttpUrl"
|
</v-carousel>
|
||||||
:src="props?.content?.uri"
|
|
||||||
title="YouTube video player"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
||||||
referrerpolicy="strict-origin-when-cross-origin"
|
|
||||||
allowfullscreen>
|
|
||||||
</iframe>
|
|
||||||
|
|
||||||
</div>
|
<v-card-text>
|
||||||
|
{{ props.content.description }}
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions>
|
||||||
<div class="flex flex-row">
|
<router-link :to="'content/' + content.id">
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="text-sm text-gray-500">{{ props.content.description }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<router-link :to="'content/' + props?.content?.id">
|
|
||||||
<div class="bg-blue-500 rounded-lg py-1 px-2">Plus ...</div>
|
<div class="bg-blue-500 rounded-lg py-1 px-2">Plus ...</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</v-card-actions>
|
||||||
|
|
||||||
</div>
|
</v-card>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
@@ -58,28 +99,4 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
|
||||||
|
|
||||||
import {computed, reactive} from 'vue';
|
|
||||||
import {time_ago} from "@/internal_time_ago.js";
|
|
||||||
import MessageList from "@/views/messages/MessageList.vue";
|
|
||||||
import PostMessage from "@/views/messages/PostMessage.vue";
|
|
||||||
|
|
||||||
const isHttpUrl = computed(() => props.content?.uri?.startsWith('http'))
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
content: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const messages = reactive([]);
|
|
||||||
|
|
||||||
function addMessage(newMessage) {
|
|
||||||
messages.unshift(newMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
@@ -48,7 +48,6 @@ async function load({done}) {
|
|||||||
let uri = `/api/contents/user/${props.creatorId}?page_size=${page_size}`
|
let uri = `/api/contents/user/${props.creatorId}?page_size=${page_size}`
|
||||||
if (last_id !== null) uri = uri + `&last_id=${last_id}`
|
if (last_id !== null) uri = uri + `&last_id=${last_id}`
|
||||||
|
|
||||||
console.log(`Fetching content at: ${uri}`)
|
|
||||||
const response = await client.get(uri)
|
const response = await client.get(uri)
|
||||||
|
|
||||||
if (response.status >= 200 && response.status < 300) {
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
|||||||
@@ -1,72 +1,8 @@
|
|||||||
<template>
|
<script setup>
|
||||||
|
|
||||||
<div v-if="creator.id === userStore.getCurrentUser().id">
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="flex items-center text-white transform transition-transform duration-200 hover:text-gray-300 hover:scale-125 px-4"
|
|
||||||
@click="isDialogActive = true">
|
|
||||||
<v-icon style="font-size: 35px; height: 35px; width: 55px;">mdi-text-box-plus-outline</v-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<v-dialog v-model="isDialogActive" max-width="500">
|
|
||||||
|
|
||||||
<v-card class="text-center rounded-xl">
|
|
||||||
|
|
||||||
<v-card-title class="text-white p-4 rounded-t"
|
|
||||||
:style="{backgroundColor: creator.profileColors.menu || '#A30E79'}">
|
|
||||||
Quicky
|
|
||||||
</v-card-title>
|
|
||||||
|
|
||||||
|
|
||||||
<v-card-text>
|
|
||||||
|
|
||||||
<v-text-field v-model="title"
|
|
||||||
class="p-2"
|
|
||||||
label="Titre"
|
|
||||||
density="comfortable"
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
clearable>
|
|
||||||
</v-text-field>
|
|
||||||
|
|
||||||
<v-textarea v-model="message"
|
|
||||||
label="Écrivez votre message ici..."
|
|
||||||
class="p-2"
|
|
||||||
density="comfortable"
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
clearable
|
|
||||||
outlined>
|
|
||||||
</v-textarea>
|
|
||||||
|
|
||||||
<v-file-input v-model="files"
|
|
||||||
label="Ajoutez des fichiers"
|
|
||||||
class="p-2"
|
|
||||||
outlined
|
|
||||||
multiple
|
|
||||||
dropzone
|
|
||||||
placeholder="Glissez et déposez des fichiers ici ou cliquez pour sélectionner des fichiers"
|
|
||||||
></v-file-input>
|
|
||||||
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions class="justify-end">
|
|
||||||
<v-btn color="secondary" @click="cancelPost">Annuler</v-btn>
|
|
||||||
<v-btn color="primary" @click="publishPost">Publier</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
|
|
||||||
</v-card>
|
|
||||||
|
|
||||||
</v-dialog>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import {useClient} from '@/plugins/api.js';
|
import {useClient} from '@/plugins/api.js';
|
||||||
import {ref} from 'vue';
|
import {ref} from 'vue';
|
||||||
import {useUserStore} from '@/stores/user.js';
|
import {useUserStore} from '@/stores/userStore.js';
|
||||||
|
import {v7} from 'uuid'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
creator: {type: Object, required: true},
|
creator: {type: Object, required: true},
|
||||||
@@ -75,22 +11,34 @@ const props = defineProps({
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const isDialogActive = ref(false);
|
const isDialogActive = ref(false);
|
||||||
const title = ref('');
|
|
||||||
const message = ref('');
|
|
||||||
const files = ref([]);
|
|
||||||
|
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
|
const title = ref('');
|
||||||
|
const message = ref('');
|
||||||
|
const files = ref([])
|
||||||
|
|
||||||
const publishPost = async () => {
|
async function publishPost() {
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('id', v7());
|
||||||
|
formData.append('creatorId', userStore.user.id);
|
||||||
|
formData.append('title', title.value);
|
||||||
|
formData.append('description', message.value);
|
||||||
|
files.value.forEach(file => {
|
||||||
|
formData.append('files', file);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.post(
|
const response = await client.post(
|
||||||
`/api/contents/`,
|
`/api/contents/`,
|
||||||
|
formData,
|
||||||
{
|
{
|
||||||
"title": title.value,
|
headers: {
|
||||||
"description": message.value
|
'Content-Type': 'multipart/form-data',
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log('Files uploaded successfully:', response.data);
|
||||||
closeDialog()
|
closeDialog()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -110,3 +58,69 @@ const closeDialog = () => {
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="creator.id === userStore.user.id"
|
||||||
|
class="flex items-center text-white transform transition-transform duration-200 hover:text-gray-300 hover:scale-125 px-4"
|
||||||
|
@click="isDialogActive = true">
|
||||||
|
<v-icon style="font-size: 35px; height: 35px; width: 55px;">mdi-text-box-plus-outline</v-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<v-dialog v-model="isDialogActive"
|
||||||
|
max-width="500">
|
||||||
|
|
||||||
|
<v-form>
|
||||||
|
|
||||||
|
<v-card class="text-center rounded-xl">
|
||||||
|
|
||||||
|
<v-card-title class="text-white p-4 rounded-t"
|
||||||
|
:style="{backgroundColor: creator.profileColors.menu || '#A30E79'}">
|
||||||
|
Quicky
|
||||||
|
</v-card-title>
|
||||||
|
|
||||||
|
<v-card-text>
|
||||||
|
|
||||||
|
<v-text-field v-model="title"
|
||||||
|
class="p-2"
|
||||||
|
label="Titre"
|
||||||
|
density="comfortable"
|
||||||
|
variant="outlined"
|
||||||
|
hide-details
|
||||||
|
clearable
|
||||||
|
></v-text-field>
|
||||||
|
|
||||||
|
<v-textarea v-model="message"
|
||||||
|
label="Écrivez votre message ici..."
|
||||||
|
class="p-2"
|
||||||
|
density="comfortable"
|
||||||
|
variant="outlined"
|
||||||
|
hide-details
|
||||||
|
clearable
|
||||||
|
outlined
|
||||||
|
></v-textarea>
|
||||||
|
|
||||||
|
<v-file-input v-model="files"
|
||||||
|
label="Ajoutez des fichiers"
|
||||||
|
class="p-2"
|
||||||
|
outlined
|
||||||
|
multiple
|
||||||
|
dropzone
|
||||||
|
placeholder="Glissez et déposez des fichiers ici ou cliquez pour sélectionner des fichiers"
|
||||||
|
></v-file-input>
|
||||||
|
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions class="justify-end">
|
||||||
|
<v-btn color="secondary" @click="cancelPost">Annuler</v-btn>
|
||||||
|
<v-btn color="primary" @click="publishPost">Publier</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
</v-form>
|
||||||
|
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|||||||
31
src/views/contents/ImageViewer.vue
Normal file
31
src/views/contents/ImageViewer.vue
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<div class="image-container">
|
||||||
|
<img :src="src" alt="Image" class="image" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.image-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
26
src/views/contents/YoutubePlayer.vue
Normal file
26
src/views/contents/YoutubePlayer.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<iframe
|
||||||
|
:src="src"
|
||||||
|
title="YouTube video player"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
referrerpolicy="strict-origin-when-cross-origin"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -86,10 +86,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import MyUserModel from "@/models/myUserModel.js";
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
user: {type: MyUserModel},
|
user
|
||||||
});
|
});
|
||||||
|
|
||||||
const dateRule = value => {
|
const dateRule = value => {
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
class="text-white text-2xl transform transition-transform duration-200 hover:scale-125 hover:text-blue-500">
|
class="text-white text-2xl transform transition-transform duration-200 hover:scale-125 hover:text-blue-500">
|
||||||
<v-icon v-if="socialNetwork.icon.includes('mdi')">{{ socialNetwork.icon }}</v-icon>
|
<v-icon v-if="socialNetwork.icon.includes('mdi')">{{ socialNetwork.icon }}</v-icon>
|
||||||
<img v-if="socialNetwork.icon.includes('tiktok')" :src="socialNetwork.icon" class="w-9 h-9" alt="Tiktok">
|
<img v-if="socialNetwork.icon.includes('tiktok')" :src="socialNetwork.icon" class="w-9 h-9" alt="Tiktok">
|
||||||
<img v-if="socialNetwork.icon.includes('websiteIcon')" :src="socialNetwork.icon" class="w-9 h-9" alt="Website">
|
<img v-if="socialNetwork.icon.includes('websiteIcon')" :src="socialNetwork.icon" class="w-9 h-9"
|
||||||
|
alt="Website">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,29 +92,29 @@ const props = defineProps({
|
|||||||
|
|
||||||
|
|
||||||
function GetActiveSocialNetworkUrls() {
|
function GetActiveSocialNetworkUrls() {
|
||||||
|
|
||||||
const socialNetworks = [];
|
const socialNetworks = [];
|
||||||
const userSocialNetworks = props.creator.socialNetworks;
|
const userSocialNetworks = props.creator.socialNetworks;
|
||||||
if (userSocialNetworks.facebookUrl !== '') {
|
if (userSocialNetworks.facebookUrl !== null) {
|
||||||
socialNetworks.push({icon: "mdi-facebook", url: props.creator.socialNetworks.facebookUrl})
|
socialNetworks.push({icon: "mdi-facebook", url: props.creator.socialNetworks.facebookUrl})
|
||||||
}
|
}
|
||||||
if (userSocialNetworks.facebookUrl !== '') {
|
if (userSocialNetworks.xUrl !== null) {
|
||||||
socialNetworks.push({icon: "mdi-twitter", url: props.creator.socialNetworks.xUrl})
|
socialNetworks.push({icon: "mdi-twitter", url: props.creator.socialNetworks.xUrl})
|
||||||
}
|
}
|
||||||
if (userSocialNetworks.instagramUrl !== '') {
|
if (userSocialNetworks.instagramUrl !== null) {
|
||||||
socialNetworks.push({icon: "mdi-instagram", url: props.creator.socialNetworks.instagramUrl})
|
socialNetworks.push({icon: "mdi-instagram", url: props.creator.socialNetworks.instagramUrl})
|
||||||
}
|
}
|
||||||
if (userSocialNetworks.tiktokUrl !== '') {
|
if (userSocialNetworks.tikTokUrl !== null) {
|
||||||
socialNetworks.push({
|
socialNetworks.push({
|
||||||
icon: "/images/hutopymedia/icons/white/tiktokwhite.png",
|
icon: '/images/externals/tiktok-white.png',
|
||||||
url: props.creator.socialNetworks.tikTokUrl
|
url: props.creator.socialNetworks.tikTokUrl
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (userSocialNetworks.youtubeUrl !== '') {
|
if (userSocialNetworks.youtubeUrl !== null) {
|
||||||
socialNetworks.push({icon: "mdi-youtube", url: props.creator.socialNetworks.youtubeUrl})
|
socialNetworks.push({icon: 'mdi-youtube', url: props.creator.socialNetworks.youtubeUrl})
|
||||||
}
|
}
|
||||||
if (userSocialNetworks.yourWebsiteUrl !== ''){
|
if (userSocialNetworks.websiteUrl !== null) {
|
||||||
const websiteIconWithBackup = props.creator.storedDataUrls.websiteIconUrl === '' ? "mdi-web" : props.creator.storedDataUrls.websiteIconUrl;
|
socialNetworks.push({icon: 'mdi-web', url: props.creator.socialNetworks.websiteUrl})
|
||||||
socialNetworks.push({icon: websiteIconWithBackup, url: props.creator.socialNetworks.yourWebsiteUrl})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return socialNetworks;
|
return socialNetworks;
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
<template>
|
|
||||||
|
|
||||||
<div class="mt-28">
|
|
||||||
|
|
||||||
<div class="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-4xl mb-4">
|
|
||||||
<div class="md:flex">
|
|
||||||
<div class="md:flex-shrink-0">
|
|
||||||
<img class="h-48 w-full object-cover md:h-full md:w-48" src="/images/usersmedia/HutopyProfile/banners/banner01.png" alt="Image">
|
|
||||||
</div>
|
|
||||||
<div class="p-8">
|
|
||||||
<div class="uppercase tracking-wide text-sm text-indigo-500 font-semibold">Nom du réseau social</div>
|
|
||||||
<p class="mt-2 text-gray-500">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.</p>
|
|
||||||
<p class="mt-2 text-gray-500">Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-4xl mb-4">
|
|
||||||
<div class="md:flex">
|
|
||||||
<div class="md:flex-shrink-0">
|
|
||||||
<img class="h-48 w-full object-cover md:h-full md:w-48" src="/images/usersmedia/HutopyProfile/banners/banner01.png" alt="Image">
|
|
||||||
</div>
|
|
||||||
<div class="p-8">
|
|
||||||
<div class="uppercase tracking-wide text-sm text-indigo-500 font-semibold">Nom du réseau social</div>
|
|
||||||
<p class="mt-2 text-gray-500">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.</p>
|
|
||||||
<p class="mt-2 text-gray-500">Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-4xl mb-4">
|
|
||||||
<div class="md:flex">
|
|
||||||
<div class="md:flex-shrink-0">
|
|
||||||
<img class="h-48 w-full object-cover md:h-full md:w-48" src="/images/usersmedia/HutopyProfile/banners/banner01.png" alt="Image">
|
|
||||||
</div>
|
|
||||||
<div class="p-8">
|
|
||||||
<div class="uppercase tracking-wide text-sm text-indigo-500 font-semibold">Nom du réseau social</div>
|
|
||||||
<p class="mt-2 text-gray-500">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.</p>
|
|
||||||
<p class="mt-2 text-gray-500">Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
@click.stop
|
@click.stop
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<v-app-bar-nav-icon @click.stop="toggleSidebar">
|
<v-app-bar-nav-icon @click.stop="sideBarStore.toggle()">
|
||||||
</v-app-bar-nav-icon>
|
</v-app-bar-nav-icon>
|
||||||
|
|
||||||
<RouterLink to="/" class="hidden md:block">
|
<RouterLink to="/" class="hidden md:block">
|
||||||
@@ -53,51 +53,52 @@
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<v-menu open-on-hover>
|
<v-menu open-on-hover>
|
||||||
<template v-slot:activator="{ props }">
|
<template v-slot:activator="{ props }">
|
||||||
<v-btn variant="plain" v-bind="props" class="d-flex align-center text-capital-none">
|
<div v-bind="props" class="flex align-center font-sans py-1 px-4 rounded-lg hover:bg-gray-100">
|
||||||
<span class="normal-case max-w-xs hidden md:block">
|
<span class="max-w-xs hidden md:block">
|
||||||
{{ currentUser.userName }}
|
{{ userStore.alias }}
|
||||||
</span>
|
</span>
|
||||||
<img
|
<img
|
||||||
:src="currentUser.storedDataUrls.profilePictureUrl ?? '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png'"
|
:src="userStore.portraitUrl"
|
||||||
alt="Profile Image"
|
alt="Profile Image"
|
||||||
class="ml-2 rounded-full" style="width: 32px; height: 32px;">
|
class="ml-2 rounded-full"
|
||||||
</v-btn>
|
width="32"
|
||||||
|
height="32">
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<v-list min-width="200px" class=" align-center mt-3 left-3">
|
<v-list min-width="200px" class=" align-center mt-3 left-3">
|
||||||
|
|
||||||
<div v-if="currentUser.userName === 'Anonyme'">
|
<template v-if="!authStore.isAuthenticated">
|
||||||
<v-list-item class="nav-button">
|
<v-list-item class="nav-button">
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
<v-btn to="/login" class="w-100 " variant="plain">Connexion</v-btn>
|
<v-btn to="/login" class="w-100 " variant="plain">Connexion</v-btn>
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<div v-if="currentUser.userName !== 'Anonyme'">
|
<template v-else>
|
||||||
|
<v-list-item v-if="userStore.creator" class="nav-button">
|
||||||
<v-list-item v-if="currentUser.creatorAlias !== null" class="nav-button">
|
<router-link :to="`/@${userStore.creator.name}`">
|
||||||
<router-link :to="`/@${currentUser.creatorAlias}`">
|
<v-btn class="w-100 " variant="plain">@{{ userStore.creator.name }}</v-btn>
|
||||||
<v-btn class="w-100 " variant="plain">@{{ currentUser.creatorAlias }}</v-btn>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
|
||||||
<v-list-item class="nav-button">
|
<v-list-item class="nav-button">
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
<v-btn to="/profile" class="w-100 " variant="plain">Mon profil</v-btn>
|
<v-btn to="/profile" class="w-100" variant="plain">Mon profil</v-btn>
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item class="nav-button">
|
<v-list-item class="nav-button">
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
<v-btn to="/wallet" class="w-100 " variant="plain"> Portefeuille</v-btn>
|
<v-btn to="/wallet" class="w-100" variant="plain">Portefeuille</v-btn>
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-list-item class="nav-button">
|
<v-list-item class="nav-button">
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
<v-btn @click="logout" to="/wallet" class="w-100 " variant="plain"> Déconnexion</v-btn>
|
<v-btn @click="authStore.logout" class="w-100" variant="plain">Déconnexion</v-btn>
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</div>
|
</template>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
</div>
|
</div>
|
||||||
@@ -108,21 +109,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref, onBeforeUnmount, onBeforeMount, watch, reactive} from "vue";
|
import {ref, onBeforeUnmount, onBeforeMount, watch} from "vue";
|
||||||
import {eventBus} from '@/eventBus.js';
|
|
||||||
import {useRouter} from 'vue-router';
|
import {useRouter} from 'vue-router';
|
||||||
import {useUserStore} from "@/stores/user.js";
|
import {useSideBarStore} from '@/stores/sideBarStore.js';
|
||||||
import MyUserModel from "@/models/myUserModel.js";
|
import {useUserStore} from "@/stores/userStore.js";
|
||||||
|
import {useAuthStore} from "@/stores/authStore.js";
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const sideBarStore = useSideBarStore()
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchQuery = ref("");
|
const searchQuery = ref("");
|
||||||
const showSearch = ref(false);
|
const showSearch = ref(false);
|
||||||
let currentUser = reactive(MyUserModel.getDefaultUser());
|
|
||||||
const userStore = useUserStore();
|
|
||||||
|
|
||||||
const toggleSidebar = () => {
|
|
||||||
eventBus.value.toggleSidebar();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSearch = () => {
|
const onSearch = () => {
|
||||||
const query = searchQuery.value.trim();
|
const query = searchQuery.value.trim();
|
||||||
@@ -148,33 +147,14 @@ const handleClickOutside = (event) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToMessages = () => {
|
|
||||||
router.push('/messages');
|
|
||||||
};
|
|
||||||
|
|
||||||
const logout = () => {
|
|
||||||
localStorage.removeItem('jwt');
|
|
||||||
window.location.reload();
|
|
||||||
};
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
currentUser = userStore.getCurrentUser();
|
document.addEventListener('click', handleClickOutside)
|
||||||
document.addEventListener('click', handleClickOutside);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watch the user state to get it again if needed.
|
|
||||||
watch(
|
|
||||||
() => userStore.hasChanged,
|
|
||||||
() => {
|
|
||||||
currentUser = userStore.getCurrentUser();
|
|
||||||
const timestamp = new Date().getTime();
|
|
||||||
currentUser.storedDataUrls.profilePictureUrl = `${currentUser.storedDataUrls.profilePictureUrl}?t=${timestamp}`;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
document.removeEventListener('click', handleClickOutside);
|
document.removeEventListener('click', handleClickOutside);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -68,9 +68,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref, onBeforeMount, onMounted} from "vue";
|
import {ref, onMounted} from "vue";
|
||||||
import {useUserStore} from "@/stores/user.js";
|
import {useUserStore} from "@/stores/userStore.js";
|
||||||
import {useClient} from "@/plugins/api.js";
|
|
||||||
import SizeIndicator from "@/views/tools/SizeIndicator.vue";
|
import SizeIndicator from "@/views/tools/SizeIndicator.vue";
|
||||||
import ManageAccount from "@/views/Profile/ManageAccount.vue";
|
import ManageAccount from "@/views/Profile/ManageAccount.vue";
|
||||||
import PageInformations from "@/views/Profile/PageInformations.vue";
|
import PageInformations from "@/views/Profile/PageInformations.vue";
|
||||||
@@ -78,21 +77,12 @@ import PersonnalInfo from "@/views/Profile/PersonnalInfo.vue";
|
|||||||
import AccountSecurity from "@/views/Profile/AccountSecurity.vue";
|
import AccountSecurity from "@/views/Profile/AccountSecurity.vue";
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const client = useClient();
|
|
||||||
|
|
||||||
const currentUser = ref(null);
|
|
||||||
const currentComponent = ref('PageInformations'); // Default component
|
const currentComponent = ref('PageInformations'); // Default component
|
||||||
const isDown = ref(false);
|
const isDown = ref(false);
|
||||||
const startX = ref(0);
|
const startX = ref(0);
|
||||||
const scrollLeft = ref(0);
|
const scrollLeft = ref(0);
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
try {
|
|
||||||
currentUser.value = userStore.getCurrentUser();
|
|
||||||
} catch (error) {
|
|
||||||
console.log("User not logged")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const slider = document.querySelector('.custom-scroll');
|
const slider = document.querySelector('.custom-scroll');
|
||||||
@@ -163,9 +153,6 @@ const scrollRightFunc = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.save-btn {
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-scroll {
|
.custom-scroll {
|
||||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||||
@@ -176,8 +163,4 @@ const scrollRightFunc = () => {
|
|||||||
display: none; /* Safari and Chrome */
|
display: none; /* Safari and Chrome */
|
||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
|
||||||
cursor: grabbing;
|
|
||||||
cursor: -webkit-grabbing;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -86,14 +86,13 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref} from 'vue';
|
import {ref} from 'vue';
|
||||||
import MyUserModel from "@/models/myUserModel.js";
|
|
||||||
|
|
||||||
const emit = defineEmits(["updateProfilePicture", "updateBannerPicture"]);
|
const emit = defineEmits(["updateProfilePicture", "updateBannerPicture"]);
|
||||||
const fallbackProfilePictureUrl = '/images/usersmedia/HutopyProfile/profilepictures/profileHutopyProfile01.png';
|
const fallbackProfilePictureUrl = '/images/usersmedia/HutopyProfile/profilepictures/profileHutopyProfile01.png';
|
||||||
const fallbackBannerPictureUrl = '/images/usersmedia/HutopyProfile/banners/banner01.png';
|
const fallbackBannerPictureUrl = '/images/usersmedia/HutopyProfile/banners/banner01.png';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
user: {type: MyUserModel},
|
user: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
const bannerImageUrl = ref(props.user.storedDataUrls.bannerPictureUrl);
|
const bannerImageUrl = ref(props.user.storedDataUrls.bannerPictureUrl);
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div ref="container">
|
|
||||||
<div class="text-center text-2xl py-4 border-t-4">Liens des réseaux sociaux et de votre site</div>
|
|
||||||
<div class="px-5 py-2 flex flex-col space-y-4">
|
|
||||||
<div class="flex justify-between items-center mb-2">
|
|
||||||
<label class="text-lg">Instagram</label>
|
|
||||||
</div>
|
|
||||||
<v-text-field v-model="props.user.socialNetworks.instagramUrl" label="Instagram"
|
|
||||||
variant="outlined"></v-text-field>
|
|
||||||
|
|
||||||
<div class="flex justify-between items-center mb-2">
|
|
||||||
<label class="text-lg">TikTok</label>
|
|
||||||
</div>
|
|
||||||
<v-text-field v-model="props.user.socialNetworks.tikTokUrl" label="TikTok" variant="outlined"></v-text-field>
|
|
||||||
|
|
||||||
<div class="flex justify-between items-center mb-2">
|
|
||||||
<label class="text-lg">Facebook</label>
|
|
||||||
</div>
|
|
||||||
<v-text-field v-model="props.user.socialNetworks.facebookUrl" label="Facebook" variant="outlined"></v-text-field>
|
|
||||||
|
|
||||||
<div class="flex justify-between items-center mb-2">
|
|
||||||
<label class="text-lg">X</label>
|
|
||||||
</div>
|
|
||||||
<v-text-field v-model="props.user.socialNetworks.xUrl" label="X" variant="outlined"></v-text-field>
|
|
||||||
|
|
||||||
<div class="flex justify-between items-center mb-2">
|
|
||||||
<label class="text-lg">LinkedIn</label>
|
|
||||||
</div>
|
|
||||||
<v-text-field v-model="props.user.socialNetworks.linkedInUrl" label="LinkedIn" variant="outlined"></v-text-field>
|
|
||||||
|
|
||||||
<div class="flex justify-between items-center mb-2">
|
|
||||||
<label class="text-lg">Site Web</label>
|
|
||||||
</div>
|
|
||||||
<v-text-field v-model="props.user.socialNetworks.yourWebsiteUrl" label="Site Web"
|
|
||||||
variant="outlined"></v-text-field>
|
|
||||||
|
|
||||||
<div class="flex flex-col space-y-4">
|
|
||||||
<div class="flex items-center mb-2">
|
|
||||||
<label class="text-lg mr-4">Icône pour votre site web *svg</label>
|
|
||||||
<v-file-input
|
|
||||||
v-model="iconFile"
|
|
||||||
accept=".png, .jpeg, .jpg"
|
|
||||||
hint="png, jpeg or jpg"
|
|
||||||
label="Téléverser une icône"
|
|
||||||
@change="onFileChange">
|
|
||||||
</v-file-input>
|
|
||||||
</div>
|
|
||||||
<div v-if="iconUrl" class="flex justify-center">
|
|
||||||
<img :src="iconUrl" alt="Icon" class="icon-preview">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import {ref} from 'vue';
|
|
||||||
import MyUserModel from "@/models/myUserModel.js";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
user: {type: MyUserModel},
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits(["updateWebsiteIcon"]);
|
|
||||||
|
|
||||||
const iconUrl = ref(props.user.storedDataUrls.websiteIconUrl);
|
|
||||||
const iconFile = ref(null);
|
|
||||||
|
|
||||||
const onFileChange = (event) => {
|
|
||||||
const file = event.target.files[0];
|
|
||||||
if (file) {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = (e) => {
|
|
||||||
iconUrl.value = e.target.result;
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
emit("updateWebsiteIcon", file)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.icon-preview {
|
|
||||||
max-width: 250px;
|
|
||||||
max-height: 250px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<script async setup>
|
<script async setup>
|
||||||
import { onBeforeMount, ref, computed } from 'vue';
|
import { onBeforeMount, ref, computed } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import {useUserStore} from "@/stores/user.js";
|
import {useUserStore} from "@/stores/userStore.js";
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -75,10 +75,8 @@ const transactionCount = computed(() => userTransactions.value.length);
|
|||||||
|
|
||||||
onBeforeMount( () => {
|
onBeforeMount( () => {
|
||||||
try {
|
try {
|
||||||
const myUser = userStore.getCurrentUser();
|
userTransactions.value = userStore.user.userTransactions;
|
||||||
|
totalBalance.value = userStore.user.totalBalance;
|
||||||
userTransactions.value = myUser.userTransactions;
|
|
||||||
totalBalance.value = myUser.totalBalance;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
navigateToHome();
|
navigateToHome();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="https://www.tiktok.com/@guillaumeaime">
|
<a href="https://www.tiktok.com/@guillaumeaime">
|
||||||
<img class="socialicons invert-color" src="/images/hutopymedia/icons/white/tiktokwhite.png"
|
<img class="socialicons invert-color" src="/images/externals/tiktok-white.png"
|
||||||
alt="Description image 2">
|
alt="Description image 2">
|
||||||
</a>
|
</a>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="flex flex-column">
|
<div class="flex flex-column">
|
||||||
<div class="flex flex-row p-1 items-center">
|
<div class="flex flex-row p-1 items-center">
|
||||||
<div class="px-2 content-center">
|
<div class="px-2 content-center">
|
||||||
<img :src="message.profileImageUrl ?? '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png'"
|
<img :src="message.createdByPortraitUrl ?? '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png'"
|
||||||
alt="Profile Image"
|
alt="Profile Image"
|
||||||
class="rounded-full"
|
class="rounded-full"
|
||||||
width="32px"
|
width="32px"
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ onBeforeMount(async () => {
|
|||||||
await load({
|
await load({
|
||||||
page_size: 2,
|
page_size: 2,
|
||||||
done: function (status) {
|
done: function (status) {
|
||||||
console.log(`Loading status: ${status}`)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -71,8 +70,7 @@ async function load({done, page_size}) {
|
|||||||
try {
|
try {
|
||||||
let uri = `/api/messages/${props.subjectId}?page_size=${page_size}`
|
let uri = `/api/messages/${props.subjectId}?page_size=${page_size}`
|
||||||
if (last_id !== null) uri = uri + `&last_id=${last_id}`
|
if (last_id !== null) uri = uri + `&last_id=${last_id}`
|
||||||
|
|
||||||
console.log(`Fetching messages at: ${uri}`)
|
|
||||||
const response = await client.get(uri)
|
const response = await client.get(uri)
|
||||||
|
|
||||||
if (response.status >= 200 && response.status < 300) {
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div class="mx-2 content-center">
|
<div class="mx-2 content-center">
|
||||||
|
|
||||||
<img :src="profileUrl"
|
<img :src="userStore.portraitUrl"
|
||||||
alt="Profile Image"
|
alt="Profile Image"
|
||||||
class="rounded-full"
|
class="rounded-full"
|
||||||
width="32px"
|
width="32px"
|
||||||
@@ -35,10 +35,12 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
import {useClient} from '@/plugins/api.js';
|
|
||||||
import {ref} from 'vue';
|
import {ref} from 'vue';
|
||||||
import {useUserStore} from "@/stores/user.js";
|
|
||||||
import {v7} from 'uuid'
|
import {v7} from 'uuid'
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
import {useClient} from '@/plugins/api.js';
|
||||||
|
import {useUserStore} from "@/stores/userStore.js";
|
||||||
|
import {useAuthStore} from "@/stores/authStore.js";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
subjectId: {
|
subjectId: {
|
||||||
@@ -51,13 +53,19 @@ const emit = defineEmits(['message-posted'])
|
|||||||
|
|
||||||
const client = useClient()
|
const client = useClient()
|
||||||
const value = ref("")
|
const value = ref("")
|
||||||
const user = useUserStore()
|
const router = useRouter()
|
||||||
const profileUrl = ref(user.getCurrentUser().storedDataUrls.profilePictureUrl ?? '/images/usersmedia/anonyme/profilepictures/profileAnonymeSquare.png')
|
const userStore = useUserStore()
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
const publish = async () => {
|
const publish = async () => {
|
||||||
|
|
||||||
|
if (!authStore.isAuthenticated) {
|
||||||
|
await router.push('/login')
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const messageId = v7()
|
const messageId = v7()
|
||||||
|
|
||||||
await client.post(`/api/messages/`,
|
await client.post(`/api/messages/`,
|
||||||
{
|
{
|
||||||
"id": messageId,
|
"id": messageId,
|
||||||
@@ -65,17 +73,16 @@ const publish = async () => {
|
|||||||
"message": value.value
|
"message": value.value
|
||||||
})
|
})
|
||||||
|
|
||||||
const currentUser = user.getCurrentUser()
|
|
||||||
emit('message-posted',
|
emit('message-posted',
|
||||||
{
|
{
|
||||||
"id": messageId,
|
"id": messageId,
|
||||||
"subjectId": props.subjectId,
|
"subjectId": props.subjectId,
|
||||||
"createdBy": currentUser.id,
|
"createdBy": userStore.user.id,
|
||||||
"createdByName": currentUser.alias ?? `${currentUser.firstName} ${currentUser.lastName}`,
|
"createdByName": userStore.alias,
|
||||||
"createdByPortraitUrl": currentUser.storedDataUrls.profilePictureUrl,
|
"createdByPortraitUrl": userStore.portraitUrl,
|
||||||
"createdAt": new Date(Date.now()).toISOString(),
|
"createdAt": new Date(Date.now()).toISOString(),
|
||||||
"value": value.value,
|
"value": value.value,
|
||||||
parentId: null,
|
"parentId": null
|
||||||
})
|
})
|
||||||
|
|
||||||
value.value = ''
|
value.value = ''
|
||||||
|
|||||||
Reference in New Issue
Block a user