summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/components/follow_button
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/components/follow_button
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/follow_button')
-rw-r--r--addons/mail/static/src/components/follow_button/follow_button.js93
-rw-r--r--addons/mail/static/src/components/follow_button/follow_button.scss27
-rw-r--r--addons/mail/static/src/components/follow_button/follow_button.xml24
-rw-r--r--addons/mail/static/src/components/follow_button/follow_button_tests.js278
4 files changed, 422 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/follow_button/follow_button.js b/addons/mail/static/src/components/follow_button/follow_button.js
new file mode 100644
index 00000000..3c1808cb
--- /dev/null
+++ b/addons/mail/static/src/components/follow_button/follow_button.js
@@ -0,0 +1,93 @@
+odoo.define('mail/static/src/components/follow_button/follow_button.js', function (require) {
+'use strict';
+
+const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js');
+const useStore = require('mail/static/src/component_hooks/use_store/use_store.js');
+
+const { Component } = owl;
+const { useState } = owl.hooks;
+
+class FollowButton extends Component {
+ /**
+ * @override
+ */
+ constructor(...args) {
+ super(...args);
+ useShouldUpdateBasedOnProps();
+ this.state = useState({
+ /**
+ * Determine whether the unfollow button is highlighted or not.
+ */
+ isUnfollowButtonHighlighted: false,
+ });
+ useStore(props => {
+ const thread = this.env.models['mail.thread'].get(props.threadLocalId);
+ return {
+ threadIsCurrentPartnerFollowing: thread && thread.isCurrentPartnerFollowing,
+ };
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @return {mail.thread}
+ */
+ get thread() {
+ return this.env.models['mail.thread'].get(this.props.threadLocalId);
+ }
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onClickFollow(ev) {
+ this.thread.follow();
+ }
+
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onClickUnfollow(ev) {
+ this.thread.unfollow();
+ }
+
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onMouseLeaveUnfollow(ev) {
+ this.state.isUnfollowButtonHighlighted = false;
+ }
+
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onMouseEnterUnfollow(ev) {
+ this.state.isUnfollowButtonHighlighted = true;
+ }
+
+}
+
+Object.assign(FollowButton, {
+ defaultProps: {
+ isDisabled: false,
+ },
+ props: {
+ isDisabled: Boolean,
+ threadLocalId: String,
+ },
+ template: 'mail.FollowButton',
+});
+
+return FollowButton;
+
+});
diff --git a/addons/mail/static/src/components/follow_button/follow_button.scss b/addons/mail/static/src/components/follow_button/follow_button.scss
new file mode 100644
index 00000000..36fb60e7
--- /dev/null
+++ b/addons/mail/static/src/components/follow_button/follow_button.scss
@@ -0,0 +1,27 @@
+// ------------------------------------------------------------------
+// Layout
+// ------------------------------------------------------------------
+
+.o_FollowButton {
+ display: flex;
+}
+
+// ------------------------------------------------------------------
+// Style
+// ------------------------------------------------------------------
+
+.o_FollowButton_follow {
+ color: gray('600');
+}
+
+.o_FollowButton_unfollow {
+ color: gray('600');
+
+ &.o-following {
+ color: $green;
+ }
+
+ &.o-unfollow {
+ color: $orange;
+ }
+}
diff --git a/addons/mail/static/src/components/follow_button/follow_button.xml b/addons/mail/static/src/components/follow_button/follow_button.xml
new file mode 100644
index 00000000..00fc8d65
--- /dev/null
+++ b/addons/mail/static/src/components/follow_button/follow_button.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates xml:space="preserve">
+
+ <t t-name="mail.FollowButton" owl="1">
+ <div class="o_FollowButton">
+ <t t-if="thread.isCurrentPartnerFollowing">
+ <button class="o_FollowButton_unfollow btn btn-link" t-att-class="{ 'o-following': !state.isUnfollowButtonHighlighted, 'o-unfollow': state.isUnfollowButtonHighlighted }" t-att-disabled="props.isDisabled" t-on-click="_onClickUnfollow" t-on-mouseenter="_onMouseEnterUnfollow" t-on-mouseleave="_onMouseLeaveUnfollow">
+ <t t-if="state.isUnfollowButtonHighlighted">
+ <i class="fa fa-times"/> Unfollow
+ </t>
+ <t t-else="">
+ <i class="fa fa-check"/> Following
+ </t>
+ </button>
+ </t>
+ <t t-else="">
+ <button class="o_FollowButton_follow btn btn-link" t-att-disabled="props.isDisabled" t-on-click="_onClickFollow">
+ Follow
+ </button>
+ </t>
+ </div>
+ </t>
+
+</templates>
diff --git a/addons/mail/static/src/components/follow_button/follow_button_tests.js b/addons/mail/static/src/components/follow_button/follow_button_tests.js
new file mode 100644
index 00000000..0c4553c6
--- /dev/null
+++ b/addons/mail/static/src/components/follow_button/follow_button_tests.js
@@ -0,0 +1,278 @@
+odoo.define('mail/static/src/components/follow_button/follow_button_tests.js', function (require) {
+'use strict';
+
+const components = {
+ FollowButton: require('mail/static/src/components/follow_button/follow_button.js'),
+};
+const {
+ afterEach,
+ afterNextRender,
+ beforeEach,
+ createRootComponent,
+ start,
+} = require('mail/static/src/utils/test_utils.js');
+
+QUnit.module('mail', {}, function () {
+QUnit.module('components', {}, function () {
+QUnit.module('follow_button', {}, function () {
+QUnit.module('follow_button_tests.js', {
+ beforeEach() {
+ beforeEach(this);
+
+ this.createFollowButtonComponent = async (thread, otherProps = {}) => {
+ const props = Object.assign({ threadLocalId: thread.localId }, otherProps);
+ await createRootComponent(this, components.FollowButton, {
+ props,
+ target: this.widget.el,
+ });
+ };
+
+ this.start = async params => {
+ const { env, widget } = await start(Object.assign({}, params, {
+ data: this.data,
+ }));
+ this.env = env;
+ this.widget = widget;
+ };
+ },
+ afterEach() {
+ afterEach(this);
+ },
+});
+
+QUnit.test('base rendering not editable', async function (assert) {
+ assert.expect(3);
+
+ await this.start();
+ const thread = this.env.models['mail.thread'].create({
+ id: 100,
+ model: 'res.partner',
+ });
+ await this.createFollowButtonComponent(thread, { isDisabled: true });
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton',
+ "should have follow button component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton_follow',
+ "should have 'Follow' button"
+ );
+ assert.ok(
+ document.querySelector('.o_FollowButton_follow').disabled,
+ "'Follow' button should be disabled"
+ );
+});
+
+QUnit.test('base rendering editable', async function (assert) {
+ assert.expect(3);
+
+ await this.start();
+ const thread = this.env.models['mail.thread'].create({
+ id: 100,
+ model: 'res.partner',
+ });
+ await this.createFollowButtonComponent(thread);
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton',
+ "should have follow button component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton_follow',
+ "should have 'Follow' button"
+ );
+ assert.notOk(
+ document.querySelector('.o_FollowButton_follow').disabled,
+ "'Follow' button should be disabled"
+ );
+});
+
+QUnit.test('hover following button', async function (assert) {
+ assert.expect(8);
+
+ this.data['res.partner'].records.push({ id: 100, message_follower_ids: [1] });
+ this.data['mail.followers'].records.push({
+ id: 1,
+ is_active: true,
+ is_editable: true,
+ partner_id: this.data.currentPartnerId,
+ res_id: 100,
+ res_model: 'res.partner',
+ });
+ await this.start();
+ const thread = this.env.models['mail.thread'].create({
+ id: 100,
+ model: 'res.partner',
+ });
+ thread.follow();
+ await this.createFollowButtonComponent(thread);
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton',
+ "should have follow button component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton_unfollow',
+ "should have 'Unfollow' button"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_FollowButton_unfollow').textContent.trim(),
+ 'Following',
+ "'unfollow' button should display 'Following' as text when not hovered"
+ );
+ assert.containsNone(
+ document.querySelector('.o_FollowButton_unfollow'),
+ '.fa-times',
+ "'unfollow' button should not contain a cross icon when not hovered"
+ );
+ assert.containsOnce(
+ document.querySelector('.o_FollowButton_unfollow'),
+ '.fa-check',
+ "'unfollow' button should contain a check icon when not hovered"
+ );
+
+ await afterNextRender(() => {
+ document
+ .querySelector('.o_FollowButton_unfollow')
+ .dispatchEvent(new window.MouseEvent('mouseenter'));
+ }
+ );
+ assert.strictEqual(
+ document.querySelector('.o_FollowButton_unfollow').textContent.trim(),
+ 'Unfollow',
+ "'unfollow' button should display 'Unfollow' as text when hovered"
+ );
+ assert.containsOnce(
+ document.querySelector('.o_FollowButton_unfollow'),
+ '.fa-times',
+ "'unfollow' button should contain a cross icon when hovered"
+ );
+ assert.containsNone(
+ document.querySelector('.o_FollowButton_unfollow'),
+ '.fa-check',
+ "'unfollow' button should not contain a check icon when hovered"
+ );
+});
+
+QUnit.test('click on "follow" button', async function (assert) {
+ assert.expect(7);
+
+ this.data['res.partner'].records.push({ id: 100, message_follower_ids: [1] });
+ this.data['mail.followers'].records.push({
+ id: 1,
+ is_active: true,
+ is_editable: true,
+ partner_id: this.data.currentPartnerId,
+ res_id: 100,
+ res_model: 'res.partner',
+ });
+ await this.start({
+ async mockRPC(route, args) {
+ if (route.includes('message_subscribe')) {
+ assert.step('rpc:message_subscribe');
+ } else if (route.includes('mail/read_followers')) {
+ assert.step('rpc:mail/read_followers');
+ }
+ return this._super(...arguments);
+ },
+ });
+ const thread = this.env.models['mail.thread'].create({
+ id: 100,
+ model: 'res.partner',
+ });
+ await this.createFollowButtonComponent(thread);
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton',
+ "should have follow button component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton_follow',
+ "should have button follow"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_FollowButton_follow').click();
+ });
+ assert.verifySteps([
+ 'rpc:message_subscribe',
+ 'rpc:mail/read_followers',
+ ]);
+ assert.containsNone(
+ document.body,
+ '.o_FollowButton_follow',
+ "should not have follow button after clicked on follow"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton_unfollow',
+ "should have unfollow button after clicked on follow"
+ );
+});
+
+QUnit.test('click on "unfollow" button', async function (assert) {
+ assert.expect(7);
+
+ this.data['res.partner'].records.push({ id: 100, message_follower_ids: [1] });
+ this.data['mail.followers'].records.push({
+ id: 1,
+ is_active: true,
+ is_editable: true,
+ partner_id: this.data.currentPartnerId,
+ res_id: 100,
+ res_model: 'res.partner',
+ });
+ await this.start({
+ async mockRPC(route, args) {
+ if (route.includes('message_unsubscribe')) {
+ assert.step('rpc:message_unsubscribe');
+ }
+ return this._super(...arguments);
+ },
+ });
+ const thread = this.env.models['mail.thread'].create({
+ id: 100,
+ model: 'res.partner',
+ });
+ thread.follow();
+ await this.createFollowButtonComponent(thread);
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton',
+ "should have follow button component"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_FollowButton_follow',
+ "should not have button follow"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton_unfollow',
+ "should have button unfollow"
+ );
+
+ await afterNextRender(() => document.querySelector('.o_FollowButton_unfollow').click());
+ assert.verifySteps(['rpc:message_unsubscribe']);
+ assert.containsOnce(
+ document.body,
+ '.o_FollowButton_follow',
+ "should have follow button after clicked on unfollow"
+ );
+ assert.containsNone(
+ document.body,
+ '.o_FollowButton_unfollow',
+ "should not have unfollow button after clicked on unfollow"
+ );
+});
+
+});
+});
+});
+
+});