Migration to typescript: first step
Add vue cli typescript support Rename .js to .ts Use class and annotations in App and NavBar Add tslint
This commit is contained in:
parent
da817d35c4
commit
b409a5583d
@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'plugin:vue/essential',
|
||||
'@vue/airbnb',
|
||||
],
|
||||
};
|
516
js/package-lock.json
generated
516
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,16 +2,13 @@
|
||||
"name": "mobilizon",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build --modern",
|
||||
"lint": "vue-cli-service lint",
|
||||
"analyze-bundle": "npm run build -- --report-json && webpack-bundle-analyzer ../priv/static/report.json",
|
||||
"dev": "vue-cli-service serve",
|
||||
"test:e2e": "vue-cli-service test:e2e",
|
||||
"test:unit": "vue-cli-service test:unit",
|
||||
"analyze-bundle": "npm run build -- --report-json && webpack-bundle-analyzer ../priv/static/report.json"
|
||||
"test:unit": "vue-cli-service test:unit"
|
||||
},
|
||||
"dependencies": {
|
||||
"apollo-absinthe-upload-link": "^1.4.0",
|
||||
@ -26,27 +23,35 @@
|
||||
"register-service-worker": "^1.4.1",
|
||||
"vue": "^2.5.17",
|
||||
"vue-apollo": "^3.0.0-beta.26",
|
||||
"vue-class-component": "^6.3.2",
|
||||
"vue-gettext": "^2.1.1",
|
||||
"vue-gravatar": "^1.3.0",
|
||||
"vue-markdown": "^2.2.4",
|
||||
"vue-property-decorator": "^7.2.0",
|
||||
"vue-router": "^3.0.2",
|
||||
"vuetify": "^1.3.9",
|
||||
"vuetify-google-autocomplete": "^2.0.0-beta.5",
|
||||
"vuex": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.0",
|
||||
"@types/mocha": "^5.2.4",
|
||||
"@vue/cli-plugin-babel": "^3.1.1",
|
||||
"@vue/cli-plugin-e2e-nightwatch": "^3.1.1",
|
||||
"@vue/cli-plugin-eslint": "^3.1.5",
|
||||
"@vue/cli-plugin-pwa": "^3.1.2",
|
||||
"@vue/cli-plugin-typescript": "^3.2.0",
|
||||
"@vue/cli-plugin-unit-mocha": "^3.1.1",
|
||||
"@vue/cli-service": "^3.1.4",
|
||||
"@vue/eslint-config-airbnb": "^3.0.5",
|
||||
"@vue/eslint-config-typescript": "^3.1.0",
|
||||
"@vue/test-utils": "^1.0.0-beta.26",
|
||||
"chai": "^4.2.0",
|
||||
"dotenv-webpack": "^1.5.7",
|
||||
"node-sass": "^4.10.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"tslint-config-airbnb": "^5.11.1",
|
||||
"typescript": "^3.0.0",
|
||||
"vue-cli-plugin-apollo": "^0.17.4",
|
||||
"vue-template-compiler": "^2.5.17",
|
||||
"webpack-bundle-analyzer": "^3.0.3"
|
||||
@ -55,5 +60,8 @@
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
]
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
}
|
||||
|
104
js/src/App.vue
104
js/src/App.vue
@ -134,7 +134,8 @@
|
||||
class="white--text"
|
||||
v-translate="{
|
||||
date: new Date().getFullYear(),
|
||||
}">© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix & <a href="https://vuejs.org/">VueJS</a> & <a href="https://www.vuetifyjs.com/">Vuetify</a> with some love and some weeks
|
||||
}">© The Mobilizon Contributors %{date} - Made with Elixir, Phoenix & <a href="https://vuejs.org/">VueJS</a> & <a
|
||||
href="https://www.vuetifyjs.com/">Vuetify</a> with some love and some weeks
|
||||
</span>
|
||||
</v-footer>
|
||||
<v-snackbar
|
||||
@ -148,75 +149,78 @@
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag';
|
||||
import NavBar from '@/components/NavBar';
|
||||
import { AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
|
||||
<script lang="ts">
|
||||
import NavBar from '@/components/NavBar.vue';
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import { AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
@Component({
|
||||
components: {
|
||||
NavBar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
drawer: false,
|
||||
fab: false,
|
||||
user: localStorage.getItem(AUTH_USER_ID),
|
||||
items: [
|
||||
NavBar
|
||||
}
|
||||
})
|
||||
export default class App extends Vue {
|
||||
drawer = false
|
||||
fab = false
|
||||
user = localStorage.getItem(AUTH_USER_ID)
|
||||
items = [
|
||||
{
|
||||
icon: 'poll', text: 'Events', route: 'EventList', role: null,
|
||||
icon: 'poll', text: 'Events', route: 'EventList', role: null
|
||||
},
|
||||
{
|
||||
icon: 'group', text: 'Groups', route: 'GroupList', role: null,
|
||||
icon: 'group', text: 'Groups', route: 'GroupList', role: null
|
||||
},
|
||||
{
|
||||
icon: 'content_copy', text: 'Categories', route: 'CategoryList', role: 'ROLE_ADMIN',
|
||||
icon: 'content_copy', text: 'Categories', route: 'CategoryList', role: 'ROLE_ADMIN'
|
||||
},
|
||||
{ icon: 'settings', text: 'Settings', role: 'ROLE_USER' },
|
||||
{ icon: 'chat_bubble', text: 'Send feedback', role: 'ROLE_USER' },
|
||||
{ icon: 'help', text: 'Help', role: null },
|
||||
{ icon: 'phonelink', text: 'App downloads', role: null },
|
||||
],
|
||||
error: {
|
||||
{ icon: 'phonelink', text: 'App downloads', role: null }
|
||||
]
|
||||
error = {
|
||||
timeout: 3000,
|
||||
show: false,
|
||||
text: '',
|
||||
},
|
||||
show_new_event_button: false,
|
||||
actor: localStorage.getItem(AUTH_USER_ACTOR),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showMenuItem(elem) {
|
||||
return elem !== null && this.user && this.user.roles !== undefined ? this.user.roles.includes(elem) : true;
|
||||
},
|
||||
getUser() {
|
||||
return this.user === undefined ? false : this.user;
|
||||
},
|
||||
toggleDrawer() {
|
||||
this.drawer = !this.drawer;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
displayed_name() {
|
||||
return this.actor.display_name === null ? this.actor.username : this.actor.display_name;
|
||||
},
|
||||
},
|
||||
};
|
||||
text: ''
|
||||
}
|
||||
|
||||
show_new_event_button = false
|
||||
actor = localStorage.getItem(AUTH_USER_ACTOR)
|
||||
|
||||
get displayed_name () {
|
||||
// FIXME: load actor
|
||||
return 'no implemented'
|
||||
// return this.actor.display_name === null ? this.actor.username : this.actor.display_name
|
||||
}
|
||||
|
||||
showMenuItem (elem) {
|
||||
// FIXME: load actor
|
||||
return false
|
||||
// return elem !== null && this.user && this.user.roles !== undefined ? this.user.roles.includes(elem) : true
|
||||
}
|
||||
|
||||
getUser () {
|
||||
return this.user === undefined ? false : this.user
|
||||
}
|
||||
|
||||
toggleDrawer () {
|
||||
this.drawer = !this.drawer
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.router-enter-active, .router-leave-active {
|
||||
.router-enter-active, .router-leave-active {
|
||||
transition-property: opacity;
|
||||
transition-duration: .25s;
|
||||
}
|
||||
}
|
||||
|
||||
.router-enter-active {
|
||||
.router-enter-active {
|
||||
transition-delay: .25s;
|
||||
}
|
||||
}
|
||||
|
||||
.router-enter, .router-leave-active {
|
||||
.router-enter, .router-leave-active {
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -74,42 +74,33 @@
|
||||
</v-list>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn flat @click="notificationMenu = false"><translate>Close</translate></v-btn>
|
||||
<v-btn color="primary" flat @click="notificationMenu = false"><translate>Save</translate></v-btn>
|
||||
<v-btn flat @click="notificationMenu = false">
|
||||
<translate>Close</translate>
|
||||
</v-btn>
|
||||
<v-btn color="primary" flat @click="notificationMenu = false">
|
||||
<translate>Save</translate>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-menu>
|
||||
<v-btn v-if="!user" :to="{ name: 'Login' }"><translate>Login</translate></v-btn>
|
||||
<v-btn v-if="!user" :to="{ name: 'Login' }">
|
||||
<translate>Login</translate>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {AUTH_USER_ACTOR, AUTH_USER_ID} from '@/constants';
|
||||
import {SEARCH} from '@/graphql/search';
|
||||
<style>
|
||||
nav.v-toolbar .v-input__slot {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
export default {
|
||||
name: 'NavBar',
|
||||
props: {
|
||||
toggleDrawer: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
notificationMenu: false,
|
||||
notifications: [
|
||||
{ header: 'Coucou' },
|
||||
{ title: "T'as une notification", subtitle: 'Et elle est cool' },
|
||||
],
|
||||
model: null,
|
||||
search: [],
|
||||
searchText: null,
|
||||
searchSelect: null,
|
||||
actor: localStorage.getItem(AUTH_USER_ACTOR),
|
||||
user: localStorage.getItem(AUTH_USER_ID),
|
||||
};
|
||||
},
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||
import { AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
|
||||
import { SEARCH } from '@/graphql/search';
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
search: {
|
||||
query: SEARCH,
|
||||
@ -122,21 +113,24 @@ export default {
|
||||
return !this.searchText;
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
model(val) {
|
||||
switch(val.__typename) {
|
||||
case 'Event':
|
||||
this.$router.push({ name: 'Event', params: { uuid: val.uuid } });
|
||||
break;
|
||||
case 'Actor':
|
||||
this.$router.push({ name: 'Account', params: { name: this.username_with_domain(val) } });
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
items() {
|
||||
})
|
||||
export default class NavBar extends Vue {
|
||||
@Prop({ required: true, type: Function }) toggleDrawer!: Function;
|
||||
|
||||
notificationMenu = false;
|
||||
notifications = [
|
||||
{ header: 'Coucou' },
|
||||
{ title: 'T\'as une notification', subtitle: 'Et elle est cool' },
|
||||
];
|
||||
model = null;
|
||||
search: any[] = [];
|
||||
searchText: string | null = null;
|
||||
searchSelect = null;
|
||||
actor: string | null = localStorage.getItem(AUTH_USER_ACTOR);
|
||||
user: string | null = localStorage.getItem(AUTH_USER_ID);
|
||||
|
||||
get items() {
|
||||
return this.search.map(searchEntry => {
|
||||
switch (searchEntry.__typename) {
|
||||
case 'Actor':
|
||||
@ -148,22 +142,29 @@ export default {
|
||||
}
|
||||
return searchEntry;
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
|
||||
@Watch('model')
|
||||
onModelChanged(val) {
|
||||
switch (val.__typename) {
|
||||
case 'Event':
|
||||
this.$router.push({ name: 'Event', params: { uuid: val.uuid } });
|
||||
break;
|
||||
case 'Actor':
|
||||
this.$router.push({ name: 'Account', params: { name: this.username_with_domain(val) } });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
username_with_domain(actor) {
|
||||
return actor.preferredUsername + (actor.domain === null ? '' : `@${actor.domain}`);
|
||||
},
|
||||
}
|
||||
|
||||
enter() {
|
||||
console.log('enter');
|
||||
this.$apollo.queries.search.refetch();
|
||||
this.$apollo.queries[ 'search' ].refetch();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
nav.v-toolbar .v-input__slot {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
</script>
|
||||
|
@ -11,14 +11,16 @@ import 'vuetify/dist/vuetify.min.css';
|
||||
import App from '@/App.vue';
|
||||
import router from '@/router';
|
||||
// import store from './store';
|
||||
import translations from '@/i18n/translations.json';
|
||||
import { createProvider } from './vue-apollo';
|
||||
|
||||
const translations = require('@/i18n/translations.json');
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
Vue.use(VueMarkdown);
|
||||
Vue.use(Vuetify);
|
||||
const language = window.navigator.userLanguage || window.navigator.language;
|
||||
|
||||
const language = (window.navigator as any).userLanguage || window.navigator.language;
|
||||
moment.locale(language);
|
||||
|
||||
Vue.filter('formatDate', value => (value ? moment(String(value)).format('LLLL') : null));
|
||||
@ -33,8 +35,8 @@ Vue.config.language = language.replace('-', '_');
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
el: '#app',
|
||||
template: '<App/>',
|
||||
apolloProvider: createProvider(),
|
||||
components: { App },
|
@ -1,23 +1,23 @@
|
||||
import Vue from 'vue';
|
||||
import Router from 'vue-router';
|
||||
import PageNotFound from '@/components/PageNotFound';
|
||||
import Home from '@/components/Home';
|
||||
import Event from '@/components/Event/Event';
|
||||
import EventList from '@/components/Event/EventList';
|
||||
import Location from '@/components/Location';
|
||||
import CreateEvent from '@/components/Event/Create';
|
||||
import CategoryList from '@/components/Category/List';
|
||||
import CreateCategory from '@/components/Category/Create';
|
||||
import Register from '@/components/Account/Register';
|
||||
import Login from '@/components/Account/Login';
|
||||
import Validate from '@/components/Account/Validate';
|
||||
import ResendConfirmation from '@/components/Account/ResendConfirmation';
|
||||
import SendPasswordReset from '@/components/Account/SendPasswordReset';
|
||||
import PasswordReset from '@/components/Account/PasswordReset';
|
||||
import Account from '@/components/Account/Account';
|
||||
import CreateGroup from '@/components/Group/Create';
|
||||
import Group from '@/components/Group/Group';
|
||||
import GroupList from '@/components/Group/GroupList';
|
||||
import PageNotFound from '@/components/PageNotFound.vue';
|
||||
import Home from '@/components/Home.vue';
|
||||
import Event from '@/components/Event/Event.vue';
|
||||
import EventList from '@/components/Event/EventList.vue';
|
||||
import Location from '@/components/Location.vue';
|
||||
import CreateEvent from '@/components/Event/Create.vue';
|
||||
import CategoryList from '@/components/Category/List.vue';
|
||||
import CreateCategory from '@/components/Category/Create.vue';
|
||||
import Register from '@/components/Account/Register.vue';
|
||||
import Login from '@/components/Account/Login.vue';
|
||||
import Validate from '@/components/Account/Validate.vue';
|
||||
import ResendConfirmation from '@/components/Account/ResendConfirmation.vue';
|
||||
import SendPasswordReset from '@/components/Account/SendPasswordReset.vue';
|
||||
import PasswordReset from '@/components/Account/PasswordReset.vue';
|
||||
import Account from '@/components/Account/Account.vue';
|
||||
import CreateGroup from '@/components/Group/Create.vue';
|
||||
import Group from '@/components/Group/Group.vue';
|
||||
import GroupList from '@/components/Group/GroupList.vue';
|
||||
import Identities from '../components/Account/Identities.vue';
|
||||
|
||||
Vue.use(Router);
|
13
js/src/shims-tsx.d.ts
vendored
Normal file
13
js/src/shims-tsx.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
import Vue, { VNode } from 'vue'
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
// tslint:disable no-empty-interface
|
||||
interface Element extends VNode {}
|
||||
// tslint:disable no-empty-interface
|
||||
interface ElementClass extends Vue {}
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any
|
||||
}
|
||||
}
|
||||
}
|
4
js/src/shims-vue.d.ts
vendored
Normal file
4
js/src/shims-vue.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue'
|
||||
export default Vue
|
||||
}
|
@ -33,7 +33,6 @@ const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
|
||||
const cache = new InMemoryCache({ fragmentMatcher });
|
||||
|
||||
|
||||
const authMiddleware = new ApolloLink((operation, forward) => {
|
||||
// add the authorization to the headers
|
||||
const token = localStorage.getItem(AUTH_TOKEN);
|
||||
@ -43,7 +42,9 @@ const authMiddleware = new ApolloLink((operation, forward) => {
|
||||
},
|
||||
});
|
||||
|
||||
return forward(operation);
|
||||
if (forward) forward(operation);
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
const uploadLink = createLink({
|
||||
@ -60,6 +61,8 @@ const link = authMiddleware.concat(uploadLink);
|
||||
|
||||
// Config
|
||||
const defaultOptions = {
|
||||
cache,
|
||||
link,
|
||||
// You can use `https` for secure connection (recommended in production)
|
||||
httpEndpoint,
|
||||
// You can use `wss` for secure connection (recommended in production)
|
||||
@ -74,9 +77,8 @@ const defaultOptions = {
|
||||
websocketsOnly: false,
|
||||
// Is being rendered on the server?
|
||||
ssr: false,
|
||||
cache,
|
||||
link,
|
||||
defaultHttpLink: false,
|
||||
connectToDevTools: true,
|
||||
};
|
||||
|
||||
// Call this in the Vue app file
|
||||
@ -89,23 +91,18 @@ export function createProvider(options = {}) {
|
||||
apolloClient.wsClient = wsClient;
|
||||
|
||||
// Create vue apollo provider
|
||||
const apolloProvider = new VueApollo({
|
||||
return new VueApollo({
|
||||
defaultClient: apolloClient,
|
||||
link,
|
||||
cache,
|
||||
connectToDevTools: true,
|
||||
defaultOptions: {
|
||||
$query: {
|
||||
// defaultOptions: {
|
||||
// $query: {
|
||||
// fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
},
|
||||
// },
|
||||
// },
|
||||
errorHandler(error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message);
|
||||
},
|
||||
});
|
||||
|
||||
return apolloProvider;
|
||||
}
|
||||
|
||||
// Manually call this when user log in
|
@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
mocha: true,
|
||||
},
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
},
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import { shallow } from '@vue/test-utils';
|
||||
import HelloWorld from '@/components/HelloWorld.vue';
|
||||
|
||||
describe('HelloWorld.vue', () => {
|
||||
it('renders props.msg when passed', () => {
|
||||
const msg = 'new message';
|
||||
const wrapper = shallow(HelloWorld, {
|
||||
propsData: { msg },
|
||||
});
|
||||
expect(wrapper.text()).to.include(msg);
|
||||
});
|
||||
});
|
42
js/tsconfig.json
Normal file
42
js/tsconfig.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env",
|
||||
"mocha",
|
||||
"chai"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
7
js/tslint.json
Normal file
7
js/tslint.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "tslint-config-airbnb",
|
||||
"rules": {
|
||||
"max-line-length": [ true, 140 ],
|
||||
"import-name": false
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user