From cf9eb76ae24344938424b53890fe568c1327365e Mon Sep 17 00:00:00 2001 From: Ramon Wenger Date: Wed, 23 Feb 2022 23:43:43 +0100 Subject: [PATCH] Add more typescript definitions --- client/src/@types/graphql.d.ts | 12 ++++++ client/src/{types => @types}/index.ts | 0 client/src/@types/vuejs-logger.d.ts | 8 ++++ .../content-block-form/ContentBlockForm.vue | 10 ++--- .../components/portfolio/ProjectActions.vue | 2 +- .../src/components/portfolio/ProjectEntry.vue | 2 +- ...-operations.js => immutable-operations.ts} | 12 +++--- client/src/main.js | 19 +--------- client/src/plugins/modal.d.ts | 13 ------- client/src/plugins/{modal.js => modal.ts} | 38 +++++++++++++++---- client/src/router/guards.ts | 21 ++++++++++ client/src/router/router.d.ts | 8 ++++ client/tsconfig.json | 6 --- 13 files changed, 94 insertions(+), 57 deletions(-) create mode 100644 client/src/@types/graphql.d.ts rename client/src/{types => @types}/index.ts (100%) create mode 100644 client/src/@types/vuejs-logger.d.ts rename client/src/graphql/{immutable-operations.js => immutable-operations.ts} (73%) delete mode 100644 client/src/plugins/modal.d.ts rename client/src/plugins/{modal.js => modal.ts} (59%) create mode 100644 client/src/router/guards.ts create mode 100644 client/src/router/router.d.ts diff --git a/client/src/@types/graphql.d.ts b/client/src/@types/graphql.d.ts new file mode 100644 index 00000000..60950b87 --- /dev/null +++ b/client/src/@types/graphql.d.ts @@ -0,0 +1,12 @@ +declare module '*.graphql' { + import {DocumentNode} from "graphql"; + const Schema: DocumentNode; + + export = Schema; +} + +declare module '*.gql' { + import {DocumentNode} from "graphql"; + const content: DocumentNode; + export default content; +} diff --git a/client/src/types/index.ts b/client/src/@types/index.ts similarity index 100% rename from client/src/types/index.ts rename to client/src/@types/index.ts diff --git a/client/src/@types/vuejs-logger.d.ts b/client/src/@types/vuejs-logger.d.ts new file mode 100644 index 00000000..298e120b --- /dev/null +++ b/client/src/@types/vuejs-logger.d.ts @@ -0,0 +1,8 @@ +import Vue from 'vue'; +import {Log} from "vuejs-logger"; + +declare module 'vue/types/vue' { + interface Vue { + $log: Log + } +} diff --git a/client/src/components/content-block-form/ContentBlockForm.vue b/client/src/components/content-block-form/ContentBlockForm.vue index e8e1112f..6a2f9f40 100644 --- a/client/src/components/content-block-form/ContentBlockForm.vue +++ b/client/src/components/content-block-form/ContentBlockForm.vue @@ -126,11 +126,11 @@ import AddContentLink from '@/components/content-block-form/AddContentLink.vue'; import ContentElement from '@/components/content-block-form/ContentElement.vue'; - import {moveToIndex, removeAtIndex, replaceAtIndex, swapElements} from '@/graphql/immutable-operations.js'; + import {moveToIndex, removeAtIndex, replaceAtIndex, swapElements} from '@/graphql/immutable-operations'; import {CHOOSER, transformInnerContents} from '@/components/content-block-form/helpers.js'; import ContentElementActions from '@/components/content-block-form/ContentElementActions.vue'; - import {ContentBlock, numberOrUndefined} from "@/types"; + import {ContentBlock, numberOrUndefined} from "@/@types"; // TODO: refactor this file, it's huuuuuge! @@ -170,7 +170,7 @@ }, }, methods: { - update(index: number, element: any, parent: numberOrUndefined = undefined) { + update(index: number, element: any, parent?: number) { if (parent === undefined) { // element is top level this.localContentBlock.contents = [ @@ -195,7 +195,7 @@ ]; } }, - addBlock(afterOuterIndex: number, innerIndex: numberOrUndefined = undefined) { + addBlock(afterOuterIndex: number, innerIndex?: number) { if (innerIndex !== undefined) { const block = this.localContentBlock.contents[afterOuterIndex]; this.localContentBlock.contents = [ @@ -225,7 +225,7 @@ ]; } }, - remove(outer: number, inner: numberOrUndefined = undefined, askForConfirmation = true) { + remove(outer: number, inner?: number, askForConfirmation = true) { if(askForConfirmation) { this.$modal.open('confirm') .then(() => { diff --git a/client/src/components/portfolio/ProjectActions.vue b/client/src/components/portfolio/ProjectActions.vue index c234bed9..83c50679 100644 --- a/client/src/components/portfolio/ProjectActions.vue +++ b/client/src/components/portfolio/ProjectActions.vue @@ -55,7 +55,7 @@ import DELETE_PROJECT_MUTATION from '@/graphql/gql/mutations/deleteProject.gql'; import PROJECTS_QUERY from '@/graphql/gql/queries/allProjects.gql'; import updateProjectShareState from '@/mixins/update-project-share-state'; -import {removeAtIndex} from '@/graphql/immutable-operations'; +import {removeAtIndex} from '@/graphql/immutable-operations.ts'; const Ellipses = () => import(/* webpackChunkName: "icons" */'@/components/icons/Ellipses.vue'); export default { diff --git a/client/src/components/portfolio/ProjectEntry.vue b/client/src/components/portfolio/ProjectEntry.vue index 50a5d95e..16288533 100644 --- a/client/src/components/portfolio/ProjectEntry.vue +++ b/client/src/components/portfolio/ProjectEntry.vue @@ -52,7 +52,7 @@ import DELETE_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/deleteProjectEntry.gql'; import PROJECT_QUERY from '@/graphql/gql/queries/projectQuery.gql'; import {dateFilter, dateTimeFilter} from '@/filters/date-filter'; - import {removeAtIndex} from '@/graphql/immutable-operations'; + import {removeAtIndex} from '@/graphql/immutable-operations.ts'; const DocumentBlock = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/DocumentBlock'); diff --git a/client/src/graphql/immutable-operations.js b/client/src/graphql/immutable-operations.ts similarity index 73% rename from client/src/graphql/immutable-operations.js rename to client/src/graphql/immutable-operations.ts index a401efaa..a070365e 100644 --- a/client/src/graphql/immutable-operations.js +++ b/client/src/graphql/immutable-operations.ts @@ -1,24 +1,24 @@ // signature should be in order: arr, idx, el, for readability -export const pushToArray = (arr, el) => { +export const pushToArray = (arr: any[], el: any) => { if (!arr) { return [el]; } return [...arr, el]; }; -export const insertAtIndex = (arr, idx, el) => { +export const insertAtIndex = (arr: any[], idx: number, el: any) => { return [ ...arr.slice(0, idx), el, ...arr.slice(idx) ]; }; -export const removeAtIndex = (arr, idx) => { +export const removeAtIndex = (arr: any[], idx: number) => { return [ ...arr.slice(0, idx), ...arr.slice(idx + 1) , ]; }; -export const replaceAtIndex = (arr, idx, el) => { +export const replaceAtIndex = (arr: any[], idx: number, el: any) => { //todo: check array index bounds, numbers return [ ...arr.slice(0, idx), @@ -26,7 +26,7 @@ export const replaceAtIndex = (arr, idx, el) => { ...arr.slice(idx+1) ]; }; -export const swapElements = (arr, idx1, idx2)=>{ +export const swapElements = (arr: any[], idx1: number, idx2: number)=>{ const maxLength = arr.length - 1; if (idx1 < 0 || idx2 < 0 || idx1 > maxLength || idx2 > maxLength) { return [...arr]; @@ -42,7 +42,7 @@ export const swapElements = (arr, idx1, idx2)=>{ ...arr.slice(larger+1) ]; }; -export const moveToIndex = (arr, from, to) => { +export const moveToIndex = (arr: any[], from: number, to: number) => { const maxLength = arr.length - 1; if (from < 0 || to < 0 || from > maxLength || to > maxLength) { throw new Error('Index out of bounds'); diff --git a/client/src/main.js b/client/src/main.js index 2d15e705..c6ca7c72 100644 --- a/client/src/main.js +++ b/client/src/main.js @@ -3,7 +3,7 @@ import Vue from 'vue'; import VueVimeoPlayer from 'vue-vimeo-player'; import apolloClientFactory from './graphql/client'; import VueApollo from 'vue-apollo'; -import App from './App'; +import App from './App.vue'; import {postLoginRedirectUrlKey, router} from './router'; import store from '@/store/index'; import VueScrollTo from 'vue-scrollto'; @@ -15,6 +15,7 @@ import VueRemoveEdges from '@/plugins/edges'; import VueMatomo from 'vue-matomo'; import VueToast from 'vue-toast-notification'; import VueLogger from 'vuejs-logger'; +import {joiningClass, loginRequired, unauthorizedAccess} from "@/router/guards"; Vue.config.productionTip = false; const isProduction = process.env.NODE_ENV === 'production'; @@ -60,20 +61,7 @@ const apolloProvider = new VueApollo({ /* guards */ -function getCookieValue(cookieName) { - // https://stackoverflow.com/questions/5639346/what-is-the-shortest-function-for-reading-a-cookie-by-name-in-javascript - let cookieValue = document.cookie.match('(^|[^;]+)\\s*' + cookieName + '\\s*=\\s*([^;]+)'); - return cookieValue ? cookieValue.pop() : ''; -} -function loginRequired(to) { - // public pages have the meta.public property set to true - return !Object.prototype.hasOwnProperty.call(to, 'meta') || !Object.prototype.hasOwnProperty.call(to.meta ,'public') || !to.meta.public; -} - -function unauthorizedAccess(to) { - return loginRequired(to) && getCookieValue('loginStatus') !== 'true'; -} function redirectUsersWithoutValidLicense() { return privateApolloClient.query({ @@ -100,9 +88,6 @@ function networkErrorCallback(statusCode) { } } -function joiningClass(to) { - return to.name && (to.name === 'join-class' || to.name === 'licenseActivation'); -} router.beforeEach(async (to, from, next) => { Vue.$log.debug('navigation guard called', to, from); diff --git a/client/src/plugins/modal.d.ts b/client/src/plugins/modal.d.ts deleted file mode 100644 index f43ab63e..00000000 --- a/client/src/plugins/modal.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Vue from 'vue'; - -interface Modal { - confirm: (res: any) => void, - open: (component: string, payload?: any) => Promise<(resolve: () => any, reject: () => any) => void>, - cancel: () => void -} - -declare module 'vue/types/vue' { - interface Vue { - $modal: Modal - } -} diff --git a/client/src/plugins/modal.js b/client/src/plugins/modal.ts similarity index 59% rename from client/src/plugins/modal.js rename to client/src/plugins/modal.ts index 355b22f5..019afaec 100644 --- a/client/src/plugins/modal.js +++ b/client/src/plugins/modal.ts @@ -1,8 +1,11 @@ // adapted from // https://stackoverflow.com/questions/41791193/vuejs-reactive-binding-for-a-plugin-how-to/41801107#41801107 -import Vue from 'vue'; +import Vue, {VueConstructor} from 'vue'; + + +class ModalStore { + vm: Vue; -class ModalStore { constructor() { this.vm = new Vue({ data: () => ({ @@ -17,20 +20,37 @@ class ModalStore { } } +interface Modal { + state: any, + component: string, + payload?: any, + confirm: (res: any) => void, + open: (component: string, payload?: any) => Promise<(resolve: () => any, reject: () => any) => void>, + cancel: () => void, + _resolve: (r?: any) => any, + _reject: (r?: any) => any +} + +declare module 'vue/types/vue' { + interface Vue { + $modal: Modal + } +} + const ModalPlugin = { - install(Vue) { - const store = new ModalStore({}); + install(Vue: VueConstructor) { + const store = new ModalStore(); const reset = () => { store.state.component = ''; store.state.payload = {}; }; - Vue.prototype.$modal = { + const modal: Modal = { state: store.state, component: store.state.component, payload: store.state.payload, - open: (component, payload) => { + open(component: string, payload?: any) { store.state.payload = payload; store.state.component = component; return new Promise((resolve, reject) => { @@ -38,11 +58,11 @@ const ModalPlugin = { this._reject = reject; }); }, - confirm: (res) => { + confirm(res: any) { reset(); this._resolve(res); }, - cancel: () => { + cancel() { reset(); this._reject(); }, @@ -51,6 +71,8 @@ const ModalPlugin = { _reject: () => { }, }; + + Vue.prototype.$modal = modal; } }; diff --git a/client/src/router/guards.ts b/client/src/router/guards.ts new file mode 100644 index 00000000..d3def2bc --- /dev/null +++ b/client/src/router/guards.ts @@ -0,0 +1,21 @@ +import {Route} from "vue-router"; + +function getCookieValue(cookieName: string) { + // https://stackoverflow.com/questions/5639346/what-is-the-shortest-function-for-reading-a-cookie-by-name-in-javascript + const cookieValue = document.cookie.match('(^|[^;]+)\\s*' + cookieName + '\\s*=\\s*([^;]+)'); + return cookieValue ? cookieValue.pop() : ''; +} + +export function loginRequired(to: Route) { + // public pages have the meta.public property set to true + const isPublic = to.meta && to.meta.public; + return !isPublic; +} + +export function unauthorizedAccess(to: Route) { + return loginRequired(to) && getCookieValue('loginStatus') !== 'true'; +} + +export function joiningClass(to: Route) { + return to.name && (to.name === 'join-class' || to.name === 'licenseActivation'); +} diff --git a/client/src/router/router.d.ts b/client/src/router/router.d.ts new file mode 100644 index 00000000..a2e8e45c --- /dev/null +++ b/client/src/router/router.d.ts @@ -0,0 +1,8 @@ +import 'vue-router'; + +declare module 'vue-router' { + interface RouteMeta { + + public?: boolean; + } +} diff --git a/client/tsconfig.json b/client/tsconfig.json index 43a45dc7..b7af0e98 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -12,12 +12,6 @@ "paths": { "@/*": ["./*"], }, - "types": [ - "cypress" - ], "plugins": [{"name": "typescript-tslint-plugin"}] }, - "include": [ - "**/*.*" - ] }