Skip to content

Commit

Permalink
Add import annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
johnfactotum committed Oct 11, 2023
1 parent f7c5071 commit c09b9b5
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 18 deletions.
1 change: 1 addition & 0 deletions po/POTFILES
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ src/ui/book-viewer.ui
src/ui/bookmark-row.ui
src/ui/export-dialog.ui
src/ui/image-viewer.ui
src/ui/import-dialog.ui
src/ui/library.ui
src/ui/library-view.ui
src/ui/navbar.ui
Expand Down
88 changes: 76 additions & 12 deletions src/annotations.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,19 +501,83 @@ export const AnnotationPopover = GObject.registerClass({
}
})

const ImportDialog = GObject.registerClass({
GTypeName: 'FoliateImportDialog',
Template: pkg.moduleuri('ui/import-dialog.ui'),
Children: ['annotation-view'],
InternalChildren: ['cancel-button', 'ok-button', 'banner'],
Properties: utils.makeParams({
'identifier-mismatch': 'boolean',
}),
Signals: {
'response': {},
},
}, class extends Adw.Window {
constructor(params) {
super(params)
const respond = () => {
this.emit('response')
this.close()
}
this._ok_button.connect('clicked', respond)
this._banner.connect('button-clicked', respond)
this._cancel_button.connect('clicked', () => this.close())
this.add_controller(utils.addShortcuts({ 'Escape|<ctrl>w': () => this.close() }))
}
close() {
super.close()
this.run_dispose()
}
})

export const importAnnotations = (window, data) => {
const dialog = new Gtk.FileDialog()
const filter = new Gtk.FileFilter({
name: _('JSON Files'),
mime_types: ['application/json'],
})
dialog.filters = new Gio.ListStore()
dialog.filters.append(new Gtk.FileFilter({
name: _('All Files'),
patterns: ['*'],
}))
dialog.filters.append(filter)
dialog.default_filter = filter
dialog.open(window, null, (__, res) => {
try {
const file = dialog.open_finish(res)
const json = utils.readJSONFile(file)
if (!json.annotations?.length) return window.error(_('No Annotations'),
_('The imported file has no annotations'))
const importDialog = new ImportDialog({
identifier_mismatch: !(json.metadata?.identifier === data.key),
transient_for: window,
})
const model = new Gio.ListStore()
importDialog.annotation_view.setupModel(model)
importDialog.show()
const annotations = json.annotations.map(item => {
const annotation = new Annotation(item)
model.append(annotation)
return annotation
})
importDialog.connect('response', () => data.addAnnotations(annotations))
} catch (e) {
if (e instanceof Gtk.DialogError) console.debug(e)
else {
console.error(e)
window.error(_('Cannot Import Annotations'),
_('An error occurred'))
}
}
})
}

