Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--css/style.scss75
-rw-r--r--js/views/callinfoview.js31
-rw-r--r--js/views/templates.js45
-rw-r--r--js/views/templates/callinfoview.handlebars29
-rw-r--r--tests/acceptance/features/bootstrap/ConversationInfoContext.php53
5 files changed, 135 insertions, 98 deletions
diff --git a/css/style.scss b/css/style.scss
index 493f12761..7533e8a98 100644
--- a/css/style.scss
+++ b/css/style.scss
@@ -999,30 +999,59 @@ body:not(#body-public) .participantWithList > li > span:not(.currentUser):not(.g
.share-link-options {
display: flex;
align-items: center;
- .password-button {
- position: relative;
+ }
+
+ .room-moderation-button .menu {
+ /* Values copied from apps.scss in server. */
+ $popoveritem-height: 44px;
+ $popovericon-size: 16px;
+ $outter-margin: ($popoveritem-height - $popovericon-size) / 2;
+
+ /* The server sets a top/bottom margin to forms and inputs of the
+ * first/last item in a menu, but that misaligns the left icon; this
+ * sets the top/bottom margin to the whole item instead of only to the
+ * forms or inputs. */
+ li:first-of-type {
+ > .menuitem.password-option {
+
+ margin-top: $outter-margin - 2px; // minus the input margin
+
+ > form, > input {
+ margin-top: 0;
+ }
+ }
+ }
+ li:last-of-type {
+ > .menuitem.password-option {
+
+ margin-bottom: $outter-margin - 2px; // minus the input margin
+
+ > form, > input {
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ .menuitem.password-option {
+ /* Override rule for menu items from server, as in this case
+ * only the button in the password field is clickable, so the
+ * pointer cursor should not be used for the whole item. */
+ cursor: default;
+
+ .password-form {
+ position: relative;
+
+ .password-confirm,
+ .password-loading {
+ /* Inputs in menu items do not have a right margin, so
+ * it does not need to be compensated. */
+ right: 0;
+ }
- .menuitem {
- /* Override rule for menu items from server, as in this case
- * only the button in the password field is clickable, so the
- * pointer cursor should not be used for the whole item. */
- cursor: default;
-
- .password-form {
- position: relative;
-
- .password-confirm,
- .password-loading {
- /* Inputs in menu items do not have a right margin, so
- * it does not need to be compensated. */
- right: 0;
- }
-
- .password-confirm {
- /* Needed to override an important rule set in the
- * server. */
- background-color: transparent !important;
- }
+ .password-confirm {
+ /* Needed to override an important rule set in the
+ * server. */
+ background-color: transparent !important;
}
}
}
diff --git a/js/views/callinfoview.js b/js/views/callinfoview.js
index 0e1cd3fcf..6fd06b728 100644
--- a/js/views/callinfoview.js
+++ b/js/views/callinfoview.js
@@ -46,17 +46,18 @@
templateContext: function() {
var canModerate = this._canModerate();
var canFullModerate = this._canFullModerate();
+ var isPublic = this.model.get('type') === OCA.SpreedMe.app.ROOM_TYPE_PUBLIC;
return $.extend(this.model.toJSON(), {
isRoomForFile: this.model.get('objectType') === 'file',
fileLink: OC.generateUrl('/f/{fileId}', { fileId: this.model.get('objectId') }),
fileLinkTitle: t('spreed', 'Go to file'),
- showRoomModerationMenu: canModerate && canFullModerate,
+ showRoomModerationMenu: canModerate && (canFullModerate || isPublic),
canModerate: canModerate,
canFullModerate: canFullModerate,
linkCheckboxLabel: t('spreed', 'Share link'),
- isPublic: this.model.get('type') === 3,
+ isPublic: isPublic,
passwordInputPlaceholder: this.model.get('hasPassword')? t('spreed', 'Change password'): t('spreed', 'Set password'),
- showShareLink: !canModerate && this.model.get('type') === 3,
+ showShareLink: !canModerate && isPublic,
isDeletable: canModerate && (Object.keys(this.model.get('participants')).length > 2 || this.model.get('numGuests') > 0)
});
},
@@ -70,7 +71,6 @@
'callButton': 'div.call-button',
- 'passwordButton': '.password-button .button',
'passwordForm': '.password-form',
'passwordInput': '.password-input',
'passwordConfirm': '.password-confirm',
@@ -88,7 +88,6 @@
events: {
'change @ui.linkCheckbox': 'toggleLinkCheckbox',
- 'keyup @ui.passwordInput': 'keyUpPassword',
'click @ui.passwordConfirm': 'confirmPassword',
'submit @ui.passwordForm': 'confirmPassword',
},
@@ -181,21 +180,10 @@
});
this.initClipboard();
- this.ui.passwordButton.tooltip({
- placement: 'bottom',
- trigger: 'hover',
- title: (this.model.get('hasPassword')) ? t('spreed', 'Change password') : t('spreed', 'Set password')
- });
-
// Set the body as the container to show the tooltip in front of the
// header.
this.ui.fileLink.tooltip({container: $('body')});
- var self = this;
- OC.registerMenu($(this.ui.passwordButton), $(this.ui.passwordMenu), function() {
- $(self.ui.passwordInput).focus();
- });
-
OC.registerMenu(this.ui.roomModerationButton, this.ui.roomModerationMenu);
},
@@ -287,7 +275,7 @@
this.ui.passwordInput.val('');
restoreState();
OC.hideMenus();
- this.ui.passwordButton.focus();
+ this.ui.roomModerationButton.focus();
}.bind(this),
error: function() {
restoreState();
@@ -297,15 +285,6 @@
});
},
- keyUpPassword: function(e) {
- e.preventDefault();
- if (e.keyCode === 27) {
- // ESC
- OC.hideMenus();
- this.ui.passwordButton.focus();
- }
- },
-
/**
* Clipboard
*/
diff --git a/js/views/templates.js b/js/views/templates.js
index 426f116ff..17e87bee2 100644
--- a/js/views/templates.js
+++ b/js/views/templates.js
@@ -54,37 +54,38 @@ templates['callinfoview'] = template({"1":function(container,depth0,helpers,part
+ ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.isPublic : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </div>\n";
},"4":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {});
-
- return " <div class=\"clipboard-button\"><button class=\"button icon-clippy\"></button></div>\n <div class=\"password-button\">\n <div class=\"menutoggle\"><button class=\"button "
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasPassword : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.program(7, data, 0),"data":data})) != null ? stack1 : "")
- + "\"></button></div>\n <div class=\"popovermenu password-menu menu-right\">\n <ul>\n <li>\n <span class=\"menuitem "
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasPassword : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.program(7, data, 0),"data":data})) != null ? stack1 : "")
- + " password-option\">\n <form class=\"password-form\">\n <input class=\"password-input\" maxlength=\"200\" type=\"password\"\n placeholder=\""
- + container.escapeExpression(((helper = (helper = helpers.passwordInputPlaceholder || (depth0 != null ? depth0.passwordInputPlaceholder : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"passwordInputPlaceholder","hash":{},"data":data}) : helper)))
- + "\">\n <input type=\"submit\" value=\"\" autocomplete=\"new-password\" class=\"icon icon-confirm password-confirm\"></input>\n <span class=\"icon icon-loading-small password-loading hidden\"/>\n </form>\n </span>\n </li>\n </ul>\n </div>\n </div>\n";
-},"5":function(container,depth0,helpers,partials,data) {
- return "icon-password";
-},"7":function(container,depth0,helpers,partials,data) {
- return "icon-no-password";
-},"9":function(container,depth0,helpers,partials,data) {
+ return " <div class=\"clipboard-button\"><button class=\"button icon-clippy\"></button></div>\n";
+},"6":function(container,depth0,helpers,partials,data) {
return " <div class=\"share-link-options\">\n <div class=\"clipboard-button\"><button class=\"button icon-clippy\"></button></div>\n </div>\n";
-},"11":function(container,depth0,helpers,partials,data) {
- var stack1;
+},"8":function(container,depth0,helpers,partials,data) {
+ var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
return " <div class=\"room-moderation-button\">\n <div class=\"menutoggle\">\n <button class=\"button icon-more\"></button>\n </div>\n <div class=\"popovermenu bubble menu\">\n <ul>\n"
- + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.canFullModerate : depth0),{"name":"if","hash":{},"fn":container.program(12, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.canFullModerate : depth0),{"name":"if","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isPublic : depth0),{"name":"if","hash":{},"fn":container.program(12, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </ul>\n </div>\n </div>\n";
-},"12":function(container,depth0,helpers,partials,data) {
+},"9":function(container,depth0,helpers,partials,data) {
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {});
return " <li>\n <span class=\"menuitem\">\n <input name=\"link-checkbox\" id=\"link-checkbox\" class=\"checkbox link-checkbox\" value=\"1\" "
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isPublic : depth0),{"name":"if","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isPublic : depth0),{"name":"if","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " type=\"checkbox\">\n <label for=\"link-checkbox\" class=\"link-checkbox-label\">"
+ container.escapeExpression(((helper = (helper = helpers.linkCheckboxLabel || (depth0 != null ? depth0.linkCheckboxLabel : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"linkCheckboxLabel","hash":{},"data":data}) : helper)))
+ "</label>\n </span>\n </li>\n";
-},"13":function(container,depth0,helpers,partials,data) {
+},"10":function(container,depth0,helpers,partials,data) {
return " checked=\"checked\"";
+},"12":function(container,depth0,helpers,partials,data) {
+ var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {});
+
+ return " <li>\n <span class=\"menuitem "
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.hasPassword : depth0),{"name":"if","hash":{},"fn":container.program(13, data, 0),"inverse":container.program(15, data, 0),"data":data})) != null ? stack1 : "")
+ + " password-option\">\n <form class=\"password-form\">\n <input class=\"password-input\" maxlength=\"200\" type=\"password\"\n placeholder=\""
+ + container.escapeExpression(((helper = (helper = helpers.passwordInputPlaceholder || (depth0 != null ? depth0.passwordInputPlaceholder : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"passwordInputPlaceholder","hash":{},"data":data}) : helper)))
+ + "\">\n <input type=\"submit\" value=\"\" autocomplete=\"new-password\" class=\"icon icon-confirm password-confirm\"></input>\n <span class=\"icon icon-loading-small password-loading hidden\"/>\n </form>\n </span>\n </li>\n";
+},"13":function(container,depth0,helpers,partials,data) {
+ return "icon-password";
+},"15":function(container,depth0,helpers,partials,data) {
+ return "icon-no-password";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {});
@@ -92,8 +93,8 @@ templates['callinfoview'] = template({"1":function(container,depth0,helpers,part
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isRoomForFile : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n<div class=\"call-controls-container\">\n <div class=\"call-button\"></div>\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.canModerate : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showShareLink : depth0),{"name":"if","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
- + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showRoomModerationMenu : depth0),{"name":"if","hash":{},"fn":container.program(11, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showShareLink : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.showRoomModerationMenu : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n";
},"useData":true});
templates['chatview'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
diff --git a/js/views/templates/callinfoview.handlebars b/js/views/templates/callinfoview.handlebars
index 6d9551207..968bfe829 100644
--- a/js/views/templates/callinfoview.handlebars
+++ b/js/views/templates/callinfoview.handlebars
@@ -12,23 +12,6 @@
<div class="share-link-options">
{{#if isPublic}}
<div class="clipboard-button"><button class="button icon-clippy"></button></div>
- <div class="password-button">
- <div class="menutoggle"><button class="button {{#if hasPassword}}icon-password{{else}}icon-no-password{{/if}}"></button></div>
- <div class="popovermenu password-menu menu-right">
- <ul>
- <li>
- <span class="menuitem {{#if hasPassword}}icon-password{{else}}icon-no-password{{/if}} password-option">
- <form class="password-form">
- <input class="password-input" maxlength="200" type="password"
- placeholder="{{passwordInputPlaceholder}}">
- <input type="submit" value="" autocomplete="new-password" class="icon icon-confirm password-confirm"></input>
- <span class="icon icon-loading-small password-loading hidden"/>
- </form>
- </span>
- </li>
- </ul>
- </div>
- </div>
{{/if}}
</div>
{{/if}}
@@ -52,6 +35,18 @@
</span>
</li>
{{/if}}
+ {{#if isPublic}}
+ <li>
+ <span class="menuitem {{#if hasPassword}}icon-password{{else}}icon-no-password{{/if}} password-option">
+ <form class="password-form">
+ <input class="password-input" maxlength="200" type="password"
+ placeholder="{{passwordInputPlaceholder}}">
+ <input type="submit" value="" autocomplete="new-password" class="icon icon-confirm password-confirm"></input>
+ <span class="icon icon-loading-small password-loading hidden"/>
+ </form>
+ </span>
+ </li>
+ {{/if}}
</ul>
</div>
</div>
diff --git a/tests/acceptance/features/bootstrap/ConversationInfoContext.php b/tests/acceptance/features/bootstrap/ConversationInfoContext.php
index 29a7e4d34..3186bc7de 100644
--- a/tests/acceptance/features/bootstrap/ConversationInfoContext.php
+++ b/tests/acceptance/features/bootstrap/ConversationInfoContext.php
@@ -84,10 +84,19 @@ class ConversationInfoContext implements Context, ActorAwareInterface {
/**
* @return Locator
*/
- public static function passwordButton() {
- return Locator::forThe()->css(".password-button")->
+ public static function roomModerationButton() {
+ return Locator::forThe()->css(".room-moderation-button")->
descendantOf(self::conversationInfoContainer())->
- describedAs("Password button in conversation info");
+ describedAs("Room moderation button in conversation info");
+ }
+
+ /**
+ * @return Locator
+ */
+ public static function roomModerationMenu() {
+ return Locator::forThe()->css(".menu")->
+ descendantOf(self::roomModerationButton())->
+ describedAs("Room moderation menu in conversation info");
}
/**
@@ -95,8 +104,8 @@ class ConversationInfoContext implements Context, ActorAwareInterface {
*/
public static function passwordIcon() {
return Locator::forThe()->css(".icon-password")->
- descendantOf(self::passwordButton())->
- describedAs("Password icon in conversation info");
+ descendantOf(self::roomModerationMenu())->
+ describedAs("Password icon in room moderation menu in conversation info");
}
/**
@@ -104,8 +113,8 @@ class ConversationInfoContext implements Context, ActorAwareInterface {
*/
public static function noPasswordIcon() {
return Locator::forThe()->css(".icon-no-password")->
- descendantOf(self::passwordButton())->
- describedAs("No password icon in conversation info");
+ descendantOf(self::roomModerationMenu())->
+ describedAs("No password icon in room moderation menu in conversation info");
}
/**
@@ -113,8 +122,8 @@ class ConversationInfoContext implements Context, ActorAwareInterface {
*/
public static function passwordField() {
return Locator::forThe()->css(".password-input")->
- descendantOf(self::conversationInfoContainer())->
- describedAs("Password field in conversation info");
+ descendantOf(self::roomModerationMenu())->
+ describedAs("Password field in room moderation menu in conversation info");
}
/**
@@ -142,7 +151,7 @@ class ConversationInfoContext implements Context, ActorAwareInterface {
* @When I protect the conversation with the password :password
*/
public function iProtectTheConversationWithThePassword($password) {
- $this->actor->find(self::passwordButton(), 2)->click();
+ $this->showRoomModerationMenu();
$this->actor->find(self::passwordField(), 2)->setValue($password . "\r");
}
@@ -151,14 +160,38 @@ class ConversationInfoContext implements Context, ActorAwareInterface {
* @Then I see that the conversation is password protected
*/
public function iSeeThatTheConversationIsPasswordProtected() {
+ $this->showRoomModerationMenu();
+
PHPUnit_Framework_Assert::assertTrue($this->actor->find(self::passwordIcon(), 10)->isVisible(), "Password icon is visible");
+
+ // Hide menu again after checking the icon.
+ $this->actor->find(self::roomModerationButton(), 2)->click();
}
/**
* @Then I see that the conversation is not password protected
*/
public function iSeeThatTheConversationIsNotPasswordProtected() {
+ $this->showRoomModerationMenu();
+
PHPUnit_Framework_Assert::assertTrue($this->actor->find(self::noPasswordIcon(), 10)->isVisible(), "No password icon is visible");
+
+ // Hide menu again after checking the icon.
+ $this->actor->find(self::roomModerationButton(), 2)->click();
+ }
+
+ private function showRoomModerationMenu() {
+ // The room moderation menu is hidden after clicking on an action of the
+ // menu. Therefore, if the menu is visible, wait a little just in case
+ // it is in the process of being hidden due to a previous action.
+ if (!WaitFor::elementToBeEventuallyNotShown(
+ $this->actor,
+ self::roomModerationMenu(),
+ $timeout = 5 * $this->actor->getFindTimeoutMultiplier())) {
+ PHPUnit_Framework_Assert::fail("The room moderation menu is still shown after $timeout seconds");
+ }
+
+ $this->actor->find(self::roomModerationButton(), 10)->click();
}
}