Skip to content

Commit

Permalink
Add composables & stores
Browse files Browse the repository at this point in the history
  • Loading branch information
lcharette committed Jul 10, 2024
1 parent a32183e commit aae5699
Show file tree
Hide file tree
Showing 23 changed files with 2,375 additions and 3 deletions.
36 changes: 36 additions & 0 deletions app/assets/composables/authCheckApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ref } from 'vue'
import axios from 'axios'
import { type AlertInterface, AlertStyle } from '../interfaces'
import { useAuthStore } from '../stores'
const authStore = useAuthStore()

/**
* Composable used to communicate with the `/auth/check` api. Calling "check"
* will fetch the user info from the server and set the frontend object.
*/
export function useCheckApi(auth: typeof authStore) {
const loading = ref(false)
const error = ref<AlertInterface | undefined>()

const check = () => {
loading.value = true
error.value = undefined
axios
.get('/account/auth-check')
.then((response) => {
auth.setUser(response.data.user)
})
.catch((err) => {
auth.unsetUser()
error.value = {
...err.response.data,
...{ style: AlertStyle.Danger, closeBtn: true }
}
})
.finally(() => {
loading.value = false
})
}

return { loading, error, check }
}
5 changes: 5 additions & 0 deletions app/assets/composables/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useCheckApi } from './authCheckApi'
import { useLoginApi } from './loginApi'
import { useLogoutApi } from './logoutApi'

export { useCheckApi, useLoginApi, useLogoutApi }
39 changes: 39 additions & 0 deletions app/assets/composables/loginApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ref } from 'vue'
import axios from 'axios'
import { type AlertInterface, AlertStyle } from '../interfaces'
import { useAuthStore } from '../stores'
import type { LoginForm } from '../interfaces'
const authStore = useAuthStore()

/**
* Composable used to communicate with the `/auth/login` api. Calling "login"
* with the user login data will validate the data with the server. If login is
* successful, the user will be set on the frontend object. Otherwise, an error
* will be defined.
*/
export function useLoginApi(auth: typeof authStore) {
const loading = ref(false)
const error = ref<AlertInterface | undefined>()

// TODO : Error if user is not null
const login = (form: LoginForm) => {
loading.value = true
error.value = undefined
axios
.post('/account/login', form)
.then((response) => {
auth.setUser(response.data)
})
.catch((err) => {
error.value = {
...err.response.data,
...{ style: AlertStyle.Danger, closeBtn: true }
}
})
.finally(() => {
loading.value = false
})
}

return { loading, error, login }
}
34 changes: 34 additions & 0 deletions app/assets/composables/logoutApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ref } from 'vue'
import axios from 'axios'
import { type AlertInterface, AlertStyle } from '../interfaces'
import { useAuthStore } from '../stores'
const authStore = useAuthStore()

/**
* Composable used to communicate with the `/auth/logout` api. Calling "logout"
* will send the request to logout the user server side and delete the frontend
* user object.
*/
export function useLogoutApi(auth: typeof authStore) {
const loading = ref(false)
const error = ref<AlertInterface | undefined>()

const logout = () => {
loading.value = true
error.value = undefined
auth.unsetUser()
axios
.get('/account/logout')
.catch((err) => {
error.value = {
...err.response.data,
...{ style: AlertStyle.Danger, closeBtn: true }
}
})
.finally(() => {
loading.value = false
})
}

return { loading, error, logout }
}
22 changes: 22 additions & 0 deletions app/assets/stores/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { defineStore } from 'pinia'
import type { UserInterface } from '../interfaces'

export const useAuthStore = defineStore('auth', {
persist: true,
state: () => {
return {
user: null as UserInterface | null
}
},
getters: {
isAuthenticated: (state): boolean => state.user !== null
},
actions: {
setUser(user: UserInterface): void {
this.user = user
},
unsetUser(): void {
this.user = null
}
}
})
3 changes: 3 additions & 0 deletions app/assets/stores/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { useAuthStore } from "./auth";