export const exportAnnotations = (window, data) => {
const n = data.annotations?.length
if (!(n > 0)) {
const dialog = new Adw.MessageDialog({
heading: _('No Annotations'),
body: _('You don’t have any annotations for this book'),
modal: true,
transient_for: window,
})
dialog.add_response('ok', _('OK'))
dialog.present()
return
}
if (!(n > 0)) return window.error(_('No Annotations'),
_('You don’t have any annotations for this book'))

const path = pkg.modulepath('ui/export-dialog.ui')
const builder = pkg.useResource
? Gtk.Builder.new_from_resource(path)
Expand Down Expand Up @@ -599,4 +663,4 @@ ${text}
${note}
`)}
`,
}
}
12 changes: 12 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ const ApplicationWindow = GObject.registerClass({
add_toast(toast) {
this.content.add_toast(toast)
}
error(heading, body) {
const dialog = new Adw.MessageDialog({
heading, body,
modal: true,
transient_for: this,
})
dialog.add_response('close', _('Close'))
dialog.present()
}
openFile(file) {
this.file = file
if (!this.#bookViewer) {
Expand Down Expand Up @@ -274,6 +283,9 @@ export const Application = GObject.registerClass({
.card-sidebar, .card-sidebar row.activatable {
background-color: transparent;
}
.card-sidebar.flat-list .card {
padding: 12px;
}
.book-image-frame {
box-shadow: 0 6px 12px rgba(0, 0, 0, .15);
Expand Down
20 changes: 14 additions & 6 deletions src/book-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import './toc.js'
import './search.js'
import './navbar.js'
import {
AnnotationPopover, AnnotationModel, BookmarkModel, exportAnnotations,
AnnotationPopover, AnnotationModel, BookmarkModel,
importAnnotations, exportAnnotations,
} from './annotations.js'
import { SelectionPopover } from './selection-tools.js'
import { ImageViewer } from './image-viewer.js'
Expand Down Expand Up @@ -76,21 +77,25 @@ class BookData {
const annotations = init
? this.storage.get('annotations', [])
: this.annotations.export()
for (const annotation of annotations) this.addAnnotation(annotation, init)
await this.addAnnotations(annotations, false)
return this
}
async addAnnotation(annotation, init) {
async addAnnotation(annotation, save = true) {
try {
const [view, ...views] = this.views
const { index, label } = await view.addAnnotation(annotation)
this.annotations.add(annotation, index, label)
for (const view of views) view.addAnnotation(annotation)
if (!init) this.#saveAnnotations()
if (save) this.#saveAnnotations()
return annotation
} catch (e) {
console.error(e)
}
}
async addAnnotations(annotations, save = true) {
await Promise.all(annotations.map(x => this.addAnnotation(x, false)))
if (save) this.#saveAnnotations()
}
async deleteAnnotation(annotation) {
try {
const [view, ...views] = this.views
Expand Down Expand Up @@ -761,7 +766,7 @@ export const BookViewer = GObject.registerClass({
'toggle-sidebar', 'toggle-search', 'show-location',
'toggle-toc', 'toggle-annotations', 'toggle-bookmarks',
'preferences', 'show-info', 'bookmark',
'export-annotations',
'export-annotations', 'import-annotations',
],
props: ['fold-sidebar'],
})
Expand Down Expand Up @@ -1081,7 +1086,10 @@ export const BookViewer = GObject.registerClass({
this._bookmark_view.toggle()
}
exportAnnotations() {
exportAnnotations(this.get_root(), this.#data.storage.export())
exportAnnotations(this.root, this.#data.storage.export())
}
importAnnotations() {
importAnnotations(this.root, this.#data)
}
vfunc_unroot() {
this._navbar.tts_box.kill()
Expand Down
1 change: 1 addition & 0 deletions src/gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<file>ui/book-viewer.ui</file>
<file>ui/export-dialog.ui</file>
<file>ui/image-viewer.ui</file>
<file>ui/import-dialog.ui</file>
<file>ui/library.ui</file>
<file>ui/library-view.ui</file>
<file>ui/media-overlay-box.ui</file>
Expand Down
52 changes: 52 additions & 0 deletions src/ui/import-dialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="FoliateImportDialog" parent="AdwWindow">
<property name="modal">true</property>
<property name="default-width">400</property>
<property name="default-widget">ok-button</property>
<property name="title" translatable="yes">Import Annotations</property>
<property name="content">
<object class="AdwToolbarView">
<child type="top">
<object class="GtkHeaderBar">
<property name="show-title-buttons">False</property>
<child>
<object class="GtkButton" id="cancel-button">
<property name="label" translatable="yes">Cancel</property>
</object>
</child>
<child type="end">
<object class="GtkButton" id="ok-button">
<property name="sensitive" bind-source="FoliateImportDialog" bind-property="identifier-mismatch" bind-flags="invert-boolean"/>
<property name="label" translatable="yes">Import</property>
<style><class name="suggested-action"/></style>
</object>
</child>
</object>
</child>
<property name="content">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="AdwBanner" id="banner">
<property name="revealed" bind-source="FoliateImportDialog" bind-property="identifier-mismatch"/>
<property name="title" translatable="yes">The identifier doesn’t match. These annotations may not be for this book.</property>
<property name="button-label" translatable="yes">Import Anyway</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="propagate-natural-height">True</property>
<child>
<object class="FoliateAnnotationView" id="annotation-view">
<style><class name="card-sidebar"/><class name="flat-list"/></style>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</property>
</template>
</interface>

0 comments on commit c09b9b5

Please sign in to comment.