diff --git a/plugins/Sidebar/SidebarPlugin.py b/plugins/Sidebar/SidebarPlugin.py index 6e9e9ffd..90fdf8a9 100644 --- a/plugins/Sidebar/SidebarPlugin.py +++ b/plugins/Sidebar/SidebarPlugin.py @@ -428,8 +428,10 @@ class UiWebsocketPlugin(object): body.append(_(u"""
- {_[Sign]} - {_[Publish]} + + {_[Sign and publish]} + \u22EE +
""")) @@ -468,6 +470,10 @@ class UiWebsocketPlugin(object): body.append("") body.append("") + body.append("") + self.response(to, "".join(body)) def downloadGeoLiteDb(self, db_path): diff --git a/plugins/Sidebar/media/Menu.coffee b/plugins/Sidebar/media/Menu.coffee new file mode 100644 index 00000000..3785009b --- /dev/null +++ b/plugins/Sidebar/media/Menu.coffee @@ -0,0 +1,46 @@ +class Menu + constructor: (@button) -> + @elem = $(".menu.template").clone().removeClass("template") + @elem.appendTo("body") + @items = [] + + show: -> + if window.visible_menu and window.visible_menu.button[0] == @button[0] # Same menu visible then hide it + window.visible_menu.hide() + @hide() + else + button_pos = @button.offset() + @elem.css({"top": button_pos.top+@button.outerHeight(), "left": button_pos.left}) + @button.addClass("menu-active") + @elem.addClass("visible") + if window.visible_menu then window.visible_menu.hide() + window.visible_menu = @ + + + hide: -> + @elem.removeClass("visible") + @button.removeClass("menu-active") + window.visible_menu = null + + + addItem: (title, cb) -> + item = $(".menu-item.template", @elem).clone().removeClass("template") + item.html(title) + item.on "click", => + if not cb(item) + @hide() + return false + item.appendTo(@elem) + @items.push item + return item + + + log: (args...) -> + console.log "[Menu]", args... + +window.Menu = Menu + +# Hide menu on outside click +$("body").on "click", (e) -> + if window.visible_menu and e.target != window.visible_menu.button[0] and $(e.target).parent()[0] != window.visible_menu.elem[0] + window.visible_menu.hide() diff --git a/plugins/Sidebar/media/Menu.css b/plugins/Sidebar/media/Menu.css new file mode 100644 index 00000000..e2afa16e --- /dev/null +++ b/plugins/Sidebar/media/Menu.css @@ -0,0 +1,19 @@ +.menu { + background-color: white; padding: 10px 0px; position: absolute; top: 0px; left: 0px; max-height: 0px; overflow: hidden; transform: translate(0px, -30px); pointer-events: none; + box-shadow: 0px 2px 8px rgba(0,0,0,0.3); border-radius: 2px; opacity: 0; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; +} +.menu.visible { opacity: 1; max-height: 350px; transform: translate(0px, 0px); transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; pointer-events: all } + +.menu-item { display: block; text-decoration: none; color: black; padding: 6px 24px; transition: all 0.2s; border-bottom: none; font-weight: normal; padding-left: 30px; } +.menu-item-separator { margin-top: 5px; border-top: 1px solid #eee } + +.menu-item:hover { background-color: #F6F6F6; transition: none; color: inherit; border: none } +.menu-item:active, .menu-item:focus { background-color: #AF3BFF; color: white; transition: none } +.menu-item.selected:before { + content: "L"; display: inline-block; transform: rotateZ(45deg) scaleX(-1); + font-weight: bold; position: absolute; margin-left: -17px; font-size: 12px; margin-top: 2px; +} + +@media only screen and (max-width: 800px) { +.menu, .menu.visible { position: absolute; left: unset !important; right: 20px; } +} \ No newline at end of file diff --git a/plugins/Sidebar/media/Sidebar.coffee b/plugins/Sidebar/media/Sidebar.coffee index 9f891260..4f15c6bb 100644 --- a/plugins/Sidebar/media/Sidebar.coffee +++ b/plugins/Sidebar/media/Sidebar.coffee @@ -354,8 +354,13 @@ class Sidebar extends Class @updateHtmlTag() return false - # Sign content.json - @tag.find("#button-sign").off("click touchend").on "click touchend", => + # Sign and publish content.json + $(document).on "click touchend", => + @tag.find("#button-sign-publish-menu").removeClass("visible") + @tag.find(".contents + .flex").removeClass("sign-publish-flex") + + menu = new Menu(@tag.find("#wrapper-sign-publish")) + menu.addItem "Sign", => inner_path = @tag.find("#input-contents").val() wrapper.ws.cmd "fileRules", {inner_path: inner_path}, (res) => @@ -372,14 +377,47 @@ class Sidebar extends Class if res == "ok" wrapper.notifications.add "sign", "done", "#{inner_path} Signed!", 5000 + @tag.find(".contents + .flex").removeClass "active" + menu.hide() + + menu.addItem "Publish", => + inner_path = @tag.find("#input-contents").val() + wrapper.ws.cmd "sitePublish", {"inner_path": inner_path, "sign": false} + + @tag.find(".contents + .flex").removeClass "active" + menu.hide() + + @tag.find("#menu-sign-publish").off("click touchend").on "click touchend", => + if window.visible_menu == menu + @tag.find(".contents + .flex").removeClass "active" + menu.hide() + else + @tag.find(".contents + .flex").addClass "active" + @tag.find(".content-wrapper").prop "scrollTop", 10000 + menu.show() return false - # Publish content.json - @tag.find("#button-publish").off("click touchend").on "click touchend", => + $("body").on "click", => + @tag.find(".contents + .flex").removeClass "active" + + @tag.find("#button-sign-publish").off("click touchend").on "click touchend", => inner_path = @tag.find("#input-contents").val() - @tag.find("#button-publish").addClass "loading" - wrapper.ws.cmd "sitePublish", {"inner_path": inner_path, "sign": false}, => - @tag.find("#button-publish").removeClass "loading" + + wrapper.ws.cmd "fileRules", {inner_path: inner_path}, (res) => + if wrapper.site_info.privatekey or wrapper.site_info.auth_address in res.signers + # Privatekey stored in users.json + wrapper.ws.cmd "sitePublish", {privatekey: "stored", inner_path: inner_path, sign: true}, (res) => + if res == "ok" + wrapper.notifications.add "sign", "done", "#{inner_path} Signed and published!", 5000 + + else + # Ask the user for privatekey + wrapper.displayPrompt "Enter your private key:", "password", "Sign", "", (privatekey) => # Prompt the private key + wrapper.ws.cmd "sitePublish", {privatekey: privatekey, inner_path: inner_path, sign: true}, (res) => + if res == "ok" + wrapper.notifications.add "sign", "done", "#{inner_path} Signed and published!", 5000 + + return false # Close @tag.find(".close").off("click touchend").on "click touchend", (e) => diff --git a/plugins/Sidebar/media/Sidebar.css b/plugins/Sidebar/media/Sidebar.css index c76bde0c..6a2bfc6e 100644 --- a/plugins/Sidebar/media/Sidebar.css +++ b/plugins/Sidebar/media/Sidebar.css @@ -1,3 +1,7 @@ +.menu { + font-family: Roboto, 'Segoe UI', 'Helvetica Neue'; +} + .drag-bg { width: 100%; height: 100%; position: fixed; } .fixbutton.dragging { cursor: -webkit-grabbing; } .fixbutton-bg:active { cursor: -webkit-grabbing; } @@ -112,7 +116,7 @@ #checkbox-owned { margin-bottom: 25px; margin-top: 26px; margin-left: 11px; } .settings-owned { clear: both } #checkbox-owned ~ .settings-owned { opacity: 0; max-height: 0px; transition: all 0.3s linear; overflow: hidden } -#checkbox-owned:checked ~ .settings-owned { opacity: 1; max-height: 400px } +#checkbox-owned:checked ~ .settings-owned { opacity: 1; max-height: 420px } /* Globe */ .globe { width: 360px; height: 360px } @@ -124,6 +128,25 @@ .contents a { color: white } .contents a:active { background-color: #6B6B6B } +.contents + .flex.active { + padding-bottom: 100px; +} +#wrapper-sign-publish { + padding: 0; +} +#button-sign-publish, #menu-sign-publish { + display: inline-block; + margin: 5px 10px; + + text-decoration: none; +} +#button-sign-publish { + margin-right: 5px; +} +#menu-sign-publish { + margin-left: 5px; +} + /* Small screen */ @media screen and (max-width: 600px) { .sidebar .close { display: block } diff --git a/plugins/Sidebar/media/all.css b/plugins/Sidebar/media/all.css index baf9b94c..abb53b6e 100644 --- a/plugins/Sidebar/media/all.css +++ b/plugins/Sidebar/media/all.css @@ -1,5 +1,29 @@ +/* ---- plugins/Sidebar/media/Menu.css ---- */ + + +.menu { + background-color: white; padding: 10px 0px; position: absolute; top: 0px; left: 0px; max-height: 0px; overflow: hidden; -webkit-transform: translate(0px, -30px); -moz-transform: translate(0px, -30px); -o-transform: translate(0px, -30px); -ms-transform: translate(0px, -30px); transform: translate(0px, -30px) ; pointer-events: none; + -webkit-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -moz-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -o-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); -ms-box-shadow: 0px 2px 8px rgba(0,0,0,0.3); box-shadow: 0px 2px 8px rgba(0,0,0,0.3) ; -webkit-border-radius: 2px; -moz-border-radius: 2px; -o-border-radius: 2px; -ms-border-radius: 2px; border-radius: 2px ; opacity: 0; -webkit-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -moz-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -o-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; -ms-transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out; transition: opacity 0.2s ease-out, transform 1s ease-out, max-height 0.2s ease-in-out ; +} +.menu.visible { opacity: 1; max-height: 350px; -webkit-transform: translate(0px, 0px); -moz-transform: translate(0px, 0px); -o-transform: translate(0px, 0px); -ms-transform: translate(0px, 0px); transform: translate(0px, 0px) ; -webkit-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; -moz-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; -o-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; -ms-transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out; transition: opacity 0.1s ease-out, transform 0.3s ease-out, max-height 0.3s ease-in-out ; pointer-events: all } + +.menu-item { display: block; text-decoration: none; color: black; padding: 6px 24px; -webkit-transition: all 0.2s; -moz-transition: all 0.2s; -o-transition: all 0.2s; -ms-transition: all 0.2s; transition: all 0.2s ; border-bottom: none; font-weight: normal; padding-left: 30px; } +.menu-item-separator { margin-top: 5px; border-top: 1px solid #eee } + +.menu-item:hover { background-color: #F6F6F6; -webkit-transition: none; -moz-transition: none; -o-transition: none; -ms-transition: none; transition: none ; color: inherit; border: none } +.menu-item:active, .menu-item:focus { background-color: #AF3BFF; color: white; -webkit-transition: none ; -moz-transition: none ; -o-transition: none ; -ms-transition: none ; transition: none } +.menu-item.selected:before { + content: "L"; display: inline-block; -webkit-transform: rotateZ(45deg) scaleX(-1); -moz-transform: rotateZ(45deg) scaleX(-1); -o-transform: rotateZ(45deg) scaleX(-1); -ms-transform: rotateZ(45deg) scaleX(-1); transform: rotateZ(45deg) scaleX(-1) ; + font-weight: bold; position: absolute; margin-left: -17px; font-size: 12px; margin-top: 2px; +} + +@media only screen and (max-width: 800px) { +.menu, .menu.visible { position: absolute; left: unset !important; right: 20px; } +} + + /* ---- plugins/Sidebar/media/Scrollbable.css ---- */ @@ -52,6 +76,10 @@ /* ---- plugins/Sidebar/media/Sidebar.css ---- */ +.menu { + font-family: Roboto, 'Segoe UI', 'Helvetica Neue'; +} + .drag-bg { width: 100%; height: 100%; position: fixed; } .fixbutton.dragging { cursor: -webkit-grabbing; } .fixbutton-bg:active { cursor: -webkit-grabbing; } @@ -166,7 +194,7 @@ #checkbox-owned { margin-bottom: 25px; margin-top: 26px; margin-left: 11px; } .settings-owned { clear: both } #checkbox-owned ~ .settings-owned { opacity: 0; max-height: 0px; -webkit-transition: all 0.3s linear; -moz-transition: all 0.3s linear; -o-transition: all 0.3s linear; -ms-transition: all 0.3s linear; transition: all 0.3s linear ; overflow: hidden } -#checkbox-owned:checked ~ .settings-owned { opacity: 1; max-height: 400px } +#checkbox-owned:checked ~ .settings-owned { opacity: 1; max-height: 420px } /* Globe */ .globe { width: 360px; height: 360px } @@ -178,6 +206,25 @@ .contents a { color: white } .contents a:active { background-color: #6B6B6B } +.contents + .flex.active { + padding-bottom: 100px; +} +#wrapper-sign-publish { + padding: 0; +} +#button-sign-publish, #menu-sign-publish { + display: inline-block; + margin: 5px 10px; + + text-decoration: none; +} +#button-sign-publish { + margin-right: 5px; +} +#menu-sign-publish { + margin-left: 5px; +} + /* Small screen */ @media screen and (max-width: 600px) { .sidebar .close { display: block } diff --git a/plugins/Sidebar/media/all.js b/plugins/Sidebar/media/all.js index 0a54d46f..a77f8ceb 100644 --- a/plugins/Sidebar/media/all.js +++ b/plugins/Sidebar/media/all.js @@ -57,6 +57,85 @@ }).call(this); +/* ---- plugins/Sidebar/media/Menu.coffee ---- */ + + +(function() { + var Menu, + slice = [].slice; + + Menu = (function() { + function Menu(button) { + this.button = button; + this.elem = $(".menu.template").clone().removeClass("template"); + this.elem.appendTo("body"); + this.items = []; + } + + Menu.prototype.show = function() { + var button_pos; + if (window.visible_menu && window.visible_menu.button[0] === this.button[0]) { + window.visible_menu.hide(); + return this.hide(); + } else { + button_pos = this.button.offset(); + this.elem.css({ + "top": button_pos.top + this.button.outerHeight(), + "left": button_pos.left + }); + this.button.addClass("menu-active"); + this.elem.addClass("visible"); + if (window.visible_menu) { + window.visible_menu.hide(); + } + return window.visible_menu = this; + } + }; + + Menu.prototype.hide = function() { + this.elem.removeClass("visible"); + this.button.removeClass("menu-active"); + return window.visible_menu = null; + }; + + Menu.prototype.addItem = function(title, cb) { + var item; + item = $(".menu-item.template", this.elem).clone().removeClass("template"); + item.html(title); + item.on("click", (function(_this) { + return function() { + if (!cb(item)) { + _this.hide(); + } + return false; + }; + })(this)); + item.appendTo(this.elem); + this.items.push(item); + return item; + }; + + Menu.prototype.log = function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return console.log.apply(console, ["[Menu]"].concat(slice.call(args))); + }; + + return Menu; + + })(); + + window.Menu = Menu; + + $("body").on("click", function(e) { + if (window.visible_menu && e.target !== window.visible_menu.button[0] && $(e.target).parent()[0] !== window.visible_menu.elem[0]) { + return window.visible_menu.hide(); + } + }); + +}).call(this); + + /* ---- plugins/Sidebar/media/RateLimit.coffee ---- */ @@ -234,8 +313,7 @@ window.initScrollable = function () { @logStart("Preloading") wrapper.ws.cmd "sidebarGetHtmlTag", {}, (res) => @logEnd("Preloading") - @preload_html = res - */ + @preload_html = res */ this.fixbutton.on("mousedown touchstart", (function(_this) { return function(e) { if (e.button > 0) { @@ -475,6 +553,7 @@ window.initScrollable = function () { }; Sidebar.prototype.onOpened = function() { + var menu; this.log("Opened"); this.scrollable(); this.tag.find("#checkbox-owned").off("click touchend").on("click touchend", (function(_this) { @@ -607,7 +686,14 @@ window.initScrollable = function () { return false; }; })(this)); - this.tag.find("#button-sign").off("click touchend").on("click touchend", (function(_this) { + $(document).on("click touchend", (function(_this) { + return function() { + _this.tag.find("#button-sign-publish-menu").removeClass("visible"); + return _this.tag.find(".contents + .flex").removeClass("sign-publish-flex"); + }; + })(this)); + menu = new Menu(this.tag.find("#wrapper-sign-publish")); + menu.addItem("Sign", (function(_this) { return function() { var inner_path; inner_path = _this.tag.find("#input-contents").val(); @@ -639,20 +725,73 @@ window.initScrollable = function () { }); } }); - return false; + _this.tag.find(".contents + .flex").removeClass("active"); + return menu.hide(); }; })(this)); - this.tag.find("#button-publish").off("click touchend").on("click touchend", (function(_this) { + menu.addItem("Publish", (function(_this) { return function() { var inner_path; inner_path = _this.tag.find("#input-contents").val(); - _this.tag.find("#button-publish").addClass("loading"); - return wrapper.ws.cmd("sitePublish", { + wrapper.ws.cmd("sitePublish", { "inner_path": inner_path, "sign": false - }, function() { - return _this.tag.find("#button-publish").removeClass("loading"); }); + _this.tag.find(".contents + .flex").removeClass("active"); + return menu.hide(); + }; + })(this)); + this.tag.find("#menu-sign-publish").off("click touchend").on("click touchend", (function(_this) { + return function() { + if (window.visible_menu === menu) { + _this.tag.find(".contents + .flex").removeClass("active"); + menu.hide(); + } else { + _this.tag.find(".contents + .flex").addClass("active"); + _this.tag.find(".content-wrapper").prop("scrollTop", 10000); + menu.show(); + } + return false; + }; + })(this)); + $("body").on("click", (function(_this) { + return function() { + return _this.tag.find(".contents + .flex").removeClass("active"); + }; + })(this)); + this.tag.find("#button-sign-publish").off("click touchend").on("click touchend", (function(_this) { + return function() { + var inner_path; + inner_path = _this.tag.find("#input-contents").val(); + wrapper.ws.cmd("fileRules", { + inner_path: inner_path + }, function(res) { + var ref; + if (wrapper.site_info.privatekey || (ref = wrapper.site_info.auth_address, indexOf.call(res.signers, ref) >= 0)) { + return wrapper.ws.cmd("sitePublish", { + privatekey: "stored", + inner_path: inner_path, + sign: true + }, function(res) { + if (res === "ok") { + return wrapper.notifications.add("sign", "done", inner_path + " Signed and published!", 5000); + } + }); + } else { + return wrapper.displayPrompt("Enter your private key:", "password", "Sign", "", function(privatekey) { + return wrapper.ws.cmd("sitePublish", { + privatekey: privatekey, + inner_path: inner_path, + sign: true + }, function(res) { + if (res === "ok") { + return wrapper.notifications.add("sign", "done", inner_path + " Signed and published!", 5000); + } + }); + }); + } + }); + return false; }; })(this)); this.tag.find(".close").off("click touchend").on("click touchend", (function(_this) {