« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--todo.txt1
-rw-r--r--ui.js76
2 files changed, 63 insertions, 14 deletions
diff --git a/todo.txt b/todo.txt
index 759fbbf..f56bd7a 100644
--- a/todo.txt
+++ b/todo.txt
@@ -454,3 +454,4 @@ TODO: I lied when I said I finished text editor stuff. Still to-do:
 
 TODO: Have a context menu option for creating a text file for notes on a
       playable, when there is no existing adjacent file.
+      (Done!)
diff --git a/ui.js b/ui.js
index 70ac1e8..2d3ae1f 100644
--- a/ui.js
+++ b/ui.js
@@ -699,20 +699,7 @@ class AppElement extends FocusElement {
 
     grouplikeListing.pathElement.on('select', item => this.reveal(item))
     grouplikeListing.on('menu', (item, el) => this.showMenuForItemElement(el, grouplikeListing))
-
-    grouplikeListing.on('selected', async item => {
-      const status = await this.textEditor.openItem(item, {
-        doubleCheckItem: () => item === grouplikeListing.currentItem
-      })
-
-      if (status === true) {
-        this.textInfoPane.visible = true
-        this.fixLayout()
-      } else if (status === false) {
-        this.textInfoPane.visible = false
-        this.fixLayout()
-      }
-    })
+    grouplikeListing.on('selected', item => this.editNotesFile(item, false))
   }
 
   showContextMenu(opts) {
@@ -833,6 +820,64 @@ class AppElement extends FocusElement {
     }
   }
 
+  async createNotesFile(item) {
+    if (!item[parentSymbol]) {
+      return
+    }
+
+    if (!item.url) {
+      return
+    }
+
+    let itemPath
+    try {
+      itemPath = url.fileURLToPath(item.url)
+    } catch (error) {
+      return
+    }
+
+    const dirname = path.dirname(itemPath)
+    const extname = path.extname(itemPath)
+    const basename = path.basename(itemPath, extname)
+    const name = basename + '.txt'
+    const filePath = path.join(dirname, name)
+    const fileURL = url.pathToFileURL(filePath).toString()
+    const file = {name, url: fileURL}
+    await writeFile(filePath, '\n')
+
+    const { items } = item[parentSymbol]
+    items.splice(items.indexOf(item), 0, file)
+    await this.editNotesFile(item, true)
+  }
+
+  async editNotesFile(item, focus) {
+    if (!item) {
+      return
+    }
+
+    const doubleCheckItem = () => item === this.tabber.currentElement.currentItem
+
+    const status = await this.textEditor.openItem(item, {doubleCheckItem})
+
+    let fixLayout
+    if (status === true) {
+      this.textInfoPane.visible = true
+      fixLayout = true
+    } else if (status === false) {
+      this.textInfoPane.visible = false
+      fixLayout = true
+    }
+
+    if (focus && (status === true || status === null) && doubleCheckItem()) {
+      this.root.select(this.textEditor)
+      fixLayout = true
+    }
+
+    if (fixLayout) {
+      this.fixLayout()
+    }
+  }
+
   openSpecialOrThroughSystem(item) {
     if (item.url.endsWith('.json')) {
       return this.handlePlaylistSource(item.url, true)
@@ -907,6 +952,7 @@ class AppElement extends FocusElement {
     const { editMode } = this
     const { canControlQueue, canProcessMetadata } = this.config
     const anyMarked = editMode && this.markGrouplike.items.length > 0
+    const hasNotesFile = !!getCorrespondingFileForItem(item, '.txt')
 
     let items;
     if (listing.grouplike.isTheQueue && isTrack(item)) {
@@ -963,6 +1009,8 @@ class AppElement extends FocusElement {
         canProcessMetadata && isTrack(item) && {label: 'Process metadata', action: () => this.processMetadata(item, true)},
         isOpenable(item) && item.url.endsWith('.json') && {label: 'Open (JSON Playlist)', action: () => this.openSpecialOrThroughSystem(item)},
         isOpenable(item) && {label: 'Open (System)', action: () => this.openThroughSystem(item)},
+        !hasNotesFile && isPlayable(item) && {label: 'Create notes file', action: () => this.createNotesFile(item)},
+        hasNotesFile && isPlayable(item) && {label: 'Edit notes file', action: () => this.editNotesFile(item, true)},
         canControlQueue && isPlayable(item) && {label: 'Remove from queue', action: () => this.unqueue(item)},
         {divider: true},