export { useAuthStore }
9 changes: 9 additions & 0 deletions dist/auth-BTqz4VbC.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use strict";const c=require("vue");var G=!1;function k(e,u,r){return Array.isArray(e)?(e.length=Math.max(e.length,u),e.splice(u,1,r),r):(e[u]=r,r)}function $(e,u){if(Array.isArray(e)){e.splice(u,1);return}delete e[u]}/*!
* pinia v2.1.7
* (c) 2023 Eduardo San Martin Morote
* @license MIT
*/let j;const x=e=>j=e,K=process.env.NODE_ENV!=="production"?Symbol("pinia"):Symbol();function O(e){return e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"&&typeof e.toJSON!="function"}var R;(function(e){e.direct="direct",e.patchObject="patch object",e.patchFunction="patch function"})(R||(R={}));const U=typeof window<"u",I=(process.env.NODE_ENV!=="production"||!1)&&process.env.NODE_ENV!=="test"&&U;function z(e,u){for(const r in u){const s=u[r];if(!(r in e))continue;const i=e[r];O(i)&&O(s)&&!c.isRef(s)&&!c.isReactive(s)?e[r]=z(i,s):e[r]=s}return e}const B=()=>{};function q(e,u,r,s=B){e.push(u);const i=()=>{const l=e.indexOf(u);l>-1&&(e.splice(l,1),s())};return!r&&c.getCurrentScope()&&c.onScopeDispose(i),i}function V(e,...u){e.slice().forEach(r=>{r(...u)})}const ee=e=>e();function A(e,u){e instanceof Map&&u instanceof Map&&u.forEach((r,s)=>e.set(s,r)),e instanceof Set&&u instanceof Set&&u.forEach(e.add,e);for(const r in u){if(!u.hasOwnProperty(r))continue;const s=u[r],i=e[r];O(i)&&O(s)&&e.hasOwnProperty(r)&&!c.isRef(s)&&!c.isReactive(s)?e[r]=A(i,s):e[r]=s}return e}const te=process.env.NODE_ENV!=="production"?Symbol("pinia:skipHydration"):Symbol();function se(e){return!O(e)||!e.hasOwnProperty(te)}const{assign:b}=Object;function J(e){return!!(c.isRef(e)&&e.effect)}function M(e,u,r,s){const{state:i,actions:l,getters:_}=u,f=r.state.value[e];let m;function E(){!f&&(process.env.NODE_ENV==="production"||!s)&&(r.state.value[e]=i?i():{});const v=process.env.NODE_ENV!=="production"&&s?c.toRefs(c.ref(i?i():{}).value):c.toRefs(r.state.value[e]);return b(v,l,Object.keys(_||{}).reduce((h,p)=>(process.env.NODE_ENV!=="production"&&p in v&&console.warn(`[🍍]: A getter cannot have the same name as another state property. Rename one of them. Found with "${p}" in store "${e}".`),h[p]=c.markRaw(c.computed(()=>{x(r);const N=r._s.get(e);return _[p].call(N,N)})),h),{}))}return m=L(e,E,u,r,s,!0),m}function L(e,u,r={},s,i,l){let _;const f=b({actions:{}},r);if(process.env.NODE_ENV!=="production"&&!s._e.active)throw new Error("Pinia destroyed");const m={deep:!0};process.env.NODE_ENV!=="production"&&!G&&(m.onTrigger=o=>{E?N=o:E==!1&&!n._hotUpdating&&(Array.isArray(N)?N.push(o):console.error("🍍 debuggerEvents should be an array. This is most likely an internal Pinia bug."))});let E,v,h=[],p=[],N;const P=s.state.value[e];!l&&!P&&(process.env.NODE_ENV==="production"||!i)&&(s.state.value[e]={});const C=c.ref({});let F;function W(o){let t;E=v=!1,process.env.NODE_ENV!=="production"&&(N=[]),typeof o=="function"?(o(s.state.value[e]),t={type:R.patchFunction,storeId:e,events:N}):(A(s.state.value[e],o),t={type:R.patchObject,payload:o,storeId:e,events:N});const a=F=Symbol();c.nextTick().then(()=>{F===a&&(E=!0)}),v=!0,V(h,t,s.state.value[e])}const Q=l?function(){const{state:t}=r,a=t?t():{};this.$patch(d=>{b(d,a)})}:process.env.NODE_ENV!=="production"?()=>{throw new Error(`🍍: Store "${e}" is built using the setup syntax and does not implement $reset().`)}:B;function X(){_.stop(),h=[],p=[],s._s.delete(e)}function H(o,t){return function(){x(s);const a=Array.from(arguments),d=[],D=[];function Y(y){d.push(y)}function Z(y){D.push(y)}V(p,{args:a,name:o,store:n,after:Y,onError:Z});let S;try{S=t.apply(this&&this.$id===e?this:n,a)}catch(y){throw V(D,y),y}return S instanceof Promise?S.then(y=>(V(d,y),y)).catch(y=>(V(D,y),Promise.reject(y))):(V(d,S),S)}}const w=c.markRaw({actions:{},getters:{},state:[],hotState:C}),T={_p:s,$id:e,$onAction:q.bind(null,p),$patch:W,$reset:Q,$subscribe(o,t={}){const a=q(h,o,t.detached,()=>d()),d=_.run(()=>c.watch(()=>s.state.value[e],D=>{(t.flush==="sync"?v:E)&&o({storeId:e,type:R.direct,events:N},D)},b({},m,t)));return a},$dispose:X},n=c.reactive(process.env.NODE_ENV!=="production"||I?b({_hmrPayload:w,_customProperties:c.markRaw(new Set)},T):T);s._s.set(e,n);const g=(s._a&&s._a.runWithContext||ee)(()=>s._e.run(()=>(_=c.effectScope()).run(u)));for(const o in g){const t=g[o];if(c.isRef(t)&&!J(t)||c.isReactive(t))process.env.NODE_ENV!=="production"&&i?k(C.value,o,c.toRef(g,o)):l||(P&&se(t)&&(c.isRef(t)?t.value=P[o]:A(t,P[o])),s.state.value[e][o]=t),process.env.NODE_ENV!=="production"&&w.state.push(o);else if(typeof t=="function"){const a=process.env.NODE_ENV!=="production"&&i?t:H(o,t);g[o]=a,process.env.NODE_ENV!=="production"&&(w.actions[o]=t),f.actions[o]=t}else process.env.NODE_ENV!=="production"&&J(t)&&(w.getters[o]=l?r.getters[o]:t,U&&(g._getters||(g._getters=c.markRaw([]))).push(o))}if(b(n,g),b(c.toRaw(n),g),Object.defineProperty(n,"$state",{get:()=>process.env.NODE_ENV!=="production"&&i?C.value:s.state.value[e],set:o=>{if(process.env.NODE_ENV!=="production"&&i)throw new Error("cannot set hotState");W(t=>{b(t,o)})}}),process.env.NODE_ENV!=="production"&&(n._hotUpdate=c.markRaw(o=>{n._hotUpdating=!0,o._hmrPayload.state.forEach(t=>{if(t in n.$state){const a=o.$state[t],d=n.$state[t];typeof a=="object"&&O(a)&&O(d)?z(a,d):o.$state[t]=d}k(n,t,c.toRef(o.$state,t))}),Object.keys(n.$state).forEach(t=>{t in o.$state||$(n,t)}),E=!1,v=!1,s.state.value[e]=c.toRef(o._hmrPayload,"hotState"),v=!0,c.nextTick().then(()=>{E=!0});for(const t in o._hmrPayload.actions){const a=o[t];k(n,t,H(t,a))}for(const t in o._hmrPayload.getters){const a=o._hmrPayload.getters[t],d=l?c.computed(()=>(x(s),a.call(n,n))):a;k(n,t,d)}Object.keys(n._hmrPayload.getters).forEach(t=>{t in o._hmrPayload.getters||$(n,t)}),Object.keys(n._hmrPayload.actions).forEach(t=>{t in o._hmrPayload.actions||$(n,t)}),n._hmrPayload=o._hmrPayload,n._getters=o._getters,n._hotUpdating=!1})),I){const o={writable:!0,configurable:!0,enumerable:!1};["_p","_hmrPayload","_getters","_customProperties"].forEach(t=>{Object.defineProperty(n,t,b({value:n[t]},o))})}return s._p.forEach(o=>{if(I){const t=_.run(()=>o({store:n,app:s._a,pinia:s,options:f}));Object.keys(t||{}).forEach(a=>n._customProperties.add(a)),b(n,t)}else b(n,_.run(()=>o({store:n,app:s._a,pinia:s,options:f})))}),process.env.NODE_ENV!=="production"&&n.$state&&typeof n.$state=="object"&&typeof n.$state.constructor=="function"&&!n.$state.constructor.toString().includes("[native code]")&&console.warn(`[🍍]: The "state" must be a plain object. It cannot be
state: () => new MyClass()
Found in store "${n.$id}".`),P&&l&&r.hydrate&&r.hydrate(n.$state,P),E=!0,v=!0,n}function oe(e,u,r){let s,i;const l=typeof u=="function";s=e,i=l?r:u;function _(f,m){const E=c.hasInjectionContext();if(f=(process.env.NODE_ENV==="test"&&j&&j._testing?null:f)||(E?c.inject(K,null):null),f&&x(f),process.env.NODE_ENV!=="production"&&!j)throw new Error(`[🍍]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"?
See https://pinia.vuejs.org/core-concepts/outside-component-usage.html for help.
This will fail in production.`);f=j,f._s.has(s)||(l?L(s,u,i,f):M(s,i,f),process.env.NODE_ENV!=="production"&&(_._pinia=f));const v=f._s.get(s);if(process.env.NODE_ENV!=="production"&&m){const h="__hot:"+s,p=l?L(h,u,i,f,!0):M(h,b({},i),f,!0);m._hotUpdate(p),delete f.state.value[h],f._s.delete(h)}if(process.env.NODE_ENV!=="production"&&U){const h=c.getCurrentInstance();if(h&&h.proxy&&!m){const p=h.proxy,N="_pStores"in p?p._pStores:p._pStores={};N[s]=v}}return v}return _.$id=s,_}const ne=oe("auth",{persist:!0,state:()=>({user:null}),getters:{isAuthenticated:e=>e.user!==null},actions:{setUser(e){this.user=e},unsetUser(){this.user=null}}});exports.useAuthStore=ne;
Loading

0 comments on commit aae5699

Please sign in to comment.