import { CustomHTMLElement } from './lib/custom_html_element.mjs'
import { Node } from './page_node.mjs'
import { MarkedPosition } from './marked_position.mjs'
export class N2PageHistory extends CustomHTMLElement {
static PAGESIZE = 15
static {// {{{
this.tmpl = document.createElement('template')
this.tmpl.innerHTML = `
Back to node
Actions
History
History on server:
History on client:
`
}// }}}
constructor() {// {{{
super()
this.selectedNode = null
this.setAttribute('tabindex', '-1')
this.addEventListener('keydown', event => this.keyHandler(event))
// Connect back icon and text to give the user a way back to the node.
this.elBackImage.addEventListener('click', () => _mbus.dispatch('SHOW_PAGE', { page: 'node' }))
this.elBackText.addEventListener('click', () => _mbus.dispatch('SHOW_PAGE', { page: 'node' }))
this.elPrev.addEventListener('click', () => this.prevPage())
this.elNext.addEventListener('click', () => this.nextPage())
this.elDownloadHistory.addEventListener('click', async () => {
await this.downloadHistory()
await this.useNode(this.node)
this.render(true)
})
_mbus.subscribe('NODE_UI_OPEN', async (event) => {
await this.useNode(event.detail.data)
this.render()
})
_mbus.subscribe('HISTORY_NODE_SELECTED', (event) => {
this.selectedNode = event.detail.data.historyNode
// Any selected history node is rendered with markdown.
const marked = new MarkedPosition()
this.elNodeMarkdown.innerHTML = marked.parse(this.selectedNode?.node.content())
})
}// }}}
async render(keepFetchHistoryProgress) {// {{{
this.elNodeName.innerText = this.node.get('Name')
this.elPage.innerText = `${this.page} / ${this.pages}`
this.elStatsOnClient.innerText = `${this.nodesTotal}`
this.elStatsOnServer.innerText = `${this.historyOnServerTotal}`
if (this.nodesTotal <= N2PageHistory.PAGESIZE)
this.elPagination.style.display = 'none'
else
this.elPagination.style.display = ''
let nodes = await nodeStore.nodesHistory.retrievePage(this.node.UUID, N2PageHistory.PAGESIZE, this.page)
let i = 0
let divs = nodes.map(n => {
i++
const index = 1 + this.nodesTotal - (N2PageHistory.PAGESIZE * (this.page - 1) + i)
const div = new N2PageHistoryNode(n, index)
div.render()
return div
})
this.elNodes.replaceChildren(...divs)
if (!keepFetchHistoryProgress)
this.elFetchHistoryProgress.innerText = ''
// Select the first node.
if (!this.selectedNode) {
this.elNodes.firstElementChild?.select()
}
}// }}}
async useNode(node) {// {{{
this.node = node
this.page = 1
this.nodesTotal = await nodeStore.nodesHistory.count(this.node.UUID)
this.historyOnServerTotal = await this.getServerTotal()
this.pages = Math.ceil(this.nodesTotal / N2PageHistory.PAGESIZE)
}// }}}
keyHandler(event) {// {{{
let handled = true
switch (event.key) {
case 'ArrowLeft':
this.prevPage()
break
case 'ArrowRight':
this.nextPage()
break
case 'ArrowUp':
const prevNode = this.selectedNode?.previousElementSibling
if (prevNode)
prevNode.select()
break
case 'ArrowDown':
const nextNode = this.selectedNode?.nextElementSibling
if (nextNode)
nextNode.select()
break
default:
handled = false
}
if (handled) {
event.stopPropagation()
event.preventDefault()
}
}// }}}
prevPage() {// {{{
if (this.page == 1)
return
// Selecting a node on another page is wrong.
this.selectedNode = null
this.page--
this.render()
}// }}}
nextPage() {// {{{
if (this.page >= this.pages)
return
// Selecting a node on another page is wrong.
this.selectedNode = null
this.page++
this.render()
}// }}}
async getServerTotal() {// {{{
const res = await fetch(`/node/history/count/${this.node.UUID}`, {
headers: {
"Authorization": 'Bearer ' + localStorage.getItem('token'),
}
})
const json = await res.json()
if (!json.OK) {
alert(json.Error)
return
}
return json.Count
}// }}}
async downloadHistory() {// {{{
try {
const nodes = []
let offset = 0
let hasMore = true
while (hasMore) {
const history = await this.downloadHistoryPage(offset)
hasMore = history.HasMore
for (const nodeData of history.Nodes) {
nodes.push(new Node(nodeData))
}
offset = nodes.length
this.elFetchHistoryProgress.innerText = `${nodes.length} fetched.`
}
let num = 0
for (const node of nodes) {
const ok = await nodeStore.nodesHistory.hasNode(node.UUID, node.get('Updated'))
if (ok) num++
await nodeStore.nodesHistory.add(node)
}
this.elFetchHistoryProgress.innerText = `${nodes.length} fetched - all history fetched.`
} catch (e) {
console.error(e)
alert(e)
}
}// }}}
async downloadHistoryPage(offset) {// {{{
const res = await fetch(`/node/history/retrieve/${this.node.UUID}/${offset}`, {
headers: {
"Authorization": 'Bearer ' + localStorage.getItem('token'),
}
})
const json = await res.json()
if (!json.OK) {
alert(json.Error)
return
}
return json
}// }}}
}
customElements.define('n2-pagehistory', N2PageHistory)
class N2PageHistoryNode extends CustomHTMLElement {
static {// {{{
this.tmpl = document.createElement('template')
this.tmpl.innerHTML = `
`
}// }}}
constructor(node, index) {// {{{
super()
this.node = node
this.index = index
this.style.display = 'contents'
this.selected = false
this.addEventListener('click', () => this.select())
// Another history node has been selected.
_mbus.subscribe('HISTORY_NODE_SELECTED', (event) => {
if (this.node.get('Updated') == event.detail.data.historyNode.node.get('Updated'))
return
this.selected = false
this.render()
})
}// }}}
select() {// {{{
this.selected = true
// Other nodes are told to unselect and rerender.
_mbus.dispatch('HISTORY_NODE_SELECTED', { historyNode: this })
this.render()
}// }}}
render() {// {{{
const date = this.node.get('Updated').slice(0, 10)
const time = this.node.get('Updated').slice(11, 19)
if (this.selected)
this.classList.add('selected')
else
this.classList.remove('selected')
this.elIndex.innerText = this.index
this.elDate.innerText = date
this.elTime.innerText = time
this.elSize.innerText = this.formatSize(this.node.get('Content').length)
this.elName.innerText = this.node.get('Name')
}// }}}
formatSize(s) {// {{{
let div = 1
let unit = 'B'
if (s >= 1048576) {
div = 1048576
unit = 'MB'
} else if (s >= 1024) {
div = 1024
unit = 'kB'
}
return new Intl.NumberFormat(undefined, {
maximumFractionDigits: 0
}).format(Math.round(s / div)) + ' ' + unit
}// }}}
}
customElements.define('n2-pagehistorynode', N2PageHistoryNode)