diff options
-rw-r--r-- | api/routes/github.js | 2 | ||||
-rw-r--r-- | api/services/github/api.js | 9 | ||||
-rw-r--r-- | api/services/github/installations.js | 21 | ||||
-rw-r--r-- | application/components/app.vue | 24 | ||||
-rw-r--r-- | application/components/initialize.vue | 95 | ||||
-rw-r--r-- | package-lock.json | 14 | ||||
-rw-r--r-- | package.json | 1 |
7 files changed, 149 insertions, 17 deletions
diff --git a/api/routes/github.js b/api/routes/github.js index 9594b5c..a1f23b4 100644 --- a/api/routes/github.js +++ b/api/routes/github.js @@ -73,7 +73,7 @@ const getInstallation = async (req, res, next) => { return res.json({ installation: null }) } - const { data: { repositories } } = await fetchInstallationRepos(user.oauth_access_token, installation.id) + const repositories = await fetchInstallationRepos(user.oauth_access_token, installation.id) res.json({ installation, repositories }) } catch (err) { diff --git a/api/services/github/api.js b/api/services/github/api.js index b9bf600..bfaad61 100644 --- a/api/services/github/api.js +++ b/api/services/github/api.js @@ -1,7 +1,8 @@ const axios = require('axios') const baseUrl = 'https://api.github.com' -function request (options={}) { + +async function request (options={}) { if (typeof options === 'string') { options = { url: options @@ -20,7 +21,11 @@ function request (options={}) { options.headers.Authorization = `Bearer ${options.token}` } - return axios(options) + const response = await axios(options) + + console.log(response.headers['x-ratelimit-remaining']) + + return response } module.exports = { diff --git a/api/services/github/installations.js b/api/services/github/installations.js index 73b1764..346dc45 100644 --- a/api/services/github/installations.js +++ b/api/services/github/installations.js @@ -1,3 +1,5 @@ +const linkHeader = require('http-link-header') + const api = require('./api') const { createAppToken } = require('./auth') @@ -12,11 +14,20 @@ function fetchInstallation (user) { }) } -function fetchInstallationRepos (installationToken, installationId) { - return api.request({ - url: `/user/installations/${installationId}/repositories`, - token: installationToken - }) +async function fetchInstallationRepos (installationToken, installationId) { + const initialPage = `/user/installations/${installationId}/repositories` + const repositories = [] + + let url = initialPage + while (url) { + console.log('fetching page', url) + const { headers, data } = await api.request({ url, token: installationToken }) + const paging = linkHeader.parse(headers.link || '') + repositories.push(...data.repositories) + url = paging.get('rel', 'next')?.[0]?.uri + } + + return repositories } module.exports = { diff --git a/application/components/app.vue b/application/components/app.vue index 6aed63d..ac4348d 100644 --- a/application/components/app.vue +++ b/application/components/app.vue @@ -1,6 +1,6 @@ <script> -import keyBy from 'lodash/keyBy' +import Initialize from './initialize.vue' import Keymap from './keymap.vue' import Loader from './loader.vue' import Modal from './modal.vue' @@ -8,14 +8,10 @@ import Modal from './modal.vue' import * as config from '../config' import * as github from '../github' -import { healthcheck, loadBehaviours } from '../api' -import { loadLayout } from '../layout.js' -import { loadKeymap } from '../keymap.js' -import { loadKeycodes } from '../keycodes' - export default { components: { keymap: Keymap, + Initialize, Loader, Modal }, @@ -131,7 +127,7 @@ export default { </script> <template> - <loader :load="doReadyCheck"> + <initialize v-slot="{ keymap, layout }"> <div v-if="tooManyRepos"> <modal> <div class="dialog"> @@ -145,6 +141,7 @@ export default { </div> </modal> </div> + <div v-else-if="loadKeyboardError === 'InvalidRepoError'"> <modal> <div class="dialog"> @@ -158,8 +155,13 @@ export default { </div> </modal> </div> + <template v-else> - <keymap :layout="layout" :keymap="editingKeymap.keyboard ? editingKeymap : keymap" @update="handleUpdateKeymap" /> + <keymap + :layout="layout" + :keymap="editingKeymap.keyboard ? editingKeymap : keymap" + @update="handleUpdateKeymap" + /> <div id="actions"> <button v-if="config.enableLocal" @@ -168,12 +170,15 @@ export default { :disabled="!this.editingKeymap.keyboard" @click="handleCompile" /> + <button v-if="config.enableGitHub && !githubAuthorized" v-text="`Authorize GitHub`" @click="handleGithubAuthorize" title="Install as a GitHub app to edit a zmk-config repository." + /> + <button v-if="config.enableGitHub && githubAuthorized" v-text="`Commit Changes`" @@ -183,10 +188,11 @@ export default { /> </div> </template> + <a class="github-link" href="https://github.com/nickcoutsos/keymap-editor"> <i class="fab fa-github" />/nickcoutsos/keymap-editor </a> - </loader> + </initialize> </template> <style scoped> diff --git a/application/components/initialize.vue b/application/components/initialize.vue new file mode 100644 index 0000000..0622fa7 --- /dev/null +++ b/application/components/initialize.vue @@ -0,0 +1,95 @@ +<script> +import keyBy from 'lodash/keyBy' + +import * as config from '../config' +import * as github from '../github' + +import { healthcheck, loadBehaviours } from '../api' +import { loadLayout } from '../layout.js' +import { loadKeymap } from '../keymap.js' +import { loadKeycodes } from '../keycodes' + +import Loader from './loader.vue' + +export default { + name: 'Initialize', + components: { Loader }, + data () { + return { + keycodes: [], + behaviours: [], + indexedKeycodes: {}, + indexedBehaviours: {}, + repositories: [], + selectedRepository: null, + keymap: {}, + layers: [], + layout: [] + } + }, + provide() { + return { + keycodes: this.keycodes, + behaviours: this.behaviours, + indexedKeycodes: this.indexedKeycodes, + indexedBehaviours: this.indexedBehaviours + } + }, + methods: { + async doReadyCheck() { + await healthcheck() + await this.loadAppData() + await this.loadKeyboardData() + }, + async loadAppData () { + await github.init() + const [ keycodes, behaviours ] = await Promise.all([ + loadKeycodes(), + loadBehaviours() + ]) + + this.keycodes.splice(0, this.keycodes.length, ...keycodes) + this.behaviours.splice(0, this.behaviours.length, ...behaviours) + Object.assign(this.indexedKeycodes, keyBy(this.keycodes, 'code')) + Object.assign(this.indexedBehaviours, keyBy(this.behaviours, 'code')) + }, + async loadKeyboardData() { + const loadKeyboardData = async () => { + if (config.enableGitHub && github.isGitHubAuthorized()) { + return github.fetchLayoutAndKeymap() + } else if (config.enableLocal) { + const [layout, keymap] = await Promise.all([ + loadLayout(), + loadKeymap() + ]) + return { layout, keymap } + } else { + return { layout: [], keymap: { layers: [] } } + } + } + + const { layout, keymap } = await loadKeyboardData() + + this.layout.splice(0, this.layout.length, ...layout.map(key => ( + { ...key, u: key.u || key.w || 1, h: key.h || 1 } + ))) + + const layerNames = keymap.layer_names || keymap.layers.map((_, i) => `Layer ${i}`) + Object.assign(this.layers, keymap.layers) + Object.assign(this.keymap, keymap, { + layer_names: layerNames + }) + } + } +} +</script> + +<template> + <loader :load="doReadyCheck"> + <slot + :keymap="keymap" + :layers="layers" + :layout="layout" + /> + </loader> +</template>
\ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c0a8b7f..1aa391f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "dotenv": "^10.0.0", "express": "^4.17.1", "express-ws": "^4.0.0", + "http-link-header": "^1.0.3", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.21", "morgan": "^1.10.0" @@ -323,6 +324,14 @@ "node": ">= 0.6" } }, + "node_modules/http-link-header": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.0.3.tgz", + "integrity": "sha512-nARK1wSKoBBrtcoESlHBx36c1Ln/gnbNQi1eB6MeTUefJIT3NvUOsV15bClga0k38f0q/kN5xxrGSDS3EFnm9w==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -959,6 +968,11 @@ "toidentifier": "1.0.0" } }, + "http-link-header": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.0.3.tgz", + "integrity": "sha512-nARK1wSKoBBrtcoESlHBx36c1Ln/gnbNQi1eB6MeTUefJIT3NvUOsV15bClga0k38f0q/kN5xxrGSDS3EFnm9w==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", diff --git a/package.json b/package.json index dbf6627..a68c1ff 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "dotenv": "^10.0.0", "express": "^4.17.1", "express-ws": "^4.0.0", + "http-link-header": "^1.0.3", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.21", "morgan": "^1.10.0" |