Browse Source

Feature: Mobile - Added first app initialization handling.

Dominik Klein 3 years ago
parent
commit
eaf5612116

+ 21 - 0
.eslintrc.js

@@ -24,6 +24,13 @@ module.exports = {
     'vue/script-setup-uses-vars': 'error',
     'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
     'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+    'vue/component-tags-order': [
+      'error',
+      {
+        order: ['template', 'script', 'style'],
+      },
+    ],
+    // Disable the following rule, because it's not relevant for the tool chain and test envoirment.
     'import/no-extraneous-dependencies': [
       'error',
       { devDependencies: ['vite.config.ts', 'app/frontend/tests/**/*'] },
@@ -40,6 +47,19 @@ module.exports = {
         tsx: 'never',
       },
     ],
+    /* We strongly recommend that you do not use the no-undef lint rule on TypeScript projects. The checks it provides are already provided by TypeScript without the need for configuration - TypeScript just does this significantly better (Source: https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors). */
+    'no-undef': 'off',
+
+    // We need to use the extended 'no-shadow' rule from typescript:
+    // https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-shadow.md
+    'no-shadow': 'off',
+    '@typescript-eslint/no-shadow': 'off',
+
+    // Enforce v-bind directive usage in long form.
+    'vue/v-bind-style': ['error', 'longform'],
+
+    // Enforce v-on directive usage in long form.
+    'vue/v-on-style': ['error', 'longform'],
   },
   overrides: [
     {
@@ -53,6 +73,7 @@ module.exports = {
     'import/resolver': {
       alias: {
         map: [
+          ['@', path.resolve(__dirname, './app/frontend/')],
           ['@mobile', path.resolve(__dirname, './app/frontend/apps/mobile')],
           ['@common', path.resolve(__dirname, './app/frontend/common')],
         ],

+ 6 - 2
.graphql_code_generator.yml

@@ -4,15 +4,20 @@ config:
   vueCompositionApiImportFrom: vue
 generates:
   ./app/frontend/common/graphql/types.ts:
+    documents:
+      [
+        'app/frontend/common/graphql/**/*.graphql',
+        'app/frontend/apps/mobile/graphql/**/*.graphql',
+      ]
     plugins:
       - typescript
+      - typescript-operations
   ./app/frontend/common/graphql/api.ts:
     documents: 'app/frontend/common/graphql/**/*.graphql'
     preset: import-types
     presetConfig:
       typesPath: './types'
     plugins:
-      - typescript-operations
       - typescript-vue-apollo
   ./app/frontend/apps/mobile/graphql/api.ts:
     documents: 'app/frontend/apps/mobile/graphql/**/*.graphql'
@@ -20,5 +25,4 @@ generates:
     presetConfig:
       typesPath: '@common/graphql/types'
     plugins:
-      - typescript-operations
       - typescript-vue-apollo

+ 27 - 21
app/frontend/apps/mobile/App.vue

@@ -1,25 +1,31 @@
+<template>
+  <CommonNotifications />
+  <div
+    class="
+      h-full
+      max-h-full
+      overflow-auto
+      w-full
+      bg-gray-200
+      text-center text-sm
+      antialiased
+      font-sans
+      select-none
+    "
+  >
+    <router-view v-if="applicationLoaded.value" v-slot="{ Component }">
+      <transition>
+        <component v-bind:is="Component" />
+      </transition>
+    </router-view>
+  </div>
+</template>
+
 <script setup lang="ts">
-// This starter template is using Vue 3 <script setup> SFCs
-// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
+import CommonNotifications from '@common/components/CommonNotifications.vue'
+import useApplicationLoadedStore from '@common/stores/application/loaded'
 
-import { ref } from 'vue'
-import CommonHelloWorld from '@common/components/CommonHelloWorld.vue'
+const applicationLoaded = useApplicationLoadedStore()
 
-const state = ref<boolean>(false)
+applicationLoaded.setLoaded()
 </script>
-
-<template>
-  <input v-model="state" type="checkbox" /> Show output?
-  <CommonHelloWorld msg="Zammad Mobile!" :show="state" />
-</template>
-
-<style>
-#app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
-  margin-top: 60px;
-}
-</style>

+ 0 - 5
app/frontend/apps/mobile/graphql/api.ts

@@ -4,11 +4,6 @@ import gql from 'graphql-tag';
 import * as VueApolloComposable from '@vue/apollo-composable';
 import * as VueCompositionApi from 'vue';
 export type ReactiveFunction<TParam> = () => TParam;
-export type ApplicationConfigQueryVariables = Types.Exact<{ [key: string]: never; }>;
-
-
-export type ApplicationConfigQuery = { __typename?: 'Queries', applicationConfig: Array<{ __typename?: 'KeyComplexValue', key: string, value?: any | null | undefined }> };
-
 
 export const ApplicationConfigDocument = gql`
     query applicationConfig {

+ 0 - 0
app/frontend/apps/mobile/initializer/.keep


+ 31 - 2
app/frontend/apps/mobile/main.ts

@@ -1,6 +1,35 @@
 import { createApp } from 'vue'
 import App from '@mobile/App.vue'
+import { DefaultApolloClient } from '@vue/apollo-composable'
+import apolloClient from '@common/server/apollo/client'
+import useSessionIdStore from '@common/stores/session/id'
+import '@common/styles/main.css'
+import initializeStore from '@common/stores'
+import InitializeHandler from '@common/initializer'
+import useApplicationConfigStore from '@common//stores/application/config'
+import initializeRouter from '@common/router/index'
+import routes from '@mobile/router'
 
-export default function mountApp(): void {
-  createApp(App).mount('#app')
+export default async function mountApp(): Promise<void> {
+  const app = createApp(App)
+
+  app.provide(DefaultApolloClient, apolloClient)
+
+  initializeStore(app)
+  initializeRouter(app, routes)
+
+  const initializer = new InitializeHandler(
+    app,
+    import.meta.globEager('/apps/mobile/initializer/*.ts'),
+  )
+
+  initializer.initialize()
+
+  const sessionId = useSessionIdStore()
+  await sessionId.checkSession()
+
+  const applicationConfig = useApplicationConfigStore()
+  await applicationConfig.getConfig()
+
+  app.mount('#app')
 }

+ 26 - 0
app/frontend/apps/mobile/router/index.ts

@@ -0,0 +1,26 @@
+import { RouteRecordRaw } from 'vue-router'
+import Login from '@mobile/views/Login.vue'
+import Home from '@mobile/views/Home.vue'
+
+// TODO ...extend "meta" in RouteRecordRaw with real type behind.
+
+const routes: Array<RouteRecordRaw> = [
+  {
+    path: '/login',
+    name: 'Login',
+    props: true,
+    component: Login,
+    meta: {},
+  },
+  {
+    path: '/',
+    name: 'Home',
+    props: true,
+    component: Home,
+    meta: {
+      requiresAuth: true,
+    },
+  },
+]
+
+export default routes

+ 38 - 0
app/frontend/apps/mobile/views/Home.vue

@@ -0,0 +1,38 @@
+<template>
+  <div>
+    <h1>Home</h1>
+    <p>{{ userData?.firstname }} {{ userData?.lastname }}</p>
+    <br />
+    <p v-on:click="logout">Logout</p>
+  </div>
+</template>
+
+<script setup lang="ts">
+import useNotifications from '@common/composables/useNotifications'
+import useAuthenticatedStore from '@common/stores/authenticated'
+import useSessionUserStore from '@common/stores/session/user'
+import { storeToRefs } from 'pinia'
+import { useRouter } from 'vue-router'
+
+// TODO ... testing the notification
+const { notify } = useNotifications()
+
+notify({
+  message: 'Hello Home!!!',
+  type: 'alert',
+})
+
+const sessionUser = useSessionUserStore()
+
+const { value: userData } = storeToRefs(sessionUser)
+
+const authenticated = useAuthenticatedStore()
+
+const router = useRouter()
+
+const logout = (): void => {
+  authenticated.logout().then(() => {
+    router.push('/login')
+  })
+}
+</script>

+ 33 - 0
app/frontend/apps/mobile/views/Login.vue

@@ -0,0 +1,33 @@
+<template>
+  <div>
+    <h1>Login</h1>
+    <p>Username: <input v-model="loginFormValues.login" type="text" /></p>
+    <br />
+    <p>
+      Password: <input v-model="loginFormValues.password" type="password" />
+    </p>
+    <br />
+    <button v-on:click="login">Login</button>
+  </div>
+</template>
+
+<script setup lang="ts">
+import useAuthenticationStore from '@common/stores/authenticated'
+import { useRouter } from 'vue-router'
+
+const authentication = useAuthenticationStore()
+const loginFormValues = {
+  login: '',
+  password: '',
+}
+
+const router = useRouter()
+
+const login = (): void => {
+  authentication
+    .login(loginFormValues.login, loginFormValues.password)
+    .then(() => {
+      router.replace('/')
+    })
+}
+</script>

+ 4 - 8
app/frontend/common/components/CommonHelloWorld.vue

@@ -1,14 +1,10 @@
-<script setup lang="ts">
-import { ref } from 'vue'
-
-defineProps<{ msg: string; show: boolean }>()
-
-const count = ref(0)
-</script>
-
 <template>
   <div v-if="show">
     <h1>{{ msg }}</h1>
     <p>The first component.</p>
   </div>
 </template>
+
+<script setup lang="ts">
+defineProps<{ msg: string; show: boolean }>()
+</script>

Some files were not shown because too many files changed in this diff