Got login working.

This commit is contained in:
Kalila L 2021-09-05 21:21:16 -04:00
parent 31fef9f9de
commit da464b2f40
26 changed files with 427 additions and 168 deletions

View file

@ -91,7 +91,7 @@ module.exports = {
'semi': ["error", "always"],
// TypeScript
quotes: ['warn', 'single', { avoidEscape: true }],
quotes: ['warn', 'double', { avoidEscape: true }],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
@ -101,6 +101,7 @@ module.exports = {
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/restrict-template-expressions': 'off',
// allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'

View file

@ -8,7 +8,7 @@
/* eslint-env node */
/* eslint-disable @typescript-eslint/no-var-requires */
const { configure } = require('quasar/wrappers');
const { configure } = require("quasar/wrappers");
module.exports = configure(function (ctx) {
return {
@ -17,7 +17,7 @@ module.exports = configure(function (ctx) {
tsCheckerConfig: {
eslint: {
enabled: true,
files: './src/**/*.{ts,tsx,js,jsx,vue}'
files: "./src/**/*.{ts,tsx,js,jsx,vue}"
}
}
},
@ -29,12 +29,12 @@ module.exports = configure(function (ctx) {
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli/boot-files
boot: [
'axios'
"axios"
],
// https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css
css: [
'app.scss'
"app.scss"
],
// https://github.com/quasarframework/quasar/tree/dev/extras
@ -47,13 +47,13 @@ module.exports = configure(function (ctx) {
// 'line-awesome',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
'roboto-font', // optional, you are not bound to it
'material-icons' // optional, you are not bound to it
"roboto-font", // optional, you are not bound to it
"material-icons" // optional, you are not bound to it
],
// Full list of options: https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build
build: {
vueRouterMode: 'hash', // available values: 'hash', 'history'
vueRouterMode: "hash", // available values: 'hash', 'history'
// transpile: false,
@ -87,7 +87,9 @@ module.exports = configure(function (ctx) {
// https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework
framework: {
config: {},
config: {
dark: 'auto'
},
// iconSet: 'material-icons', // Quasar icon set
// lang: 'en-US', // Quasar language pack
@ -100,12 +102,14 @@ module.exports = configure(function (ctx) {
// directives: [],
// Quasar plugins
plugins: []
plugins: [
'Notify'
]
},
// animations: 'all', // --- includes all animations
// https://v2.quasar.dev/options/animations
animations: 'all',
animations: "all",
// https://v2.quasar.dev/quasar-cli/developing-ssr/configuring-ssr
ssr: {
@ -125,14 +129,14 @@ module.exports = configure(function (ctx) {
},
middlewares: [
ctx.prod ? 'compression' : '',
'render' // keep this as last one
ctx.prod ? "compression" : "",
"render" // keep this as last one
]
},
// https://v2.quasar.dev/quasar-cli/developing-pwa/configuring-pwa
pwa: {
workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest'
workboxPluginMode: "GenerateSW", // 'GenerateSW' or 'InjectManifest'
workboxOptions: {}, // only for GenerateSW
// for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts])
@ -142,38 +146,38 @@ module.exports = configure(function (ctx) {
},
manifest: {
name: 'Vircadia Domain Dashboard',
short_name: 'Vircadia Domain Dashboard',
description: 'The Domain dashboard for Vircadia virtual worlds.',
display: 'standalone',
orientation: 'portrait',
background_color: '#ffffff',
theme_color: '#027be3',
name: "Vircadia Domain Dashboard",
short_name: "Vircadia Domain Dashboard",
description: "The Domain dashboard for Vircadia virtual worlds.",
display: "standalone",
orientation: "portrait",
background_color: "#ffffff",
theme_color: "#027be3",
icons: [
{
src: 'icons/icon-128x128.png',
sizes: '128x128',
type: 'image/png'
src: "icons/icon-128x128.png",
sizes: "128x128",
type: "image/png"
},
{
src: 'icons/icon-192x192.png',
sizes: '192x192',
type: 'image/png'
src: "icons/icon-192x192.png",
sizes: "192x192",
type: "image/png"
},
{
src: 'icons/icon-256x256.png',
sizes: '256x256',
type: 'image/png'
src: "icons/icon-256x256.png",
sizes: "256x256",
type: "image/png"
},
{
src: 'icons/icon-384x384.png',
sizes: '384x384',
type: 'image/png'
src: "icons/icon-384x384.png",
sizes: "384x384",
type: "image/png"
},
{
src: 'icons/icon-512x512.png',
sizes: '512x512',
type: 'image/png'
src: "icons/icon-512x512.png",
sizes: "512x512",
type: "image/png"
}
]
}
@ -191,7 +195,7 @@ module.exports = configure(function (ctx) {
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-electron-apps/configuring-electron
electron: {
bundler: 'packager', // 'packager' or 'builder'
bundler: "packager", // 'packager' or 'builder'
packager: {
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
@ -209,7 +213,7 @@ module.exports = configure(function (ctx) {
builder: {
// https://www.electron.build/configuration/configuration
appId: 'vircadia-domain-dashboard'
appId: "vircadia-domain-dashboard"
},
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain

View file

@ -2,9 +2,9 @@
<router-view />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent } from "vue";
export default defineComponent({
name: 'App'
name: "App"
});
</script>

View file

@ -1,7 +1,7 @@
import { boot } from 'quasar/wrappers';
import axios, { AxiosInstance } from 'axios';
import { boot } from "quasar/wrappers";
import axios, { AxiosInstance } from "axios";
declare module '@vue/runtime-core' {
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
$axios: AxiosInstance;
}
@ -13,7 +13,7 @@ declare module '@vue/runtime-core' {
// good idea to move this instance creation inside of the
// "export default () => {}" function below (which runs individually
// for each client)
const api = axios.create({ baseURL: 'https://api.example.com' });
const api = axios.create({ baseURL: "https://api.example.com" });
export default boot(({ app }) => {
// for use inside Vue files (Options API) through this.$axios and this.$api

View file

@ -0,0 +1,139 @@
<!--
// MetaverseLogin.vue
//
// Created by Kalila L. on May 18th, 2021.
// Copyright 2021 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-->
<template>
<q-form
@submit="onSubmit"
@reset="onReset"
class="q-gutter-md"
>
<q-input
v-model="username"
filled
dark
label="Username"
hint="Enter your username."
lazy-rules
:rules="[ val => val && val.length > 0 || 'Please enter a username.']"
/>
<q-input
v-model="password"
filled
dark
label="Password"
:type="showPassword ? 'text' : 'password'"
hint="Enter your password."
lazy-rules
:rules="[ val => val && val.length > 0 || 'Please enter a password.']"
>
<template v-slot:append>
<q-icon
:name="showPassword ? 'visibility' : 'visibility_off'"
class="cursor-pointer"
@click="showPassword = !showPassword"
/>
</template>
</q-input>
<div align="right">
<q-btn label="Reset" type="reset" color="primary" flat class="q-mr-sm" />
<q-btn label="Login" type="submit" color="primary"/>
</div>
</q-form>
</template>
<script>
const axios = require("axios");
import Log from "../../../modules/utilities/log";
export default {
name: "MetaverseLogin",
emits: ["loginResult"],
data: () => ({
username: "",
password: "",
showPassword: false,
DEFAULT_METAVERSE_URL: "https://metaverse.vircadia.com/live"
}),
methods: {
async onSubmit () {
try {
const metaverseURL = await this.retrieveMetaverseUrl();
const result = await this.attemptLogin(metaverseURL, this.username, this.password);
this.$q.notify({
type: "positive",
textColor: "white",
icon: "cloud_done",
message: `Welcome ${this.username}.`
});
this.$emit("loginResult", true, result);
} catch (result) {
this.$q.notify({
type: "negative",
textColor: "white",
icon: "warning",
message: `Login attempted failed: ${result.error}`
});
this.$emit("loginResult", false, result);
}
},
async retrieveMetaverseUrl () {
return new Promise((resolve, reject) => {
axios.get("/api/metaverse_info")
.then((response) => {
Log.info(Log.types.METAVERSE, `Retrieved Metaverse URL ${response.metaverse_url}.`);
resolve(response.metaverse_url);
}, (error) => {
Log.error(Log.types.METAVERSE, `Failed to retrieve Metaverse URL, using default URL ${this.DEFAULT_METAVERSE_URL} instead.`);
resolve(this.DEFAULT_METAVERSE_URL);
});
});
},
async attemptLogin (metaverse, username, password) {
Log.info(Log.types.METAVERSE, `Attempting to login as ${username}.`);
return new Promise((resolve, reject) => {
axios.post(`${metaverse}/oauth/token`, {
grant_type: "password",
scope: "owner", // as opposed to 'domain', we're asking for a user token
username: username,
password: password
})
.then((response) => {
Log.info(Log.types.METAVERSE, `Successfully got key and details for ${username}.`);
resolve(response.data);
}, (error) => {
Log.error(Log.types.METAVERSE, `Failed to get key and details for ${username}.`);
if (error.response && error.response.data) {
reject(error.response.data);
} else {
reject("Unknown reason.");
}
});
});
},
onReset () {
this.username = "";
this.password = "";
}
}
};
</script>

View file

@ -0,0 +1,44 @@
<!--
// ConnectMetaverse.vue
//
// Created by Kalila L. on Sep. 5th, 2021.
// Copyright 2021 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-->
<template>
<q-card-section>
<div class="row no-wrap items-center">
<div class="col text-h4 ellipsis">
Metaverse
</div>
</div>
</q-card-section>
<q-separator />
<q-card-section>
<MetaverseLogin></MetaverseLogin>
</q-card-section>
</template>
<script>
import MetaverseLogin from "../components/login/MetaverseLogin.vue";
export default {
name: "ConnectMetaverse",
components: {
MetaverseLogin
},
data: () => ({
}),
methods: {
}
};
</script>

View file

@ -1,7 +1,7 @@
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: string;
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
VUE_ROUTER_MODE: "hash" | "history" | "abstract" | undefined;
VUE_ROUTER_BASE: string | undefined;
}
}

View file

@ -9,12 +9,12 @@
</template>
<script>
import { defineComponent } from 'vue';
import { defineComponent } from "vue";
import * as THREE from 'three';
import * as THREE from "three";
export default defineComponent({
name: 'FirstTimeWizard',
name: "FirstTimeWizard",
data () {
return {
vantaBG: null,
@ -25,11 +25,11 @@ export default defineComponent({
},
async mounted () {
window.THREE = THREE;
this.vantaRings = (await import('vanta/dist/vanta.rings.min')).default;
this.vantaRings = (await import("vanta/dist/vanta.rings.min")).default;
this.initVanta();
visualViewport.addEventListener('resize', this.onResize);
visualViewport.addEventListener("resize", this.onResize);
},
methods: {
onResize () {
@ -48,7 +48,7 @@ export default defineComponent({
}
this.vantaBG = this.vantaRings({
el: '#vantaBG',
el: "#vantaBG",
mouseControls: false,
touchControls: false,
gyroControls: false,

View file

@ -0,0 +1,60 @@
/*
// log.js
//
// Created by Kalila L. on May 10th, 2021.
// Copyright 2021 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
/* eslint-disable */
const Log = (function () {
enum types {
OTHER = "[OTHER]",
METAVERSE = "[METAVERSE]"
}
enum levels {
ERROR = "[ERROR]",
DEBUG = "[DEBUG]",
WARN = "[WARN]",
INFO = "[INFO]"
}
function print (pType: types, pLevel: levels, pMsg: string): void {
console.info(pType, pLevel, pMsg);
}
// Print out message if debugging
function debug (pType: types, pMsg: string) {
print(pType, levels.DEBUG, pMsg);
}
function error (pType: types, pMsg: string) {
print(pType, levels.ERROR, pMsg);
}
function warn (pType: types, pMsg: string) {
print(pType, levels.WARN, pMsg);
}
function info (pType: types, pMsg: string) {
print(pType, levels.INFO, pMsg);
}
return {
// Tables
types,
levels,
// Functions
print,
debug,
error,
warn,
info
};
}());
export default Log;

View file

@ -23,9 +23,9 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent } from "vue";
export default defineComponent({
name: 'Error404'
name: "Error404"
});
</script>

View file

@ -63,6 +63,10 @@
.q-dialog__inner div {
box-shadow: none !important;
}
.q-dialog__inner div {
border: none !important;
}
</style>
<template>
@ -166,7 +170,7 @@
text-color="white"
icon-right="chevron_right"
>
Continue
Skip
</q-btn>
<q-btn
class="q-mb-md"
@ -235,6 +239,16 @@
</q-btn>
</q-card-actions>
</q-card>
<q-dialog v-model="connectMetaverseDialog">
<q-card
class="column no-wrap items-stretch q-pa-md"
style="background: rgba(0, 0, 0, 0.95);"
dark
>
<ConnectMetaverse></ConnectMetaverse>
</q-card>
</q-dialog>
</q-step>
</q-stepper>
</transition>
@ -244,10 +258,17 @@
</template>
<script>
import { defineComponent, ref } from 'vue';
import { defineComponent, ref } from "vue";
import ConnectMetaverse from "../../components/dialogs/ConnectMetaverse.vue";
export default defineComponent({
name: 'Index',
name: "Index",
components: {
ConnectMetaverse
},
data () {
return {
mainOverlay: true,
@ -255,33 +276,23 @@ export default defineComponent({
welcomeText: true,
mainWizard: true,
mainWizardStep: ref(1),
connectMetaverseDialog: false,
// Consts
WELCOME_TEXT_TIMEOUT: 2000,
MAIN_WIZARD_TRANSITION_TIME: 1000,
DEFAULT_METAVERSE_URL: "https://metaverse.vircadia.com/live"
};
},
mounted () {
setTimeout(() => {
this.mainWizardStep++;
}, this.WELCOME_TEXT_TIMEOUT);
},
methods: {
connectMetaverseTriggered () {
const axios = require('axios');
axios.get('/api/metaverse')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
this.connectMetaverseDialog = true;
}
}
});

View file

@ -3,9 +3,9 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent } from "vue";
export default defineComponent({
name: 'Index'
name: "Index"
});
</script>

View file

@ -1,12 +1,12 @@
import { route } from 'quasar/wrappers';
import { route } from "quasar/wrappers";
import {
createMemoryHistory,
createRouter,
createWebHashHistory,
createWebHistory
} from 'vue-router';
import { StateInterface } from '../store';
import routes from './routes';
} from "vue-router";
import { StateInterface } from "../store";
import routes from "./routes";
/*
* If not building with SSR mode, you can
@ -20,7 +20,7 @@ import routes from './routes';
export default route<StateInterface>(function (/* { store, ssrContext } */) {
const createHistory = process.env.SERVER
? createMemoryHistory
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory);
: (process.env.VUE_ROUTER_MODE === "history" ? createWebHistory : createWebHashHistory);
const Router = createRouter({
scrollBehavior: () => ({ left: 0, top: 0 }),
@ -30,7 +30,7 @@ export default route<StateInterface>(function (/* { store, ssrContext } */) {
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
history: createHistory(
process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE
process.env.MODE === "ssr" ? void 0 : process.env.VUE_ROUTER_BASE
)
});

View file

@ -1,22 +1,22 @@
import { RouteRecordRaw } from 'vue-router';
import { RouteRecordRaw } from "vue-router";
const routes: RouteRecordRaw[] = [
{
path: '/',
component: () => import('pages/Index.vue'),
children: [{ path: '', component: () => import('pages/Index.vue') }]
path: "/",
component: () => import("pages/Index.vue"),
children: [{ path: "", component: () => import("pages/Index.vue") }]
},
{
path: '/wizard',
component: () => import('layouts/FirstTimeWizard.vue'),
children: [{ path: '', component: () => import('pages/FirstTimeWizard/Index.vue') }]
path: "/wizard",
component: () => import("layouts/FirstTimeWizard.vue"),
children: [{ path: "", component: () => import("pages/FirstTimeWizard/Index.vue") }]
},
// Always leave this as last one,
// but you can also remove it
{
path: '/:catchAll(.*)*',
component: () => import('pages/Error404.vue')
path: "/:catchAll(.*)*",
component: () => import("pages/Error404.vue")
}
];

View file

@ -1,6 +1,6 @@
// Mocks all files ending in `.vue` showing them as plain Vue instances
declare module '*.vue' {
import { ComponentOptions } from 'vue';
declare module "*.vue" {
import { ComponentOptions } from "vue";
const component: ComponentOptions;
export default component;
}

View file

@ -1,39 +1,39 @@
import { store } from 'quasar/wrappers';
import { InjectionKey } from 'vue';
import { store } from "quasar/wrappers";
import { InjectionKey } from "vue";
import {
createStore,
Store as VuexStore,
useStore as vuexUseStore
} from 'vuex';
} from "vuex";
// import example from './module-example'
// import { ExampleStateInterface } from './module-example/state';
import { MainState } from "./modules/state";
/*
* If not building with SSR mode, you can
* directly export the Store instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Store instance.
*/
* If not building with SSR mode, you can
* directly export the Store instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Store instance.
*/
export interface StateInterface {
// Define your own store structure, using submodules if needed
// example: ExampleStateInterface;
// Declared as unknown to avoid linting issue. Best to strongly type as per the line above.
example: unknown
// Define your own store structure, using submodules if needed
// example: ExampleStateInterface;
// Declared as unknown to avoid linting issue. Best to strongly type as per the line above.
MainState: MainState
}
// provide typings for `this.$store`
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$store: VuexStore<StateInterface>
}
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
$store: VuexStore<StateInterface>
}
}
// provide typings for `useStore` helper
export const storeKey: InjectionKey<VuexStore<StateInterface>> = Symbol('vuex-key');
export const storeKey: InjectionKey<VuexStore<StateInterface>> = Symbol("vuex-key");
export default store(function (/* { ssrContext } */) {
const Store = createStore<StateInterface>({

View file

@ -1,11 +0,0 @@
import { ActionTree } from 'vuex';
import { StateInterface } from '../index';
import { ExampleStateInterface } from './state';
const actions: ActionTree<ExampleStateInterface, StateInterface> = {
someAction (/* context */) {
// your code
}
};
export default actions;

View file

@ -1,11 +0,0 @@
import { GetterTree } from 'vuex';
import { StateInterface } from '../index';
import { ExampleStateInterface } from './state';
const getters: GetterTree<ExampleStateInterface, StateInterface> = {
someAction (/* context */) {
// your code
}
};
export default getters;

View file

@ -1,16 +0,0 @@
import { Module } from 'vuex';
import { StateInterface } from '../index';
import state, { ExampleStateInterface } from './state';
import actions from './actions';
import getters from './getters';
import mutations from './mutations';
const exampleModule: Module<ExampleStateInterface, StateInterface> = {
namespaced: true,
actions,
getters,
mutations,
state
};
export default exampleModule;

View file

@ -1,10 +0,0 @@
import { MutationTree } from 'vuex';
import { ExampleStateInterface } from './state';
const mutation: MutationTree<ExampleStateInterface> = {
someMutation (/* state: ExampleStateInterface */) {
// your code
}
};
export default mutation;

View file

@ -1,11 +0,0 @@
export interface ExampleStateInterface {
prop: boolean;
}
function state (): ExampleStateInterface {
return {
prop: false
};
}
export default state;

View file

@ -0,0 +1,11 @@
import { ActionTree } from "vuex";
import { StateInterface } from "../index";
import { MainState } from "./state";
const actions: ActionTree<MainState, StateInterface> = {
someAction (/* context */) {
// your code
}
};
export default actions;

View file

@ -0,0 +1,11 @@
import { GetterTree } from "vuex";
import { StateInterface } from "../index";
import { MainState } from "./state";
const getters: GetterTree<MainState, StateInterface> = {
someAction (/* context */) {
// your code
}
};
export default getters;

View file

@ -0,0 +1,16 @@
import { Module } from "vuex";
import { StateInterface } from "../index";
import state, { MainState } from "./state";
import actions from "./actions";
import getters from "./getters";
import mutations from "./mutations";
const exampleModule: Module<MainState, StateInterface> = {
namespaced: true,
actions,
getters,
mutations,
state
};
export default exampleModule;

View file

@ -0,0 +1,10 @@
import { MutationTree } from "vuex";
import { MainState } from "./state";
const mutation: MutationTree<MainState> = {
someMutation (/* state: ExampleStateInterface */) {
// your code
}
};
export default mutation;

View file

@ -0,0 +1,11 @@
export interface MainState {
prop: boolean;
}
function state (): MainState {
return {
prop: false
};
}
export default state;