feat: add toolbar + notifications
This commit is contained in:
@ -23,6 +23,13 @@ export default class Editor {
|
||||
|
||||
this.integrity_mgr = new IntegrityManager(this)
|
||||
|
||||
document.getElementById("check-integrity").addEventListener("click", () => this.checkIntegrity())
|
||||
document.getElementById("improve-all").addEventListener("click", () => this.improveAllNames())
|
||||
document.getElementById("save").addEventListener("click", () => this.save())
|
||||
document.getElementById("reload").addEventListener("click", () => window.location.reload())
|
||||
document.getElementById("toggle-notifs").addEventListener("click", () => this.toggleNotifications())
|
||||
document.getElementById("close-notifs").addEventListener("click", () => this.closeNotifications())
|
||||
|
||||
this.setup()
|
||||
}
|
||||
|
||||
@ -49,7 +56,6 @@ export default class Editor {
|
||||
document.getElementById("title").value = this.data.title
|
||||
this.tables.audio.loadTracks(this.data.audio_tracks)
|
||||
this.tables.subtitle.loadTracks(this.data.subtitle_tracks)
|
||||
this.integrity_mgr.checkIntegrity()
|
||||
}
|
||||
|
||||
save() {
|
||||
@ -63,8 +69,9 @@ export default class Editor {
|
||||
if (res.ok) {
|
||||
this.dirty = false
|
||||
document.getElementById("unsaved").classList.remove("show")
|
||||
this.notify("Saved successfully !", "success")
|
||||
} else {
|
||||
alert(`Error ${res.status}: ${res.statusText}`)
|
||||
this.notify(`Error ${res.status}: ${res.statusText}`, "error", 10000)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -78,4 +85,47 @@ export default class Editor {
|
||||
updateObjectFromJoinedKey(this.data[listKey][trackIdx], key, value)
|
||||
this.setDirty()
|
||||
}
|
||||
|
||||
notify(text, type, duration=5000) {
|
||||
const list = document.getElementById("notifs")
|
||||
const hist = document.getElementById("notifs-hist").querySelector(".list")
|
||||
const notif = document.createElement("div")
|
||||
notif.classList.add("notif")
|
||||
notif.dataset.type = type
|
||||
notif.innerText = text
|
||||
list.appendChild(notif)
|
||||
setTimeout(() => notif.remove(), duration)
|
||||
notif.addEventListener("click", () => notif.remove())
|
||||
hist.prepend(notif.cloneNode(true))
|
||||
}
|
||||
|
||||
checkIntegrity() {
|
||||
if (this.integrity_mgr.checkIntegrity()) {
|
||||
this.notify("No integrity error detected !", "success")
|
||||
}
|
||||
}
|
||||
|
||||
improveAllNames() {
|
||||
this.integrity_mgr.improveAllNames()
|
||||
this.notify("Improved all names !", "success")
|
||||
}
|
||||
|
||||
toggleNotifications() {
|
||||
const hist = document.getElementById("notifs-hist")
|
||||
if (hist.classList.contains("show")) {
|
||||
this.closeNotifications()
|
||||
} else {
|
||||
this.openNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
openNotifications() {
|
||||
const hist = document.getElementById("notifs-hist")
|
||||
hist.classList.add("show")
|
||||
}
|
||||
|
||||
closeNotifications() {
|
||||
const hist = document.getElementById("notifs-hist")
|
||||
hist.classList.remove("show")
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ class MismatchCorrection {
|
||||
|
||||
export default class IntegrityManager {
|
||||
IGNORE_KEYS = [
|
||||
"type", "channels"
|
||||
"type", "channels_details"
|
||||
]
|
||||
|
||||
/**
|
||||
@ -136,11 +136,17 @@ export default class IntegrityManager {
|
||||
}
|
||||
|
||||
checkIntegrity() {
|
||||
this.ignoreList = []
|
||||
this.mismatches = []
|
||||
for (const table of Object.values(this.editor.tables)) {
|
||||
this.checkTableIntegrity(table)
|
||||
}
|
||||
|
||||
if (this.mismatches.length === 0) {
|
||||
return true
|
||||
}
|
||||
this.nextError()
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,7 +204,7 @@ export default class IntegrityManager {
|
||||
|
||||
const channels = lower.match(/\d+\.\d+/)
|
||||
if (channels) {
|
||||
fields.channels = channels[0]
|
||||
fields.channels_details = channels[0]
|
||||
}
|
||||
|
||||
break
|
||||
@ -297,8 +303,8 @@ export default class IntegrityManager {
|
||||
if (fields.flags.visual_impaired) {
|
||||
name += " AD"
|
||||
}
|
||||
if (fields.channels) {
|
||||
name += " / " + fields.channels
|
||||
if (fields.channels_details) {
|
||||
name += " / " + fields.channels_details
|
||||
}
|
||||
break
|
||||
case "subtitle":
|
||||
@ -345,6 +351,14 @@ export default class IntegrityManager {
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
improveAllNames() {
|
||||
for (const table of Object.values(this.editor.tables)) {
|
||||
for (const track of table.tracks) {
|
||||
this.improveName(track)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { flattenObj, getLanguageOptions } from "./utils.mjs"
|
||||
import { findLanguage, flattenObj, getLanguageOptions } from "./utils.mjs"
|
||||
|
||||
export class Track {
|
||||
constructor(table, idx, fields) {
|
||||
@ -54,29 +54,36 @@ export class Track {
|
||||
|
||||
case "bool":
|
||||
input.type = "checkbox"
|
||||
input.checked = value
|
||||
getValue = () => input.checked
|
||||
const hotone = this.table.CONSTRAINTS[field.key]?.type == "hotone"
|
||||
|
||||
if (listeners && hotone) {
|
||||
getValue = () => input.checked
|
||||
const onehot = this.table.CONSTRAINTS[field.key]?.type == "onehot"
|
||||
|
||||
if (listeners && onehot) {
|
||||
if (value) {
|
||||
if (field.key in this.table.hotones) {
|
||||
alert(`Error in metadata file: field ${field.name} is hotone but multiple tracks are enabled`)
|
||||
if (field.key in this.table.onehots) {
|
||||
this.table.editor.notify(
|
||||
`Error in metadata file: field '${field.name}' is onehot but multiple tracks are enabled. Only the first one will be enabled`,
|
||||
"error",
|
||||
20000
|
||||
)
|
||||
value = false
|
||||
} else {
|
||||
this.table.onehots[field.key] = input
|
||||
}
|
||||
this.table.hotones[field.key] = input
|
||||
}
|
||||
input.addEventListener("click", e => {
|
||||
if (!input.checked) {
|
||||
e.preventDefault()
|
||||
} else {
|
||||
if (field.key in this.table.hotones) {
|
||||
this.table.hotones[field.key].checked = false
|
||||
this.table.hotones[field.key].dispatchEvent(new Event("change"))
|
||||
if (field.key in this.table.onehots) {
|
||||
this.table.onehots[field.key].checked = false
|
||||
this.table.onehots[field.key].dispatchEvent(new Event("change"))
|
||||
}
|
||||
this.table.hotones[field.key] = input
|
||||
this.table.onehots[field.key] = input
|
||||
}
|
||||
})
|
||||
}
|
||||
input.checked = value
|
||||
break
|
||||
|
||||
case "sel":
|
||||
@ -91,11 +98,31 @@ export class Track {
|
||||
opt.value = option.value
|
||||
input.appendChild(opt)
|
||||
})
|
||||
|
||||
if (field.key === "language") {
|
||||
const lang = findLanguage(value)
|
||||
if (lang === null) {
|
||||
this.table.editor.notify(
|
||||
`Unknown language '${value}' for ${this.table.type} track ${this.idx}`,
|
||||
"error",
|
||||
20000
|
||||
)
|
||||
} else if (lang !== value) {
|
||||
this.table.editor.notify(
|
||||
`Language of ${this.table.type} track ${this.idx} was corrected (${value} -> ${lang})`,
|
||||
"warning"
|
||||
)
|
||||
value = lang
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
input.value = value
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
input.name = field.key + "[]"
|
||||
input.dataset.key = field.key
|
||||
if (this.table.CONSTRAINTS[field.key]?.type === "readonly") {
|
||||
input.disabled = true
|
||||
@ -135,7 +162,7 @@ export default class TracksTable {
|
||||
}
|
||||
CONSTRAINTS = {
|
||||
"flags/default": {
|
||||
type: "hotone"
|
||||
type: "onehot"
|
||||
},
|
||||
"index": {
|
||||
type: "readonly"
|
||||
@ -160,7 +187,7 @@ export default class TracksTable {
|
||||
this.fields = []
|
||||
this.tracks = []
|
||||
this.dataKey = dataKey
|
||||
this.hotones = {}
|
||||
this.onehots = {}
|
||||
}
|
||||
|
||||
getFieldProps(key) {
|
||||
|
@ -75,10 +75,10 @@ export function getLanguageAliases(langTag) {
|
||||
export function findLanguage(value) {
|
||||
for (const lang in LANGUAGES) {
|
||||
const aliases = getLanguageAliases(lang)
|
||||
const matches = aliases.map(a => {
|
||||
const matches = aliases.some(a => {
|
||||
return new RegExp("\\b" + a + "\\b").test(value)
|
||||
})
|
||||
if (matches.some(v => v)) {
|
||||
if (matches) {
|
||||
return lang
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user