FEATURE: Replace share-popup with share-topic (#16108)
share-topic modal is used everywhere expect when clicking on the top right corner of the post. This changes standardize on share-topic modal and add the missing features from share-popup.
This commit is contained in:
parent
d19b5fe80b
commit
08a1f41582
|
@ -1,219 +0,0 @@
|
|||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
||||
import { later, scheduleOnce } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import Sharing from "discourse/lib/sharing";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { longDateNoYear } from "discourse/lib/formatter";
|
||||
import { nativeShare } from "discourse/lib/pwa-utils";
|
||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||
|
||||
export default Component.extend({
|
||||
elementId: "share-link",
|
||||
classNameBindings: ["visible"],
|
||||
link: null,
|
||||
visible: null,
|
||||
privateCategory: alias("topic.category.read_restricted"),
|
||||
|
||||
@discourseComputed("topic.{isPrivateMessage,invisible,category}")
|
||||
sources(topic) {
|
||||
const privateContext =
|
||||
this.siteSettings.login_required ||
|
||||
(topic && topic.isPrivateMessage) ||
|
||||
(topic && topic.invisible) ||
|
||||
this.privateCategory;
|
||||
|
||||
return Sharing.activeSources(this.siteSettings.share_links, privateContext);
|
||||
},
|
||||
|
||||
@discourseComputed("type", "postNumber")
|
||||
shareTitle(type, postNumber) {
|
||||
if (type === "topic") {
|
||||
return I18n.t("share.topic");
|
||||
}
|
||||
if (postNumber) {
|
||||
return I18n.t("share.post", { postNumber });
|
||||
}
|
||||
return I18n.t("share.topic");
|
||||
},
|
||||
|
||||
@discourseComputed("date")
|
||||
displayDate(date) {
|
||||
return longDateNoYear(new Date(date));
|
||||
},
|
||||
|
||||
_focusUrl() {
|
||||
// Wait for the fade-in transition to finish before selecting the link:
|
||||
later(() => {
|
||||
if (this.element) {
|
||||
const linkInput = this.element.querySelector("#share-link input");
|
||||
linkInput.value = this.link;
|
||||
if (!this.site.mobileView) {
|
||||
// if the input is auto-focused on mobile, iOS requires two taps of the copy button
|
||||
linkInput.setSelectionRange(0, this.link.length);
|
||||
linkInput.focus();
|
||||
}
|
||||
}
|
||||
}, 200);
|
||||
},
|
||||
|
||||
_showUrl($target, url) {
|
||||
const currentTargetOffset = $target.offset();
|
||||
const $this = $(this.element);
|
||||
|
||||
if (isEmpty(url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Relative urls
|
||||
if (url.indexOf("/") === 0) {
|
||||
url = window.location.protocol + "//" + window.location.host + url;
|
||||
}
|
||||
|
||||
const shareLinkWidth = $this.width();
|
||||
let x = currentTargetOffset.left - shareLinkWidth / 2;
|
||||
if (x < 25) {
|
||||
x = 25;
|
||||
}
|
||||
if (x + shareLinkWidth > $(window).width()) {
|
||||
x -= shareLinkWidth / 2;
|
||||
}
|
||||
|
||||
const header = $(".d-header");
|
||||
let y = currentTargetOffset.top - ($this.height() + 20);
|
||||
if (y < header.offset().top + header.height()) {
|
||||
y = currentTargetOffset.top + 10;
|
||||
}
|
||||
|
||||
this.element.style.top = `${y}px`;
|
||||
|
||||
if (!this.site.mobileView) {
|
||||
this.element.style.left = `${x}px`;
|
||||
if (document.documentElement.classList.contains("rtl")) {
|
||||
this.element.style.right = "unset";
|
||||
}
|
||||
}
|
||||
this.set("link", url);
|
||||
this.set("visible", true);
|
||||
|
||||
scheduleOnce("afterRender", this, this._focusUrl);
|
||||
},
|
||||
|
||||
@bind
|
||||
_mouseDownHandler(event) {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use mousedown instead of click so this event is handled before routing occurs when a
|
||||
// link is clicked (which is a click event) while the share dialog is showing.
|
||||
if ($(this.element).has(event.target).length !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.send("close");
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@bind
|
||||
_clickHandler(event) {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if they want to open in a new tab, let it so
|
||||
if (wantsNewWindow(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const $currentTarget = $(event.currentTarget);
|
||||
const url = $currentTarget.data("share-url");
|
||||
const postNumber = $currentTarget.data("post-number");
|
||||
const postId = $currentTarget.closest("article").data("post-id");
|
||||
const date = $currentTarget.children().data("time");
|
||||
|
||||
this.setProperties({ postNumber, date, postId });
|
||||
|
||||
// use native webshare only when the user clicks on the "chain" icon
|
||||
if (!$currentTarget.hasClass("post-date")) {
|
||||
nativeShare(this.capabilities, { url }).then(null, () =>
|
||||
this._showUrl($currentTarget, url)
|
||||
);
|
||||
} else {
|
||||
this._showUrl($currentTarget, url);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
@bind
|
||||
_keydownHandler(event) {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === "Escape") {
|
||||
this.send("close");
|
||||
}
|
||||
},
|
||||
|
||||
_shareUrlHandler(url, $target) {
|
||||
this._showUrl($target, url);
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
$("html")
|
||||
.on("mousedown.outside-share-link", this._mouseDownHandler)
|
||||
.on(
|
||||
"click.discourse-share-link",
|
||||
"button[data-share-url], .post-info .post-date[data-share-url]",
|
||||
this._clickHandler
|
||||
)
|
||||
.on("keydown.share-view", this._keydownHandler);
|
||||
|
||||
this.appEvents.on("share:url", this, "_shareUrlHandler");
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
$("html")
|
||||
.off("click.discourse-share-link", this._clickHandler)
|
||||
.off("mousedown.outside-share-link", this._mouseDownHandler)
|
||||
.off("keydown.share-view", this._keydownHandler);
|
||||
|
||||
this.appEvents.off("share:url", this, "_shareUrlHandler");
|
||||
},
|
||||
|
||||
actions: {
|
||||
replyAsNewTopic() {
|
||||
const postStream = this.get("topic.postStream");
|
||||
const postId = this.postId || postStream.findPostIdForPostNumber(1);
|
||||
const post = postStream.findLoadedPost(postId);
|
||||
this.replyAsNewTopic(post);
|
||||
this.send("close");
|
||||
},
|
||||
|
||||
close() {
|
||||
this.setProperties({
|
||||
link: null,
|
||||
postNumber: null,
|
||||
postId: null,
|
||||
visible: false,
|
||||
});
|
||||
},
|
||||
|
||||
share(source) {
|
||||
Sharing.shareSource(source, {
|
||||
url: this.link,
|
||||
title: this.get("topic.title"),
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -2,6 +2,7 @@ import Controller from "@ember/controller";
|
|||
import { action } from "@ember/object";
|
||||
import { getAbsoluteURL } from "discourse-common/lib/get-url";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { longDateNoYear } from "discourse/lib/formatter";
|
||||
import Sharing from "discourse/lib/sharing";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
|
@ -9,6 +10,7 @@ import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|||
import I18n from "I18n";
|
||||
import Category from "discourse/models/category";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
|
||||
export default Controller.extend(
|
||||
ModalFunctionality,
|
||||
|
@ -51,6 +53,12 @@ export default Controller.extend(
|
|||
}
|
||||
},
|
||||
|
||||
@discourseComputed("post.created_at", "post.wiki", "post.last_wiki_edit")
|
||||
displayDate(createdAt, wiki, lastWikiEdit) {
|
||||
const date = wiki && lastWikiEdit ? lastWikiEdit : createdAt;
|
||||
return longDateNoYear(new Date(date));
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"topic.{isPrivateMessage,invisible,category.read_restricted}"
|
||||
)
|
||||
|
@ -88,6 +96,16 @@ export default Controller.extend(
|
|||
});
|
||||
},
|
||||
|
||||
@action
|
||||
replyAsNewTopic() {
|
||||
const postStream = this.topic.postStream;
|
||||
const postId = this.post?.id || postStream.findPostIdForPostNumber(1);
|
||||
const post = postStream.findLoadedPost(postId);
|
||||
const topicController = getOwner(this).lookup("controller:topic");
|
||||
topicController.actions.replyAsNewTopic.call(topicController, post);
|
||||
this.send("closeModal");
|
||||
},
|
||||
|
||||
restrictedGroupWarning() {
|
||||
this.appEvents.on("modal:body-shown", () => {
|
||||
let restrictedGroups;
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
<div class="title">
|
||||
<h3>{{html-safe shareTitle}}</h3>
|
||||
|
||||
{{#if date}}
|
||||
<span class="date">{{displayDate}}</span>
|
||||
{{/if}}
|
||||
|
||||
{{d-button
|
||||
action=(action "close")
|
||||
class="btn btn-flat close"
|
||||
icon="times"
|
||||
aria-label="share.close"
|
||||
title="share.close"
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div class="link-share-container">
|
||||
<input class="share-link-input" type="text" aria-label={{i18n "share.url"}}> {{copy-button selector="input.share-link-input"}}
|
||||
</div>
|
||||
|
||||
<div class="link-share-actions">
|
||||
<div class="sources">
|
||||
{{#each sources as |s|}}
|
||||
{{share-source source=s title=model.title action=(action "share")}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div class="alt-actions">
|
||||
{{#if topic.details.can_reply_as_new_topic}}
|
||||
{{#if topic.isPrivateMessage}}
|
||||
{{d-button
|
||||
action=(action "replyAsNewTopic")
|
||||
class="btn btn-default new-topic"
|
||||
icon="plus"
|
||||
aria-label="post.reply_as_new_private_message"
|
||||
title="post.reply_as_new_private_message"
|
||||
label="user.new_private_message"
|
||||
}}
|
||||
{{else}}
|
||||
{{d-button
|
||||
action=(action "replyAsNewTopic")
|
||||
class="btn btn-default new-topic"
|
||||
icon="plus"
|
||||
aria-label="post.reply_as_new_topic"
|
||||
title="post.reply_as_new_topic"
|
||||
label="topic.create"
|
||||
}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -29,7 +29,33 @@
|
|||
action=(action "inviteUsers")
|
||||
}}
|
||||
{{/if}}
|
||||
|
||||
{{#if topic.details.can_reply_as_new_topic}}
|
||||
{{#if topic.isPrivateMessage}}
|
||||
{{d-button
|
||||
action=(action "replyAsNewTopic")
|
||||
class="btn-default new-topic"
|
||||
icon="plus"
|
||||
aria-label="post.reply_as_new_private_message"
|
||||
title="post.reply_as_new_private_message"
|
||||
label="user.new_private_message"
|
||||
}}
|
||||
{{else}}
|
||||
{{d-button
|
||||
action=(action "replyAsNewTopic")
|
||||
class="btn-default new-topic"
|
||||
icon="plus"
|
||||
aria-label="post.reply_as_new_topic"
|
||||
title="post.reply_as_new_topic"
|
||||
label="topic.create"
|
||||
}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if post}}
|
||||
<div class="date">{{displayDate}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
{{/d-modal-body}}
|
||||
|
|
|
@ -406,8 +406,6 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{share-popup topic=model replyAsNewTopic=(action "replyAsNewTopic")}}
|
||||
|
||||
{{quote-button
|
||||
quoteState=quoteState
|
||||
selectText=(action "selectText")
|
||||
|
|
|
@ -280,21 +280,6 @@ createWidget("post-meta-data", {
|
|||
);
|
||||
}
|
||||
|
||||
const lastWikiEdit =
|
||||
attrs.wiki && attrs.lastWikiEdit && new Date(attrs.lastWikiEdit);
|
||||
const createdAt = new Date(attrs.created_at);
|
||||
const date = lastWikiEdit ? dateNode(lastWikiEdit) : dateNode(createdAt);
|
||||
const attributes = {
|
||||
class: "post-date",
|
||||
href: attrs.shareUrl,
|
||||
"data-share-url": attrs.shareUrl,
|
||||
"data-post-number": attrs.post_number,
|
||||
};
|
||||
|
||||
if (lastWikiEdit) {
|
||||
attributes["class"] += " last-wiki-edit";
|
||||
}
|
||||
|
||||
if (attrs.via_email) {
|
||||
postInfo.push(this.attach("post-email-indicator", attrs));
|
||||
}
|
||||
|
@ -315,7 +300,7 @@ createWidget("post-meta-data", {
|
|||
postInfo.push(this.attach("reply-to-tab", attrs));
|
||||
}
|
||||
|
||||
postInfo.push(h("div.post-info.post-date", h("a", { attributes }, date)));
|
||||
postInfo.push(this.attach("post-date", attrs));
|
||||
|
||||
postInfo.push(
|
||||
h(
|
||||
|
@ -352,6 +337,29 @@ createWidget("expand-hidden", {
|
|||
},
|
||||
});
|
||||
|
||||
createWidget("post-date", {
|
||||
tagName: "div.post-info.post-date",
|
||||
|
||||
html(attrs) {
|
||||
const attributes = { class: "post-date" };
|
||||
let date;
|
||||
if (attrs.wiki && attrs.lastWikiEdit) {
|
||||
attributes["class"] += " last-wiki-edit";
|
||||
date = new Date(attrs.lastWikiEdit);
|
||||
} else {
|
||||
date = new Date(attrs.created_at);
|
||||
}
|
||||
return h("a", { attributes }, dateNode(date));
|
||||
},
|
||||
|
||||
click() {
|
||||
const post = this.findAncestorModel();
|
||||
const topic = post.topic;
|
||||
const controller = showModal("share-topic", { model: topic.category });
|
||||
controller.setProperties({ topic, post });
|
||||
},
|
||||
});
|
||||
|
||||
createWidget("expand-post-button", {
|
||||
tagName: "button.btn.expand-post",
|
||||
buildKey: (attrs) => `expand-post-button-${attrs.id}`,
|
||||
|
|
|
@ -39,7 +39,7 @@ acceptance("Share and Invite modal", function (needs) {
|
|||
await visit("/t/short-topic-with-two-posts/54077");
|
||||
await click("#post_2 .post-info.post-date a");
|
||||
|
||||
assert.ok(exists("#share-link"), "it shows the share modal");
|
||||
assert.ok(exists(".share-topic-modal"), "it shows the share modal");
|
||||
});
|
||||
|
||||
test("Share topic in a restricted category", async function (assert) {
|
||||
|
|
|
@ -74,7 +74,7 @@ acceptance("Topic", function (needs) {
|
|||
await visit("/t/internationalization-localization/280");
|
||||
await click(".topic-post:first-child button.share");
|
||||
|
||||
assert.ok(exists("#share-link"), "it shows the share modal");
|
||||
assert.ok(exists(".share-topic-modal"), "it shows the share modal");
|
||||
});
|
||||
|
||||
test("Showing and hiding the edit controls", async function (assert) {
|
||||
|
|
|
@ -22,10 +22,7 @@ discourseModule("Integration | Component | Widget | post", function (hooks) {
|
|||
},
|
||||
test(assert) {
|
||||
assert.ok(exists(".names"), "includes poster name");
|
||||
|
||||
assert.ok(exists("a.post-date"), "includes post date");
|
||||
assert.ok(exists("a.post-date[data-share-url]"));
|
||||
assert.ok(exists("a.post-date[data-post-number]"));
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
.link-share-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
align-items: baseline;
|
||||
|
||||
button {
|
||||
margin-top: 0.5em;
|
||||
|
@ -26,17 +26,15 @@
|
|||
|
||||
.sources {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.new-topic {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.alt-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
.date {
|
||||
color: var(--primary-med-or-secondary-med);
|
||||
font-weight: normal;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
@ -52,47 +50,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// post share popup
|
||||
|
||||
#share-link {
|
||||
position: absolute;
|
||||
z-index: z("dropdown");
|
||||
box-shadow: shadow("card");
|
||||
background-color: var(--secondary);
|
||||
padding: 0.5em;
|
||||
width: 20em; // scales with user font-size
|
||||
max-width: 100vw; // prevents overflow due to extra large user font-size
|
||||
display: none;
|
||||
&.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 0.5em;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
h3 {
|
||||
font-size: $font-0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-weight: normal;
|
||||
color: var(--primary-med-or-secondary-med);
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.btn.close {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// topic share modal
|
||||
|
||||
.share-topic-modal {
|
||||
|
|
Loading…
Reference in New Issue