diff --git a/src/public/javascripts/services/note_type.js b/src/public/javascripts/services/note_type.js index 9665bb7cd..3137ad340 100644 --- a/src/public/javascripts/services/note_type.js +++ b/src/public/javascripts/services/note_type.js @@ -50,7 +50,26 @@ const DEFAULT_MIME_TYPES = [ { mime: 'text/x-yaml', title: 'YAML' } ]; -let mimeTypes = DEFAULT_MIME_TYPES; +let mimeTypes = null; + +async function getMimeTypes() { + if (!mimeTypes) { + let customCodeMimeTypes = []; + + try { + customCodeMimeTypes = await server.get('custom-code-mime-types'); + } + catch (e) { + log.error(`Could not retrieve custom mime types: ${e.message}`); + } + + mimeTypes = DEFAULT_MIME_TYPES.concat(customCodeMimeTypes); + + mimeTypes.sort((a, b) => a.title < b.title ? -1 : 1); + } + + return mimeTypes; +} export default class NoteTypeContext { /** @@ -68,15 +87,15 @@ export default class NoteTypeContext { this.$renderButton = ctx.$tabContent.find('.render-button'); } - update() { + async update() { this.$noteTypeButton.prop("disabled", () => ["file", "image", "search"].includes(this.ctx.note.type)); - this.$noteTypeDesc.text(this.findTypeTitle(this.ctx.note.type)); + this.$noteTypeDesc.text(await this.findTypeTitle(this.ctx.note.type, this.ctx.note.mime)); } /** actual body is rendered lazily on note-type button click */ - renderDropdown() { + async renderDropdown() { this.$noteTypeDropdown.empty(); for (const noteType of NOTE_TYPES.filter(nt => nt.selectable)) { @@ -102,7 +121,7 @@ export default class NoteTypeContext { } } - for (const mimeType of mimeTypes) { + for (const mimeType of await getMimeTypes()) { const $mimeLink = $('') .attr("data-mime-type", mimeType.mime) .append(' ') @@ -122,10 +141,18 @@ export default class NoteTypeContext { this.$renderButton.toggle(this.ctx.note.type === 'render'); } - findTypeTitle(type) { - const noteType = NOTE_TYPES.find(nt => nt.type === type); + async findTypeTitle(type, mime) { + if (type === 'code') { + const mimeTypes = await getMimeTypes(); + const found = mimeTypes.find(mt => mt.mime === mime); - return noteType ? noteType.title : type; + return found ? found.title : mime; + } + else { + const noteType = NOTE_TYPES.find(nt => nt.type === type); + + return noteType ? noteType.title : type; + } } async save(type, mime) { diff --git a/src/routes/api/custom_code_mime_types.js b/src/routes/api/custom_code_mime_types.js new file mode 100644 index 000000000..8d976df73 --- /dev/null +++ b/src/routes/api/custom_code_mime_types.js @@ -0,0 +1,24 @@ +"use strict"; + +const attributesService = require('../../services/attributes'); +const log = require('../../services/log'); + +async function get() { + const notes = await attributesService.getNotesWithLabel('codeMimeTypes'); + let merged = []; + + for (const note of notes) { + try { + merged = merged.concat(await note.getJsonContent()); + } + catch (e) { + log.error(`Cannot merge mime types from note=${note.noteId}: ${e.message}`); + } + } + + return merged; +} + +module.exports = { + get +}; \ No newline at end of file diff --git a/src/routes/routes.js b/src/routes/routes.js index 78aca685d..a5898fbcf 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -35,6 +35,7 @@ const dateNotesRoute = require('./api/date_notes'); const linkMapRoute = require('./api/link_map'); const linksRoute = require('./api/links'); const clipperRoute = require('./api/clipper'); +const customCodeMimeTypesRoute = require('./api/custom_code_mime_types.js'); const log = require('../services/log'); const express = require('express'); @@ -238,6 +239,8 @@ function register(app) { route(POST, '/api/clipper/notes', clipperMiddleware, clipperRoute.createNote, apiResultHandler); route(POST, '/api/clipper/open/:noteId', clipperMiddleware, clipperRoute.openNote, apiResultHandler); + apiRoute(GET, '/api/custom-code-mime-types', customCodeMimeTypesRoute.get); + app.use('', router); }