diff --git a/htdocs/css/admin.css b/htdocs/css/admin.css index ec59bd2..aeaf728 100644 --- a/htdocs/css/admin.css +++ b/htdocs/css/admin.css @@ -1,6 +1,10 @@ @import url("openwebrx-header.css"); @import url("openwebrx-globals.css"); +html, body { + height: unset; +} + .buttons { text-align: right; } @@ -59,8 +63,16 @@ table.bookmarks .frequency { text-align: right; } -table.bookmarks input, table.bookmarks select { +.bookmarks table input, .bookmarks table select { width: initial; text-align: inherit; display: initial; +} + +.actions { + margin: 1rem 0; +} + +.actions .btn { + width: 100%; } \ No newline at end of file diff --git a/htdocs/lib/settings/BookmarkTable.js b/htdocs/lib/settings/BookmarkTable.js index b530513..04372da 100644 --- a/htdocs/lib/settings/BookmarkTable.js +++ b/htdocs/lib/settings/BookmarkTable.js @@ -1,6 +1,6 @@ $.fn.bookmarktable = function() { $.each(this, function(){ - var $table = $(this); + var $table = $(this).find('table'); var inputs = $table.find('tr.inputs td').map(function(){ var candidates = $(this).find('input, select') @@ -8,6 +8,16 @@ $.fn.bookmarktable = function() { }).toArray(); $table.find('tr.inputs').remove(); + var transformToHtml = function($cell) { + var $input = $cell.find('input, select'); + var $option = $input.find('option:selected') + if ($option.length) { + $cell.html($option.html()); + } else { + $cell.html($input.val()); + } + }; + $table.on('dblclick', 'td', function(e) { var $cell = $(e.target); var html = $cell.html(); @@ -18,6 +28,7 @@ $.fn.bookmarktable = function() { var $input = inputs[index]; if (!$input) return; + $table.find('tr[data-id="new"]').remove(); $input.val($cell.data('value') || html); $input.prop('disabled', false); $cell.html($input); @@ -30,12 +41,7 @@ $.fn.bookmarktable = function() { contentType: 'application/json', method: 'POST' }).then(function(){ - var $option = $input.find('option:selected') - if ($option.length) { - $cell.html($option.html()); - } else { - $cell.html($input.val()); - } + transformToHtml($cell); }); }; @@ -46,5 +52,64 @@ $.fn.bookmarktable = function() { } }); }); + + $(this).find('.bookmark-add').on('click', function() { + if ($table.find('tr[data-id="new"]').length) return; + + var row = $(''); + row.append(inputs.map(function(i){ + var cell = $(''); + if (i) { + i.prop('disabled', false); + i.val(''); + cell.html(i); + } else { + cell.html( + '
' + + '' + + '' + + '
' + ); + } + return cell; + })); + + row.on('click', '.bookmark-cancel', function() { + row.remove(); + }); + + row.on('click', '.bookmark-save', function() { + var data = Object.fromEntries( + row.find('input, select').toArray().map(function(input){ + var $input = $(input); + $input.prop('disabled', true); + return [$input.prop('name'), $input.val()] + }) + ); + + $.ajax(document.location.href, { + data: JSON.stringify(data), + contentType: 'application/json', + method: 'POST' + }).then(function(data){ + if ('bookmark_id' in data) { + row.attr('data-id', data['bookmark_id']); + row.find('td').each(function(){ + var $cell = $(this); + var $group = $cell.find('.btn-group') + if ($group.length) { + $group.remove; + $cell.html('
delete
'); + } + transformToHtml($cell); + }); + } + }); + + }); + + $table.append(row); + row[0].scrollIntoView(); + }); }); }; diff --git a/htdocs/settings.js b/htdocs/settings.js index aa2db73..bddbf16 100644 --- a/htdocs/settings.js +++ b/htdocs/settings.js @@ -23,5 +23,5 @@ $(function(){ $(".sdrdevice").sdrdevice(); $(".imageupload").imageUpload(); - $("table.bookmarks").bookmarktable(); + $(".bookmarks").bookmarktable(); }); \ No newline at end of file diff --git a/htdocs/settings/bookmarks.html b/htdocs/settings/bookmarks.html index e666986..343deb6 100644 --- a/htdocs/settings/bookmarks.html +++ b/htdocs/settings/bookmarks.html @@ -13,7 +13,18 @@ ${header}

Bookmarks

+
+ Double-click the values in the table to edit them. +
+
+
+
+ +
+ ${bookmarks} +
+ +
- ${bookmarks}
\ No newline at end of file diff --git a/owrx/bookmarks.py b/owrx/bookmarks.py index 211c13e..31ec647 100644 --- a/owrx/bookmarks.py +++ b/owrx/bookmarks.py @@ -96,3 +96,6 @@ class Bookmarks(object): with open(Bookmarks._getBookmarksFile(), "w") as file: json.dump([b.__dict__() for b in self.bookmarks], file, indent=4) self.file_modified = self._getFileModifiedTimestamp() + + def addBookmark(self, bookmark: Bookmark): + self.bookmarks.append(bookmark) diff --git a/owrx/controllers/bookmarks.py b/owrx/controllers/bookmarks.py index f4c55ba..8171ad7 100644 --- a/owrx/controllers/bookmarks.py +++ b/owrx/controllers/bookmarks.py @@ -32,7 +32,7 @@ class BookmarksController(AuthorizationMixin, WebpageController): ) return """ - +
@@ -97,5 +97,19 @@ class BookmarksController(AuthorizationMixin, WebpageController): except json.JSONDecodeError: self.send_response("{}", content_type="application/json", code=400) + def new(self): + bookmarks = Bookmarks.getSharedInstance() + try: + data = json.loads(self.get_body()) + # sanitize + data = {k: data[k] for k in ["name", "frequency", "modulation"]} + bookmark = Bookmark(data) + + bookmarks.addBookmark(bookmark) + bookmarks.store() + self.send_response(json.dumps({"bookmark_id": id(bookmark)}), content_type="application/json", code=200) + except json.JSONDecodeError: + self.send_response("{}", content_type="application/json", code=400) + def indexAction(self): self.serve_template("settings/bookmarks.html", **self.template_variables()) diff --git a/owrx/http.py b/owrx/http.py index 96f54ee..008168b 100644 --- a/owrx/http.py +++ b/owrx/http.py @@ -110,6 +110,7 @@ class Router(object): ), StaticRoute("/settings/sdr", SdrSettingsController), StaticRoute("/settings/bookmarks", BookmarksController), + StaticRoute("/settings/bookmarks", BookmarksController, method="POST", options={"action": "new"}), RegexRoute("/settings/bookmarks/(.+)", BookmarksController, method="POST", options={"action": "update"}), StaticRoute("/login", SessionController, options={"action": "loginAction"}), StaticRoute("/login", SessionController, method="POST", options={"action": "processLoginAction"}),
Name Frequency