[WIP] FEATURE: merge share and invite actions together (#7021)
This commit also: - removes [+ New Topic] behaviour from share, this feature has been duplicated in composer actions, months ago - introduces our new experimental spacing standard for css: eg: `s(2)` - introduces a new panel UI for modals
This commit is contained in:
parent
6a8007e5fb
commit
04a63cfaaa
|
@ -1,9 +1,17 @@
|
||||||
import { on } from "ember-addons/ember-computed-decorators";
|
import { on } from "ember-addons/ember-computed-decorators";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNameBindings: [":modal", ":d-modal", "modalClass", "modalStyle"],
|
classNameBindings: [
|
||||||
|
":modal",
|
||||||
|
":d-modal",
|
||||||
|
"modalClass",
|
||||||
|
"modalStyle",
|
||||||
|
"hasPanels"
|
||||||
|
],
|
||||||
attributeBindings: ["data-keyboard"],
|
attributeBindings: ["data-keyboard"],
|
||||||
dismissable: true,
|
dismissable: true,
|
||||||
|
title: null,
|
||||||
|
subtitle: null,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
|
@ -30,6 +30,7 @@ export default Ember.Component.extend({
|
||||||
? []
|
? []
|
||||||
: [groupNames],
|
: [groupNames],
|
||||||
single: this.get("single"),
|
single: this.get("single"),
|
||||||
|
fullWidthWrap: this.get("fullWidthWrap"),
|
||||||
updateData: opts && opts.updateData ? opts.updateData : false,
|
updateData: opts && opts.updateData ? opts.updateData : false,
|
||||||
onChangeItems: items => {
|
onChangeItems: items => {
|
||||||
selectedGroups = items;
|
selectedGroups = items;
|
||||||
|
|
|
@ -1,34 +1,30 @@
|
||||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|
||||||
import { emailValid } from "discourse/lib/utilities";
|
import { emailValid } from "discourse/lib/utilities";
|
||||||
import computed from "ember-addons/ember-computed-decorators";
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
import Group from "discourse/models/group";
|
import Group from "discourse/models/group";
|
||||||
import Invite from "discourse/models/invite";
|
import Invite from "discourse/models/invite";
|
||||||
|
import { i18n } from "discourse/lib/computed";
|
||||||
|
|
||||||
export default Ember.Controller.extend(ModalFunctionality, {
|
export default Ember.Component.extend({
|
||||||
userInvitedShow: Ember.inject.controller("user-invited-show"),
|
tagName: null,
|
||||||
|
|
||||||
// If this isn't defined, it will proxy to the user model on the preferences
|
inviteModel: Ember.computed.alias("panel.model.inviteModel"),
|
||||||
|
userInvitedShow: Ember.computed.alias("panel.model.userInvitedShow"),
|
||||||
|
|
||||||
|
// If this isn't defined, it will proxy to the user topic on the preferences
|
||||||
// page which is wrong.
|
// page which is wrong.
|
||||||
emailOrUsername: null,
|
emailOrUsername: null,
|
||||||
hasCustomMessage: false,
|
hasCustomMessage: false,
|
||||||
|
hasCustomMessage: false,
|
||||||
customMessage: null,
|
customMessage: null,
|
||||||
inviteIcon: "envelope",
|
inviteIcon: "envelope",
|
||||||
invitingExistingUserToTopic: false,
|
invitingExistingUserToTopic: false,
|
||||||
|
|
||||||
@computed("isMessage", "invitingToTopic")
|
isAdmin: Ember.computed.alias("currentUser.admin"),
|
||||||
title(isMessage, invitingToTopic) {
|
|
||||||
if (isMessage) {
|
|
||||||
return "topic.invite_private.title";
|
|
||||||
} else if (invitingToTopic) {
|
|
||||||
return "topic.invite_reply.title";
|
|
||||||
} else {
|
|
||||||
return "user.invited.create";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed
|
willDestroyElement() {
|
||||||
isAdmin() {
|
this._super(...arguments);
|
||||||
return this.currentUser.admin;
|
|
||||||
|
this.reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed(
|
@computed(
|
||||||
|
@ -36,9 +32,9 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
"emailOrUsername",
|
"emailOrUsername",
|
||||||
"invitingToTopic",
|
"invitingToTopic",
|
||||||
"isPrivateTopic",
|
"isPrivateTopic",
|
||||||
"model.groupNames",
|
"topic.groupNames",
|
||||||
"model.saving",
|
"topic.saving",
|
||||||
"model.details.can_invite_to"
|
"topic.details.can_invite_to"
|
||||||
)
|
)
|
||||||
disabled(
|
disabled(
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
@ -51,26 +47,39 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
) {
|
) {
|
||||||
if (saving) return true;
|
if (saving) return true;
|
||||||
if (Ember.isEmpty(emailOrUsername)) return true;
|
if (Ember.isEmpty(emailOrUsername)) return true;
|
||||||
|
|
||||||
const emailTrimmed = emailOrUsername.trim();
|
const emailTrimmed = emailOrUsername.trim();
|
||||||
|
|
||||||
// when inviting to forum, email must be valid
|
// when inviting to forum, email must be valid
|
||||||
if (!invitingToTopic && !emailValid(emailTrimmed)) return true;
|
if (!invitingToTopic && !emailValid(emailTrimmed)) {
|
||||||
// normal users (not admin) can't invite users to private topic via email
|
|
||||||
if (!isAdmin && isPrivateTopic && emailValid(emailTrimmed)) return true;
|
|
||||||
// when inviting to private topic via email, group name must be specified
|
|
||||||
if (isPrivateTopic && Ember.isEmpty(groupNames) && emailValid(emailTrimmed))
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal users (not admin) can't invite users to private topic via email
|
||||||
|
if (!isAdmin && isPrivateTopic && emailValid(emailTrimmed)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when inviting to private topic via email, group name must be specified
|
||||||
|
if (
|
||||||
|
isPrivateTopic &&
|
||||||
|
Ember.isEmpty(groupNames) &&
|
||||||
|
emailValid(emailTrimmed)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (can_invite_to) return false;
|
if (can_invite_to) return false;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed(
|
@computed(
|
||||||
"isAdmin",
|
"isAdmin",
|
||||||
"emailOrUsername",
|
"emailOrUsername",
|
||||||
"model.saving",
|
"inviteModel.saving",
|
||||||
"isPrivateTopic",
|
"isPrivateTopic",
|
||||||
"model.groupNames",
|
"inviteModel.groupNames",
|
||||||
"hasCustomMessage"
|
"hasCustomMessage"
|
||||||
)
|
)
|
||||||
disabledCopyLink(
|
disabledCopyLink(
|
||||||
|
@ -84,54 +93,65 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
if (hasCustomMessage) return true;
|
if (hasCustomMessage) return true;
|
||||||
if (saving) return true;
|
if (saving) return true;
|
||||||
if (Ember.isEmpty(emailOrUsername)) return true;
|
if (Ember.isEmpty(emailOrUsername)) return true;
|
||||||
|
|
||||||
const email = emailOrUsername.trim();
|
const email = emailOrUsername.trim();
|
||||||
|
|
||||||
// email must be valid
|
// email must be valid
|
||||||
if (!emailValid(email)) return true;
|
if (!emailValid(email)) {
|
||||||
// normal users (not admin) can't invite users to private topic via email
|
|
||||||
if (!isAdmin && isPrivateTopic && emailValid(email)) return true;
|
|
||||||
// when inviting to private topic via email, group name must be specified
|
|
||||||
if (isPrivateTopic && Ember.isEmpty(groupNames) && emailValid(email))
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal users (not admin) can't invite users to private topic via email
|
||||||
|
if (!isAdmin && isPrivateTopic && emailValid(email)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when inviting to private topic via email, group name must be specified
|
||||||
|
if (isPrivateTopic && Ember.isEmpty(groupNames) && emailValid(email)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("model.saving")
|
@computed("inviteModel.saving")
|
||||||
buttonTitle(saving) {
|
buttonTitle(saving) {
|
||||||
return saving ? "topic.inviting" : "topic.invite_reply.action";
|
return saving ? "topic.inviting" : "topic.invite_reply.action";
|
||||||
},
|
},
|
||||||
|
|
||||||
// We are inviting to a topic if the model isn't the current user.
|
// We are inviting to a topic if the topic isn't the current user.
|
||||||
// The current user would mean we are inviting to the forum in general.
|
// The current user would mean we are inviting to the forum in general.
|
||||||
@computed("model")
|
@computed("inviteModel")
|
||||||
invitingToTopic(model) {
|
invitingToTopic(inviteModel) {
|
||||||
return model !== this.currentUser;
|
return inviteModel !== this.currentUser;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("model", "model.details.can_invite_via_email")
|
@computed("inviteModel", "inviteModel.details.can_invite_via_email")
|
||||||
canInviteViaEmail(model, can_invite_via_email) {
|
canInviteViaEmail(inviteModel, canInviteViaEmail) {
|
||||||
return this.get("model") === this.currentUser ? true : can_invite_via_email;
|
return this.get("inviteModel") === this.currentUser
|
||||||
|
? true
|
||||||
|
: canInviteViaEmail;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("isMessage", "canInviteViaEmail")
|
@computed("isPM", "canInviteViaEmail")
|
||||||
showCopyInviteButton(isMessage, canInviteViaEmail) {
|
showCopyInviteButton(isPM, canInviteViaEmail) {
|
||||||
return canInviteViaEmail && !isMessage;
|
return canInviteViaEmail && !isPM;
|
||||||
},
|
},
|
||||||
|
|
||||||
topicId: Ember.computed.alias("model.id"),
|
topicId: Ember.computed.alias("inviteModel.id"),
|
||||||
|
|
||||||
// Is Private Topic? (i.e. visible only to specific group members)
|
// eg: visible only to specific group members
|
||||||
isPrivateTopic: Ember.computed.and(
|
isPrivateTopic: Ember.computed.and(
|
||||||
"invitingToTopic",
|
"invitingToTopic",
|
||||||
"model.category.read_restricted"
|
"inviteModel.category.read_restricted"
|
||||||
),
|
),
|
||||||
|
|
||||||
// Is Private Message?
|
isPM: Ember.computed.equal("inviteModel.archetype", "private_message"),
|
||||||
isMessage: Ember.computed.equal("model.archetype", "private_message"),
|
|
||||||
|
|
||||||
// Allow Existing Members? (username autocomplete)
|
// scope to allowed usernames
|
||||||
allowExistingMembers: Ember.computed.alias("invitingToTopic"),
|
allowExistingMembers: Ember.computed.alias("invitingToTopic"),
|
||||||
|
|
||||||
@computed("isAdmin", "model.group_users")
|
@computed("isAdmin", "inviteModel.group_users")
|
||||||
isGroupOwnerOrAdmin(isAdmin, groupUsers) {
|
isGroupOwnerOrAdmin(isAdmin, groupUsers) {
|
||||||
return (
|
return (
|
||||||
isAdmin || (groupUsers && groupUsers.some(groupUser => groupUser.owner))
|
isAdmin || (groupUsers && groupUsers.some(groupUser => groupUser.owner))
|
||||||
|
@ -143,7 +163,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
"isGroupOwnerOrAdmin",
|
"isGroupOwnerOrAdmin",
|
||||||
"emailOrUsername",
|
"emailOrUsername",
|
||||||
"isPrivateTopic",
|
"isPrivateTopic",
|
||||||
"isMessage",
|
"isPM",
|
||||||
"invitingToTopic",
|
"invitingToTopic",
|
||||||
"canInviteViaEmail"
|
"canInviteViaEmail"
|
||||||
)
|
)
|
||||||
|
@ -151,14 +171,14 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
isGroupOwnerOrAdmin,
|
isGroupOwnerOrAdmin,
|
||||||
emailOrUsername,
|
emailOrUsername,
|
||||||
isPrivateTopic,
|
isPrivateTopic,
|
||||||
isMessage,
|
isPM,
|
||||||
invitingToTopic,
|
invitingToTopic,
|
||||||
canInviteViaEmail
|
canInviteViaEmail
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
isGroupOwnerOrAdmin &&
|
isGroupOwnerOrAdmin &&
|
||||||
canInviteViaEmail &&
|
canInviteViaEmail &&
|
||||||
!isMessage &&
|
!isPM &&
|
||||||
(emailValid(emailOrUsername) || isPrivateTopic || !invitingToTopic)
|
(emailValid(emailOrUsername) || isPrivateTopic || !invitingToTopic)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -166,13 +186,14 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
@computed("emailOrUsername")
|
@computed("emailOrUsername")
|
||||||
showCustomMessage(emailOrUsername) {
|
showCustomMessage(emailOrUsername) {
|
||||||
return (
|
return (
|
||||||
this.get("model") === this.currentUser || emailValid(emailOrUsername)
|
this.get("inviteModel") === this.currentUser ||
|
||||||
|
emailValid(emailOrUsername)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Instructional text for the modal.
|
// Instructional text for the modal.
|
||||||
@computed(
|
@computed(
|
||||||
"isMessage",
|
"isPM",
|
||||||
"invitingToTopic",
|
"invitingToTopic",
|
||||||
"emailOrUsername",
|
"emailOrUsername",
|
||||||
"isPrivateTopic",
|
"isPrivateTopic",
|
||||||
|
@ -180,7 +201,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
"canInviteViaEmail"
|
"canInviteViaEmail"
|
||||||
)
|
)
|
||||||
inviteInstructions(
|
inviteInstructions(
|
||||||
isMessage,
|
isPM,
|
||||||
invitingToTopic,
|
invitingToTopic,
|
||||||
emailOrUsername,
|
emailOrUsername,
|
||||||
isPrivateTopic,
|
isPrivateTopic,
|
||||||
|
@ -190,7 +211,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
if (!canInviteViaEmail) {
|
if (!canInviteViaEmail) {
|
||||||
// can't invite via email, only existing users
|
// can't invite via email, only existing users
|
||||||
return I18n.t("topic.invite_reply.sso_enabled");
|
return I18n.t("topic.invite_reply.sso_enabled");
|
||||||
} else if (isMessage) {
|
} else if (isPM) {
|
||||||
// inviting to a message
|
// inviting to a message
|
||||||
return I18n.t("topic.invite_private.email_or_username");
|
return I18n.t("topic.invite_private.email_or_username");
|
||||||
} else if (invitingToTopic) {
|
} else if (invitingToTopic) {
|
||||||
|
@ -222,14 +243,14 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
},
|
},
|
||||||
|
|
||||||
groupFinder(term) {
|
groupFinder(term) {
|
||||||
return Group.findAll({ term: term, ignore_automatic: true });
|
return Group.findAll({ term, ignore_automatic: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("isMessage", "emailOrUsername", "invitingExistingUserToTopic")
|
@computed("isPM", "emailOrUsername", "invitingExistingUserToTopic")
|
||||||
successMessage(isMessage, emailOrUsername, invitingExistingUserToTopic) {
|
successMessage(isPM, emailOrUsername, invitingExistingUserToTopic) {
|
||||||
if (this.get("hasGroups")) {
|
if (this.get("hasGroups")) {
|
||||||
return I18n.t("topic.invite_private.success_group");
|
return I18n.t("topic.invite_private.success_group");
|
||||||
} else if (isMessage) {
|
} else if (isPM) {
|
||||||
return I18n.t("topic.invite_private.success");
|
return I18n.t("topic.invite_private.success");
|
||||||
} else if (invitingExistingUserToTopic) {
|
} else if (invitingExistingUserToTopic) {
|
||||||
return I18n.t("topic.invite_reply.success_existing_email", {
|
return I18n.t("topic.invite_reply.success_existing_email", {
|
||||||
|
@ -242,9 +263,9 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("isMessage")
|
@computed("isPM")
|
||||||
errorMessage(isMessage) {
|
errorMessage(isPM) {
|
||||||
return isMessage
|
return isPM
|
||||||
? I18n.t("topic.invite_private.error")
|
? I18n.t("topic.invite_private.error")
|
||||||
: I18n.t("topic.invite_reply.error");
|
: I18n.t("topic.invite_reply.error");
|
||||||
},
|
},
|
||||||
|
@ -256,18 +277,18 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
: "topic.invite_reply.username_placeholder";
|
: "topic.invite_reply.username_placeholder";
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed
|
customMessagePlaceholder: i18n("invite.custom_message_placeholder"),
|
||||||
customMessagePlaceholder() {
|
|
||||||
return I18n.t("invite.custom_message_placeholder");
|
|
||||||
},
|
|
||||||
|
|
||||||
// Reset the modal to allow a new user to be invited.
|
// Reset the modal to allow a new user to be invited.
|
||||||
reset() {
|
reset() {
|
||||||
this.set("emailOrUsername", null);
|
this.setProperties({
|
||||||
this.set("hasCustomMessage", false);
|
emailOrUsername: null,
|
||||||
this.set("customMessage", null);
|
hasCustomMessage: false,
|
||||||
this.set("invitingExistingUserToTopic", false);
|
customMessage: null,
|
||||||
this.get("model").setProperties({
|
invitingExistingUserToTopic: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.get("inviteModel").setProperties({
|
||||||
groupNames: null,
|
groupNames: null,
|
||||||
error: false,
|
error: false,
|
||||||
saving: false,
|
saving: false,
|
||||||
|
@ -278,24 +299,23 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
createInvite() {
|
createInvite() {
|
||||||
const self = this;
|
|
||||||
if (this.get("disabled")) {
|
if (this.get("disabled")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupNames = this.get("model.groupNames"),
|
const groupNames = this.get("inviteModel.groupNames");
|
||||||
userInvitedController = this.get("userInvitedShow"),
|
const userInvitedController = this.get("userInvitedShow");
|
||||||
model = this.get("model");
|
|
||||||
|
|
||||||
|
const model = this.get("inviteModel");
|
||||||
model.setProperties({ saving: true, error: false });
|
model.setProperties({ saving: true, error: false });
|
||||||
|
|
||||||
const onerror = e => {
|
const onerror = e => {
|
||||||
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
|
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
|
||||||
self.set("errorMessage", e.jqXHR.responseJSON.errors[0]);
|
this.set("errorMessage", e.jqXHR.responseJSON.errors[0]);
|
||||||
} else {
|
} else {
|
||||||
self.set(
|
this.set(
|
||||||
"errorMessage",
|
"errorMessage",
|
||||||
self.get("isMessage")
|
this.get("isPM")
|
||||||
? I18n.t("topic.invite_private.error")
|
? I18n.t("topic.invite_private.error")
|
||||||
: I18n.t("topic.invite_reply.error")
|
: I18n.t("topic.invite_reply.error")
|
||||||
);
|
);
|
||||||
|
@ -304,18 +324,18 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.get("hasGroups")) {
|
if (this.get("hasGroups")) {
|
||||||
return this.get("model")
|
return this.get("inviteModel")
|
||||||
.createGroupInvite(this.get("emailOrUsername").trim())
|
.createGroupInvite(this.get("emailOrUsername").trim())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
model.setProperties({ saving: false, finished: true });
|
model.setProperties({ saving: false, finished: true });
|
||||||
this.get("model.details.allowed_groups").pushObject(
|
this.get("inviteModel.details.allowed_groups").pushObject(
|
||||||
Ember.Object.create(data.group)
|
Ember.Object.create(data.group)
|
||||||
);
|
);
|
||||||
this.appEvents.trigger("post-stream:refresh");
|
this.appEvents.trigger("post-stream:refresh");
|
||||||
})
|
})
|
||||||
.catch(onerror);
|
.catch(onerror);
|
||||||
} else {
|
} else {
|
||||||
return this.get("model")
|
return this.get("inviteModel")
|
||||||
.createInvite(
|
.createInvite(
|
||||||
this.get("emailOrUsername").trim(),
|
this.get("emailOrUsername").trim(),
|
||||||
groupNames,
|
groupNames,
|
||||||
|
@ -323,19 +343,18 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
)
|
)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
model.setProperties({ saving: false, finished: true });
|
model.setProperties({ saving: false, finished: true });
|
||||||
if (!this.get("invitingToTopic")) {
|
if (!this.get("invitingToTopic") && userInvitedController) {
|
||||||
Invite.findInvitedBy(
|
Invite.findInvitedBy(
|
||||||
this.currentUser,
|
this.currentUser,
|
||||||
userInvitedController.get("filter")
|
userInvitedController.get("filter")
|
||||||
).then(invite_model => {
|
).then(inviteModel => {
|
||||||
userInvitedController.set("model", invite_model);
|
userInvitedController.setProperties({
|
||||||
userInvitedController.set(
|
model: inviteModel,
|
||||||
"totalInvites",
|
totalInvites: inviteModel.invites.length
|
||||||
invite_model.invites.length
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
} else if (this.get("isMessage") && result && result.user) {
|
});
|
||||||
this.get("model.details.allowed_users").pushObject(
|
} else if (this.get("isPM") && result && result.user) {
|
||||||
|
this.get("inviteModel.details.allowed_users").pushObject(
|
||||||
Ember.Object.create(result.user)
|
Ember.Object.create(result.user)
|
||||||
);
|
);
|
||||||
this.appEvents.trigger("post-stream:refresh");
|
this.appEvents.trigger("post-stream:refresh");
|
||||||
|
@ -353,24 +372,21 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
},
|
},
|
||||||
|
|
||||||
generateInvitelink() {
|
generateInvitelink() {
|
||||||
const self = this;
|
|
||||||
|
|
||||||
if (this.get("disabled")) {
|
if (this.get("disabled")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupNames = this.get("model.groupNames"),
|
const groupNames = this.get("inviteModel.groupNames");
|
||||||
userInvitedController = this.get("userInvitedShow"),
|
const userInvitedController = this.get("userInvitedShow");
|
||||||
model = this.get("model");
|
const model = this.get("inviteModel");
|
||||||
|
|
||||||
var topicId = null;
|
|
||||||
if (this.get("invitingToTopic")) {
|
|
||||||
topicId = this.get("model.id");
|
|
||||||
}
|
|
||||||
|
|
||||||
model.setProperties({ saving: true, error: false });
|
model.setProperties({ saving: true, error: false });
|
||||||
|
|
||||||
return this.get("model")
|
let topicId;
|
||||||
|
if (this.get("invitingToTopic")) {
|
||||||
|
topicId = this.get("inviteModel.id");
|
||||||
|
}
|
||||||
|
|
||||||
|
return model
|
||||||
.generateInviteLink(
|
.generateInviteLink(
|
||||||
this.get("emailOrUsername").trim(),
|
this.get("emailOrUsername").trim(),
|
||||||
groupNames,
|
groupNames,
|
||||||
|
@ -382,24 +398,26 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
finished: true,
|
finished: true,
|
||||||
inviteLink: result
|
inviteLink: result
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (userInvitedController) {
|
||||||
Invite.findInvitedBy(
|
Invite.findInvitedBy(
|
||||||
this.currentUser,
|
this.currentUser,
|
||||||
userInvitedController.get("filter")
|
userInvitedController.get("filter")
|
||||||
).then(invite_model => {
|
).then(inviteModel => {
|
||||||
userInvitedController.set("model", invite_model);
|
userInvitedController.setProperties({
|
||||||
userInvitedController.set(
|
model: inviteModel,
|
||||||
"totalInvites",
|
totalInvites: inviteModel.invites.length
|
||||||
invite_model.invites.length
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(function(e) {
|
.catch(e => {
|
||||||
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
|
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
|
||||||
self.set("errorMessage", e.jqXHR.responseJSON.errors[0]);
|
this.set("errorMessage", e.jqXHR.responseJSON.errors[0]);
|
||||||
} else {
|
} else {
|
||||||
self.set(
|
this.set(
|
||||||
"errorMessage",
|
"errorMessage",
|
||||||
self.get("isMessage")
|
this.get("isPM")
|
||||||
? I18n.t("topic.invite_private.error")
|
? I18n.t("topic.invite_private.error")
|
||||||
: I18n.t("topic.invite_reply.error")
|
: I18n.t("topic.invite_reply.error")
|
||||||
);
|
);
|
||||||
|
@ -411,7 +429,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
showCustomMessageBox() {
|
showCustomMessageBox() {
|
||||||
this.toggleProperty("hasCustomMessage");
|
this.toggleProperty("hasCustomMessage");
|
||||||
if (this.get("hasCustomMessage")) {
|
if (this.get("hasCustomMessage")) {
|
||||||
if (this.get("model") === this.currentUser) {
|
if (this.get("inviteModel") === this.currentUser) {
|
||||||
this.set(
|
this.set(
|
||||||
"customMessage",
|
"customMessage",
|
||||||
I18n.t("invite.custom_message_template_forum")
|
I18n.t("invite.custom_message_template_forum")
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { fmt } from "discourse/lib/computed";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
panel: null,
|
||||||
|
|
||||||
|
panelComponent: fmt("panel.id", "%@-panel"),
|
||||||
|
|
||||||
|
classNameBindings: ["panel.id"],
|
||||||
|
|
||||||
|
classNames: ["modal-panel"]
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { propertyEqual } from "discourse/lib/computed";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
tagName: "li",
|
||||||
|
classNames: ["modal-tab"],
|
||||||
|
panel: null,
|
||||||
|
selectedPanel: null,
|
||||||
|
panelsLength: null,
|
||||||
|
classNameBindings: ["isActive", "singleTab", "panel.id"],
|
||||||
|
singleTab: Ember.computed.equal("panelsLength", 1),
|
||||||
|
title: Ember.computed.alias("panel.title"),
|
||||||
|
isActive: propertyEqual("panel.id", "selectedPanel.id"),
|
||||||
|
|
||||||
|
click() {
|
||||||
|
this.onSelectPanel(this.get("panel"));
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,13 +0,0 @@
|
||||||
import Button from "discourse/components/d-button";
|
|
||||||
|
|
||||||
export default Button.extend({
|
|
||||||
classNames: ["btn-default", "share"],
|
|
||||||
icon: "link",
|
|
||||||
title: "topic.share.help",
|
|
||||||
label: "topic.share.title",
|
|
||||||
attributeBindings: ["url:data-share-url"],
|
|
||||||
|
|
||||||
click() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
|
import { longDateNoYear } from "discourse/lib/formatter";
|
||||||
|
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||||
|
import Sharing from "discourse/lib/sharing";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
tagName: null,
|
||||||
|
|
||||||
|
date: Ember.computed.alias("panel.model.date"),
|
||||||
|
type: Ember.computed.alias("panel.model.type"),
|
||||||
|
postNumber: Ember.computed.alias("panel.model.postNumber"),
|
||||||
|
postId: Ember.computed.alias("panel.model.postId"),
|
||||||
|
topic: Ember.computed.alias("panel.model.topic"),
|
||||||
|
|
||||||
|
@computed
|
||||||
|
sources() {
|
||||||
|
return Sharing.activeSources(this.siteSettings.share_links);
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("date")
|
||||||
|
postDate(date) {
|
||||||
|
return date ? longDateNoYear(new Date(date)) : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("type", "postNumber", "postDate", "topic.title")
|
||||||
|
shareTitle(type, postNumber, postDate, topicTitle) {
|
||||||
|
topicTitle = escapeExpression(topicTitle);
|
||||||
|
|
||||||
|
if (type === "topic") {
|
||||||
|
return I18n.t("share.topic", { topicTitle });
|
||||||
|
}
|
||||||
|
if (postNumber) {
|
||||||
|
return I18n.t("share.post", { postNumber, postDate });
|
||||||
|
}
|
||||||
|
return I18n.t("share.topic", { topicTitle });
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("topic.shareUrl")
|
||||||
|
shareUrl(shareUrl) {
|
||||||
|
if (Ember.isEmpty(shareUrl)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relative urls
|
||||||
|
if (shareUrl.indexOf("/") === 0) {
|
||||||
|
const location = window.location;
|
||||||
|
shareUrl = `${location.protocol}//${location.host}${shareUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeURI(shareUrl);
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
const shareUrl = this.get("shareUrl");
|
||||||
|
const $linkInput = this.$(".topic-share-url");
|
||||||
|
const $linkForTouch = this.$(".topic-share-url-for-touch a");
|
||||||
|
|
||||||
|
Ember.run.schedule("afterRender", () => {
|
||||||
|
if (!this.capabilities.touch) {
|
||||||
|
$linkForTouch.parent().remove();
|
||||||
|
|
||||||
|
$linkInput
|
||||||
|
.val(shareUrl)
|
||||||
|
.select()
|
||||||
|
.focus();
|
||||||
|
} else {
|
||||||
|
$linkInput.remove();
|
||||||
|
|
||||||
|
$linkForTouch.attr("href", shareUrl).text(shareUrl);
|
||||||
|
|
||||||
|
const range = window.document.createRange();
|
||||||
|
range.selectNode($linkForTouch[0]);
|
||||||
|
window.getSelection().addRange(range);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
share(source) {
|
||||||
|
const url = source.generateUrl(
|
||||||
|
this.get("shareUrl"),
|
||||||
|
this.get("topic.title")
|
||||||
|
);
|
||||||
|
const options = {
|
||||||
|
menubar: "no",
|
||||||
|
toolbar: "no",
|
||||||
|
resizable: "yes",
|
||||||
|
scrollbars: "yes",
|
||||||
|
width: 600,
|
||||||
|
height: source.popupHeight || 315
|
||||||
|
};
|
||||||
|
const stringOptions = Object.keys(options)
|
||||||
|
.map(k => `${k}=${options[k]}`)
|
||||||
|
.join(",");
|
||||||
|
|
||||||
|
if (source.shouldOpenInPopup) {
|
||||||
|
window.open(url, "", stringOptions);
|
||||||
|
} else {
|
||||||
|
window.open(url, "_blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,195 +0,0 @@
|
||||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
|
||||||
import { longDateNoYear } from "discourse/lib/formatter";
|
|
||||||
import computed from "ember-addons/ember-computed-decorators";
|
|
||||||
import Sharing from "discourse/lib/sharing";
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
elementId: "share-link",
|
|
||||||
classNameBindings: ["visible"],
|
|
||||||
link: null,
|
|
||||||
visible: null,
|
|
||||||
|
|
||||||
@computed
|
|
||||||
sources() {
|
|
||||||
return Sharing.activeSources(this.siteSettings.share_links);
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed("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");
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed("date")
|
|
||||||
displayDate(date) {
|
|
||||||
return longDateNoYear(new Date(date));
|
|
||||||
},
|
|
||||||
|
|
||||||
_focusUrl() {
|
|
||||||
const link = this.get("link");
|
|
||||||
if (!this.capabilities.touch) {
|
|
||||||
const $linkInput = $("#share-link input");
|
|
||||||
$linkInput.val(link);
|
|
||||||
|
|
||||||
// Wait for the fade-in transition to finish before selecting the link:
|
|
||||||
window.setTimeout(() => $linkInput.select().focus(), 160);
|
|
||||||
} else {
|
|
||||||
const $linkForTouch = $("#share-link .share-for-touch a");
|
|
||||||
$linkForTouch.attr("href", link);
|
|
||||||
$linkForTouch.text(link);
|
|
||||||
const range = window.document.createRange();
|
|
||||||
range.selectNode($linkForTouch[0]);
|
|
||||||
window.getSelection().addRange(range);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_showUrl($target, url) {
|
|
||||||
const $currentTargetOffset = $target.offset();
|
|
||||||
const $this = this.$();
|
|
||||||
|
|
||||||
if (Ember.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.css({ top: "" + y + "px" });
|
|
||||||
|
|
||||||
if (!this.site.mobileView) {
|
|
||||||
$this.css({ left: "" + x + "px" });
|
|
||||||
}
|
|
||||||
this.set("link", encodeURI(url));
|
|
||||||
this.set("visible", true);
|
|
||||||
|
|
||||||
Ember.run.scheduleOnce("afterRender", this, this._focusUrl);
|
|
||||||
},
|
|
||||||
|
|
||||||
_webShare(url) {
|
|
||||||
// We can pass title and text too, but most share targets do their own oneboxing
|
|
||||||
return navigator.share({ url });
|
|
||||||
},
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
const $html = $("html");
|
|
||||||
$html.on("mousedown.outside-share-link", e => {
|
|
||||||
// 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.$().has(e.target).length !== 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.send("close");
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
$html.on(
|
|
||||||
"click.discourse-share-link",
|
|
||||||
"button[data-share-url], .post-info .post-date[data-share-url]",
|
|
||||||
e => {
|
|
||||||
// if they want to open in a new tab, let it so
|
|
||||||
if (wantsNewWindow(e)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const $currentTarget = $(e.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
|
|
||||||
// navigator.share needs HTTPS, returns undefined on HTTP
|
|
||||||
if (navigator.share && !$currentTarget.hasClass("post-date")) {
|
|
||||||
this._webShare(url).catch(() => {
|
|
||||||
// if navigator fails for unexpected reason fallback to popup
|
|
||||||
this._showUrl($currentTarget, url);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this._showUrl($currentTarget, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$html.on("keydown.share-view", e => {
|
|
||||||
if (e.keyCode === 27) {
|
|
||||||
this.send("close");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.appEvents.on("share:url", (url, $target) =>
|
|
||||||
this._showUrl($target, url)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
$("html")
|
|
||||||
.off("click.discourse-share-link")
|
|
||||||
.off("mousedown.outside-share-link")
|
|
||||||
.off("keydown.share-view");
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
replyAsNewTopic() {
|
|
||||||
const postStream = this.get("topic.postStream");
|
|
||||||
const postId =
|
|
||||||
this.get("postId") || postStream.findPostIdForPostNumber(1);
|
|
||||||
const post = postStream.findLoadedPost(postId);
|
|
||||||
this.get("replyAsNewTopic")(post);
|
|
||||||
this.send("close");
|
|
||||||
},
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.setProperties({
|
|
||||||
link: null,
|
|
||||||
postNumber: null,
|
|
||||||
postId: null,
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
share(source) {
|
|
||||||
const url = source.generateUrl(this.get("link"), this.get("topic.title"));
|
|
||||||
if (source.shouldOpenInPopup) {
|
|
||||||
window.open(
|
|
||||||
url,
|
|
||||||
"",
|
|
||||||
"menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=600,height=" +
|
|
||||||
(source.popupHeight || 315)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
window.open(url, "_blank");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -34,7 +34,8 @@ export default TextField.extend({
|
||||||
single = bool("single"),
|
single = bool("single"),
|
||||||
allowAny = bool("allowAny"),
|
allowAny = bool("allowAny"),
|
||||||
disabled = bool("disabled"),
|
disabled = bool("disabled"),
|
||||||
disallowEmails = bool("disallowEmails");
|
disallowEmails = bool("disallowEmails"),
|
||||||
|
fullWidthWrap = bool("fullWidthWrap");
|
||||||
|
|
||||||
function excludedUsernames() {
|
function excludedUsernames() {
|
||||||
// hack works around some issues with allowAny eventing
|
// hack works around some issues with allowAny eventing
|
||||||
|
@ -54,6 +55,7 @@ export default TextField.extend({
|
||||||
single: single,
|
single: single,
|
||||||
allowAny: allowAny,
|
allowAny: allowAny,
|
||||||
updateData: opts && opts.updateData ? opts.updateData : false,
|
updateData: opts && opts.updateData ? opts.updateData : false,
|
||||||
|
fullWidthWrap,
|
||||||
|
|
||||||
dataSource(term) {
|
dataSource(term) {
|
||||||
var results = userSearch({
|
var results = userSearch({
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import showModal from "discourse/lib/show-modal";
|
||||||
|
import { share } from "discourse/lib/pwa-utils";
|
||||||
import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button";
|
import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -5,25 +7,100 @@ export default {
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
registerTopicFooterButton({
|
registerTopicFooterButton({
|
||||||
id: "share",
|
id: "native-share",
|
||||||
icon: "link",
|
icon: "link",
|
||||||
priority: 999,
|
priority: 999,
|
||||||
label: "topic.share.title",
|
label: "topic.share.title",
|
||||||
title: "topic.share.help",
|
title: "topic.share.help",
|
||||||
action() {
|
action() {
|
||||||
this.appEvents.trigger(
|
share({ url: this.get("topic.shareUrl") }).catch(() =>
|
||||||
"share:url",
|
showModal("share-and-invite", {
|
||||||
this.get("topic.shareUrl"),
|
modalClass: "share-and-invite",
|
||||||
$("#topic-footer-buttons")
|
panels: [
|
||||||
|
{
|
||||||
|
id: "share",
|
||||||
|
title: "topic.share.title",
|
||||||
|
model: { topic: this.get("topic") }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
dropdown: true,
|
||||||
|
classNames: ["native-share"],
|
||||||
|
dependentKeys: ["topic.shareUrl", "topic.isPrivateMessage"],
|
||||||
|
displayed() {
|
||||||
|
return window.navigator.share;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
registerTopicFooterButton({
|
||||||
|
id: "share-and-invite",
|
||||||
|
icon: "link",
|
||||||
|
priority: 999,
|
||||||
|
label: "topic.share.title",
|
||||||
|
title: "topic.share.help",
|
||||||
|
action() {
|
||||||
|
const modal = () => {
|
||||||
|
const panels = [
|
||||||
|
{
|
||||||
|
id: "share",
|
||||||
|
title: "topic.share.extended_title",
|
||||||
|
model: {
|
||||||
|
topic: this.get("topic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this.get("canInviteTo") && !this.get("inviteDisabled")) {
|
||||||
|
let invitePanelTitle;
|
||||||
|
|
||||||
|
if (this.get("isPM")) {
|
||||||
|
invitePanelTitle = "topic.invite_private.title";
|
||||||
|
} else if (this.get("invitingToTopic")) {
|
||||||
|
invitePanelTitle = "topic.invite_reply.title";
|
||||||
|
} else {
|
||||||
|
invitePanelTitle = "user.invited.create";
|
||||||
|
}
|
||||||
|
|
||||||
|
panels.push({
|
||||||
|
id: "invite",
|
||||||
|
title: invitePanelTitle,
|
||||||
|
model: {
|
||||||
|
inviteModel: this.get("topic")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showModal("share-and-invite", {
|
||||||
|
model: this.get("topic"),
|
||||||
|
modalClass: "share-and-invite",
|
||||||
|
panels
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (window.navigator.share) {
|
||||||
|
window.navigator
|
||||||
|
.share({ url: this.get("topic.shareUrl") })
|
||||||
|
.catch(() => modal());
|
||||||
|
} else {
|
||||||
|
modal();
|
||||||
|
}
|
||||||
|
},
|
||||||
dropdown() {
|
dropdown() {
|
||||||
return this.site.mobileView;
|
return this.site.mobileView;
|
||||||
},
|
},
|
||||||
classNames: ["share"],
|
classNames: ["share-and-invite"],
|
||||||
dependentKeys: ["topic.shareUrl", "topic.isPrivateMessage"],
|
dependentKeys: [
|
||||||
|
"topic.shareUrl",
|
||||||
|
"topic.isPrivateMessage",
|
||||||
|
"canInviteTo",
|
||||||
|
"inviteDisabled",
|
||||||
|
"isPM",
|
||||||
|
"invitingToTopic"
|
||||||
|
],
|
||||||
displayed() {
|
displayed() {
|
||||||
return !this.get("topic.isPrivateMessage");
|
return !(this.site.mobileView && window.navigator.share);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -47,26 +124,6 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
registerTopicFooterButton({
|
|
||||||
id: "invite",
|
|
||||||
icon: "users",
|
|
||||||
priority: 997,
|
|
||||||
label: "topic.invite_reply.title",
|
|
||||||
title: "topic.invite_reply.help",
|
|
||||||
action: "showInvite",
|
|
||||||
dropdown() {
|
|
||||||
return this.site.mobileView;
|
|
||||||
},
|
|
||||||
classNames: ["invite-topic"],
|
|
||||||
dependentKeys: ["canInviteTo", "inviteDisabled"],
|
|
||||||
displayed() {
|
|
||||||
return this.get("canInviteTo");
|
|
||||||
},
|
|
||||||
disabled() {
|
|
||||||
return this.get("inviteDisabled");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
registerTopicFooterButton({
|
registerTopicFooterButton({
|
||||||
dependentKeys: ["topic.bookmarked", "topic.isPrivateMessage"],
|
dependentKeys: ["topic.bookmarked", "topic.isPrivateMessage"],
|
||||||
id: "bookmark",
|
id: "bookmark",
|
||||||
|
|
|
@ -201,8 +201,11 @@ export default function(options) {
|
||||||
wrap = this.wrap(
|
wrap = this.wrap(
|
||||||
"<div class='ac-wrap clearfix" + (disabled ? " disabled" : "") + "'/>"
|
"<div class='ac-wrap clearfix" + (disabled ? " disabled" : "") + "'/>"
|
||||||
).parent();
|
).parent();
|
||||||
|
|
||||||
|
if (!options.fullWidthWrap) {
|
||||||
wrap.width(width);
|
wrap.width(width);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.single && !options.width) {
|
if (options.single && !options.width) {
|
||||||
this.css("width", "100%");
|
this.css("width", "100%");
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export function share(data) {
|
||||||
|
return new Ember.RSVP.Promise((resolve, reject) => {
|
||||||
|
if (window.location.protocol === "https:" && window.navigator.share) {
|
||||||
|
window.navigator
|
||||||
|
.share(data)
|
||||||
|
.catch(reject)
|
||||||
|
.then(resolve);
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -64,6 +64,24 @@ export default function(name, opts) {
|
||||||
modalController.set("title", I18n.t(opts.title));
|
modalController.set("title", I18n.t(opts.title));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.panels) {
|
||||||
|
modalController.setProperties({
|
||||||
|
panels: opts.panels,
|
||||||
|
selectedPanel: opts.panels[0]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (controller.actions.onSelectPanel) {
|
||||||
|
modalController.set("onSelectPanel", controller.actions.onSelectPanel);
|
||||||
|
}
|
||||||
|
|
||||||
|
modalController.set(
|
||||||
|
"modalClass",
|
||||||
|
`${modalController.get("modalClass")} has-tabs`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
modalController.setProperties({ panels: [], selectedPanel: null });
|
||||||
|
}
|
||||||
|
|
||||||
controller.set("modal", modalController);
|
controller.set("modal", modalController);
|
||||||
const model = opts.model;
|
const model = opts.model;
|
||||||
if (model) {
|
if (model) {
|
||||||
|
|
|
@ -16,6 +16,11 @@ export default Ember.Mixin.create({
|
||||||
actions: {
|
actions: {
|
||||||
closeModal() {
|
closeModal() {
|
||||||
this.get("modal").send("closeModal");
|
this.get("modal").send("closeModal");
|
||||||
|
this.set("panels", []);
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelectPanel(panel) {
|
||||||
|
this.set("selectedPanel", panel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -92,11 +92,6 @@ const TopicRoute = Discourse.Route.extend({
|
||||||
this.controllerFor("feature_topic").reset();
|
this.controllerFor("feature_topic").reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
showInvite() {
|
|
||||||
showModal("invite", { model: this.modelFor("topic") });
|
|
||||||
this.controllerFor("invite").reset();
|
|
||||||
},
|
|
||||||
|
|
||||||
showHistory(model, revision) {
|
showHistory(model, revision) {
|
||||||
showModal("history", { model });
|
showModal("history", { model });
|
||||||
const historyController = this.controllerFor("history");
|
const historyController = this.controllerFor("history");
|
||||||
|
|
|
@ -30,8 +30,19 @@ export default Discourse.Route.extend({
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
showInvite() {
|
showInvite() {
|
||||||
showModal("invite", { model: this.currentUser });
|
showModal("share-and-invite", {
|
||||||
this.controllerFor("invite").reset();
|
modalClass: "share-and-invite",
|
||||||
|
panels: [
|
||||||
|
{
|
||||||
|
id: "invite",
|
||||||
|
title: "user.invited.create",
|
||||||
|
model: {
|
||||||
|
inviteModel: this.currentUser,
|
||||||
|
userInvitedShow: this.controllerFor("user-invited-show")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,17 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if panels}}
|
||||||
|
<ul class="modal-tabs">
|
||||||
|
{{#each panels as |panel|}}
|
||||||
|
{{modal-tab
|
||||||
|
panel=panel
|
||||||
|
panelsLength=panels.length
|
||||||
|
selectedPanel=selectedPanel
|
||||||
|
onSelectPanel=onSelectPanel}}
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{else}}
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h3>{{title}}</h3>
|
<h3>{{title}}</h3>
|
||||||
|
|
||||||
|
@ -17,6 +28,7 @@
|
||||||
<p>{{subtitle}}</p>
|
<p>{{subtitle}}</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id='modal-alert'></div>
|
<div id='modal-alert'></div>
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
{{#if inviteModel.error}}
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<button class="close" data-dismiss="alert">×</button>
|
||||||
|
<div class="error-message">
|
||||||
|
{{{errorMessage}}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
{{#if inviteModel.finished}}
|
||||||
|
{{#if inviteModel.inviteLink}}
|
||||||
|
{{generated-invite-link link=inviteModel.inviteLink email=emailOrUsername}}
|
||||||
|
{{else}}
|
||||||
|
<div class="success-message">
|
||||||
|
{{{successMessage}}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
<div class="invite-user-control">
|
||||||
|
<label class="instructions">{{inviteInstructions}}</label>
|
||||||
|
{{#if allowExistingMembers}}
|
||||||
|
{{user-selector
|
||||||
|
fullWidthWrap=true
|
||||||
|
single=true
|
||||||
|
allowAny=true
|
||||||
|
excludeCurrentUser=true
|
||||||
|
includeMessageableGroups=isMessage
|
||||||
|
hasGroups=hasGroups
|
||||||
|
usernames=emailOrUsername
|
||||||
|
placeholderKey=placeholderKey
|
||||||
|
class="invite-user-input"
|
||||||
|
autocomplete="off"}}
|
||||||
|
{{else}}
|
||||||
|
{{text-field
|
||||||
|
class="email-or-username-input"
|
||||||
|
value=emailOrUsername
|
||||||
|
placeholderKey="topic.invite_reply.email_placeholder"}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if showGroups}}
|
||||||
|
<div class="group-access-control">
|
||||||
|
<label class="instructions {{showGroupsClass}}">
|
||||||
|
{{i18n "topic.automatically_add_to_groups"}}
|
||||||
|
</label>
|
||||||
|
{{group-selector
|
||||||
|
fullWidthWrap=true
|
||||||
|
groupFinder=groupFinder
|
||||||
|
groupNames=inviteModel.groupNames
|
||||||
|
placeholderKey="topic.invite_private.group_name"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if showCustomMessage}}
|
||||||
|
<div class="show-custom-message-control">
|
||||||
|
<label class="instructions">
|
||||||
|
{{discourse-linked-text
|
||||||
|
class="optional"
|
||||||
|
action=(action "showCustomMessageBox")
|
||||||
|
text="invite.custom_message"}}
|
||||||
|
</label>
|
||||||
|
{{#if hasCustomMessage}}
|
||||||
|
{{textarea value=customMessage placeholder=customMessagePlaceholder}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
{{#if inviteModel.finished}}
|
||||||
|
{{d-button
|
||||||
|
class="btn-primary"
|
||||||
|
action=(route-action "closeModal")
|
||||||
|
label="close"}}
|
||||||
|
{{else}}
|
||||||
|
{{d-button
|
||||||
|
icon=inviteIcon
|
||||||
|
action=(action "createInvite")
|
||||||
|
class="btn-primary send-invite"
|
||||||
|
disabled=disabled
|
||||||
|
label=buttonTitle}}
|
||||||
|
{{#if showCopyInviteButton}}
|
||||||
|
{{d-button
|
||||||
|
icon="link"
|
||||||
|
action=(action "generateInvitelink")
|
||||||
|
class="btn-primary generate-invite-link"
|
||||||
|
disabled=disabledCopyLink
|
||||||
|
label="user.invited.generate_link"}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
|
@ -0,0 +1 @@
|
||||||
|
{{component panelComponent panel=panel close=(route-action "closeModal")}}
|
|
@ -0,0 +1 @@
|
||||||
|
{{i18n title}}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<div class="header">
|
||||||
|
<h3 class="title">{{{shareTitle}}}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
{{input value=shareUrl class="topic-share-url"}}
|
||||||
|
<div class="topic-share-url-for-touch"><a></a></div>
|
||||||
|
|
||||||
|
<div class="sources">
|
||||||
|
{{#each sources as |source|}}
|
||||||
|
{{share-source source=source title=topic.title action=(action "share")}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,28 +0,0 @@
|
||||||
<h3>{{shareTitle}}</h3>
|
|
||||||
|
|
||||||
{{#if date}}
|
|
||||||
<span class="date">{{displayDate}}</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input type='text'>
|
|
||||||
<div class="share-for-touch"><div class="overflow-ellipsis"><a></a></div></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#each sources as |s|}}
|
|
||||||
{{share-source source=s title=model.title action=(action "share")}}
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#if topic.details.can_reply_as_new_topic}}
|
|
||||||
<div class='reply-as-new-topic'>
|
|
||||||
{{#if topic.isPrivateMessage}}
|
|
||||||
<a href {{action "replyAsNewTopic"}} aria-label={{i18n 'post.reply_as_new_private_message'}} title={{i18n 'post.reply_as_new_private_message'}}>{{d-icon "plus"}}{{i18n 'user.new_private_message'}}</a>
|
|
||||||
{{else}}
|
|
||||||
<a href {{action "replyAsNewTopic"}} aria-label={{i18n 'post.reply_as_new_topic'}} title={{i18n 'post.reply_as_new_topic'}}>{{d-icon "plus"}}{{i18n 'topic.create'}}</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class='link'>
|
|
||||||
<a href {{action "close"}} class="close-share" aria-label={{i18n 'share.close'}} title={{i18n 'share.close'}}>{{d-icon "times"}}</a>
|
|
||||||
</div>
|
|
|
@ -2,10 +2,11 @@
|
||||||
modalClass=modalClass
|
modalClass=modalClass
|
||||||
title=title
|
title=title
|
||||||
subtitle=subtitle
|
subtitle=subtitle
|
||||||
|
panels=panels
|
||||||
|
selectedPanel=selectedPanel
|
||||||
|
onSelectPanel=onSelectPanel
|
||||||
class="hidden"
|
class="hidden"
|
||||||
errors=errors
|
errors=errors
|
||||||
closeModal=(route-action "closeModal")}}
|
closeModal=(route-action "closeModal")}}
|
||||||
|
|
||||||
{{outlet "modalBody"}}
|
{{outlet "modalBody"}}
|
||||||
|
|
||||||
{{/d-modal}}
|
{{/d-modal}}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
{{#d-modal-body id="invite-modal" title=title}}
|
|
||||||
{{#if model.error}}
|
|
||||||
<div class="alert alert-error">
|
|
||||||
<button class="close" data-dismiss="alert">×</button>
|
|
||||||
{{{errorMessage}}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{#if model.finished}}
|
|
||||||
{{#if model.inviteLink}}
|
|
||||||
{{generated-invite-link link=model.inviteLink email=emailOrUsername}}
|
|
||||||
{{else}}
|
|
||||||
{{{successMessage}}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<label>{{inviteInstructions}}</label>
|
|
||||||
{{#if allowExistingMembers}}
|
|
||||||
{{user-selector
|
|
||||||
single=true
|
|
||||||
allowAny=true
|
|
||||||
excludeCurrentUser=true
|
|
||||||
includeMessageableGroups=isMessage
|
|
||||||
hasGroups=hasGroups
|
|
||||||
usernames=emailOrUsername
|
|
||||||
placeholderKey=placeholderKey
|
|
||||||
autocomplete="discourse"}}
|
|
||||||
{{else}}
|
|
||||||
{{text-field value=emailOrUsername placeholderKey="topic.invite_reply.email_placeholder"}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if showGroups}}
|
|
||||||
<label><span class={{showGroupsClass}}>{{i18n 'topic.automatically_add_to_groups'}}</span></label>
|
|
||||||
{{group-selector groupFinder=groupFinder groupNames=model.groupNames placeholderKey="topic.invite_private.group_name"}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if showCustomMessage}}
|
|
||||||
<label>{{discourse-linked-text class="optional" action=(action "showCustomMessageBox") text="invite.custom_message"}}</label>
|
|
||||||
{{#if hasCustomMessage}}{{textarea value=customMessage placeholder=customMessagePlaceholder}}{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{/if}}
|
|
||||||
{{/d-modal-body}}
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
{{#if model.finished}}
|
|
||||||
{{d-button class="btn-primary" action=(route-action "closeModal") label="close"}}
|
|
||||||
{{else}}
|
|
||||||
{{d-button icon=inviteIcon action=(action "createInvite") class="btn-primary" disabled=disabled label=buttonTitle}}
|
|
||||||
{{#if showCopyInviteButton}}
|
|
||||||
{{d-button icon="link" action=(action "generateInvitelink") class="btn-primary" disabled=disabledCopyLink label='user.invited.generate_link'}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{{#d-modal-body}}
|
||||||
|
{{modal-panel panel=modal.selectedPanel}}
|
||||||
|
{{/d-modal-body}}
|
|
@ -192,7 +192,6 @@
|
||||||
toggleSummary=(action "toggleSummary")
|
toggleSummary=(action "toggleSummary")
|
||||||
removeAllowedUser=(action "removeAllowedUser")
|
removeAllowedUser=(action "removeAllowedUser")
|
||||||
removeAllowedGroup=(action "removeAllowedGroup")
|
removeAllowedGroup=(action "removeAllowedGroup")
|
||||||
showInvite=(route-action "showInvite")
|
|
||||||
topVisibleChanged=(action "topVisibleChanged")
|
topVisibleChanged=(action "topVisibleChanged")
|
||||||
currentPostChanged=(action "currentPostChanged")
|
currentPostChanged=(action "currentPostChanged")
|
||||||
currentPostScrolled=(action "currentPostScrolled")
|
currentPostScrolled=(action "currentPostScrolled")
|
||||||
|
@ -264,11 +263,9 @@
|
||||||
convertToPrivateMessage=(action "convertToPrivateMessage")
|
convertToPrivateMessage=(action "convertToPrivateMessage")
|
||||||
toggleBookmark=(action "toggleBookmark")
|
toggleBookmark=(action "toggleBookmark")
|
||||||
showFlagTopic=(route-action "showFlagTopic")
|
showFlagTopic=(route-action "showFlagTopic")
|
||||||
showInvite=(route-action "showInvite")
|
|
||||||
toggleArchiveMessage=(action "toggleArchiveMessage")
|
toggleArchiveMessage=(action "toggleArchiveMessage")
|
||||||
editFirstPost=(action "editFirstPost")
|
editFirstPost=(action "editFirstPost")
|
||||||
replyToPost=(action "replyToPost")
|
replyToPost=(action "replyToPost")}}
|
||||||
}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div id="topic-footer-buttons">
|
<div id="topic-footer-buttons">
|
||||||
{{d-button icon="reply" class="btn-primary pull-right" action=(route-action "showLogin") label="topic.reply.title"}}
|
{{d-button icon="reply" class="btn-primary pull-right" action=(route-action "showLogin") label="topic.reply.title"}}
|
||||||
|
@ -320,8 +317,6 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{share-popup topic=model replyAsNewTopic=(action "replyAsNewTopic")}}
|
|
||||||
|
|
||||||
{{#if embedQuoteButton}}
|
{{#if embedQuoteButton}}
|
||||||
{{quote-button quoteState=quoteState selectText=(action "selectText")}}
|
{{quote-button quoteState=quoteState selectText=(action "selectText")}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import PostCooked from "discourse/widgets/post-cooked";
|
||||||
import DecoratorHelper from "discourse/widgets/decorator-helper";
|
import DecoratorHelper from "discourse/widgets/decorator-helper";
|
||||||
import { createWidget, applyDecorators } from "discourse/widgets/widget";
|
import { createWidget, applyDecorators } from "discourse/widgets/widget";
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
import { iconNode } from "discourse-common/lib/icon-library";
|
||||||
|
import { share } from "discourse/lib/pwa-utils";
|
||||||
import { transformBasicPost } from "discourse/lib/transform-post";
|
import { transformBasicPost } from "discourse/lib/transform-post";
|
||||||
import { postTransformCallbacks } from "discourse/widgets/post-stream";
|
import { postTransformCallbacks } from "discourse/widgets/post-stream";
|
||||||
import { h } from "virtual-dom";
|
import { h } from "virtual-dom";
|
||||||
|
@ -13,6 +14,7 @@ import {
|
||||||
formatUsername
|
formatUsername
|
||||||
} from "discourse/lib/utilities";
|
} from "discourse/lib/utilities";
|
||||||
import hbs from "discourse/widgets/hbs-compiler";
|
import hbs from "discourse/widgets/hbs-compiler";
|
||||||
|
import showModal from "discourse/lib/show-modal";
|
||||||
|
|
||||||
function transformWithCallbacks(post) {
|
function transformWithCallbacks(post) {
|
||||||
let transformed = transformBasicPost(post);
|
let transformed = transformBasicPost(post);
|
||||||
|
@ -219,6 +221,71 @@ function showReplyTab(attrs, siteSettings) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createWidget("post-date", {
|
||||||
|
tagName: "div.post-info.post-date",
|
||||||
|
|
||||||
|
buildClasses(attrs) {
|
||||||
|
let classes = "post-date";
|
||||||
|
|
||||||
|
const lastWikiEdit =
|
||||||
|
attrs.wiki && attrs.lastWikiEdit && new Date(attrs.lastWikiEdit);
|
||||||
|
|
||||||
|
if (lastWikiEdit) {
|
||||||
|
classes = `${classes} last-wiki-edit`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
},
|
||||||
|
|
||||||
|
html(attrs) {
|
||||||
|
return h(
|
||||||
|
"a",
|
||||||
|
{
|
||||||
|
attributes: {
|
||||||
|
class: "post-date",
|
||||||
|
"data-share-url": attrs.shareUrl,
|
||||||
|
"data-post-number": attrs.post_number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dateNode(this._date(attrs))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_date(attrs) {
|
||||||
|
const lastWikiEdit =
|
||||||
|
attrs.wiki && attrs.lastWikiEdit && new Date(attrs.lastWikiEdit);
|
||||||
|
const createdAt = new Date(attrs.created_at);
|
||||||
|
return lastWikiEdit ? lastWikiEdit : createdAt;
|
||||||
|
},
|
||||||
|
|
||||||
|
click() {
|
||||||
|
const post = this.findAncestorModel();
|
||||||
|
|
||||||
|
const modalFallback = () => {
|
||||||
|
showModal("share-and-invite", {
|
||||||
|
modalClass: "share-and-invite",
|
||||||
|
panels: [
|
||||||
|
{
|
||||||
|
id: "share",
|
||||||
|
title: "topic.share.extended_title",
|
||||||
|
model: {
|
||||||
|
postNumber: this.attrs.post_number,
|
||||||
|
shareUrl: this.attrs.shareUrl,
|
||||||
|
date: this._date(this.attrs),
|
||||||
|
postId: post.get("id"),
|
||||||
|
topic: post.get("topic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// use native webshare when available
|
||||||
|
// navigator.share needs HTTPS, returns undefined on HTTP
|
||||||
|
share({ url: this.attrs.shareUrl }).catch(modalFallback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
createWidget("post-meta-data", {
|
createWidget("post-meta-data", {
|
||||||
tagName: "div.topic-meta-data",
|
tagName: "div.topic-meta-data",
|
||||||
|
|
||||||
|
@ -241,21 +308,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) {
|
if (attrs.via_email) {
|
||||||
postInfo.push(this.attach("post-email-indicator", attrs));
|
postInfo.push(this.attach("post-email-indicator", attrs));
|
||||||
}
|
}
|
||||||
|
@ -276,7 +328,7 @@ createWidget("post-meta-data", {
|
||||||
postInfo.push(this.attach("reply-to-tab", attrs));
|
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(
|
postInfo.push(
|
||||||
h(
|
h(
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
@import "common/foundation/base";
|
@import "common/foundation/base";
|
||||||
@import "common/foundation/mixins";
|
@import "common/foundation/mixins";
|
||||||
@import "common/foundation/variables";
|
@import "common/foundation/variables";
|
||||||
|
@import "common/foundation/spacing";
|
||||||
@import "common/select-kit/admin-agree-flag-dropdown";
|
@import "common/select-kit/admin-agree-flag-dropdown";
|
||||||
@import "common/select-kit/admin-delete-flag-dropdown";
|
@import "common/select-kit/admin-delete-flag-dropdown";
|
||||||
@import "common/select-kit/categories-admin-dropdown";
|
@import "common/select-kit/categories-admin-dropdown";
|
||||||
|
|
|
@ -27,9 +27,9 @@
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
border-bottom: 1px solid $primary-low;
|
border-bottom: 1px solid $primary-low;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
h3 {
|
h3 {
|
||||||
|
@ -42,6 +42,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-close {
|
.modal-close {
|
||||||
|
align-self: flex-start;
|
||||||
order: 2;
|
order: 2;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
@ -104,6 +105,7 @@
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
box-shadow: shadow("modal");
|
box-shadow: shadow("modal");
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
@media screen and (min-width: 475px) {
|
@media screen and (min-width: 475px) {
|
||||||
min-width: 475px;
|
min-width: 475px;
|
||||||
|
@ -617,7 +619,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal:not(.has-tabs) {
|
||||||
.modal-tab {
|
.modal-tab {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
&.has-tabs {
|
||||||
|
.modal-tabs {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
flex: 1 0 auto;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.modal-tab {
|
||||||
|
list-style: none;
|
||||||
|
padding: s(1 2);
|
||||||
|
margin-right: s(1);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: $secondary;
|
||||||
|
background: $danger;
|
||||||
|
|
||||||
|
&.single-tab {
|
||||||
|
color: $primary;
|
||||||
|
background: none;
|
||||||
|
padding: s(1 0);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: $font-up-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
// styles that apply to the "share" popup when sharing a link to a post or topic
|
|
||||||
|
|
||||||
#share-link {
|
|
||||||
position: absolute;
|
|
||||||
left: 20px;
|
|
||||||
z-index: z("dropdown");
|
|
||||||
box-shadow: shadow("card");
|
|
||||||
background-color: $secondary;
|
|
||||||
padding: 6px 10px 10px 10px;
|
|
||||||
width: 300px;
|
|
||||||
display: none;
|
|
||||||
&.visible {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
input[type="text"] {
|
|
||||||
width: 96%;
|
|
||||||
}
|
|
||||||
.share-for-touch .overflow-ellipsis {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
.share-for-touch {
|
|
||||||
margin: 14px 0;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: $font-0;
|
|
||||||
}
|
|
||||||
.copy-text {
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
margin: 5px 5px 5px 15px;
|
|
||||||
color: $success;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 0.25s;
|
|
||||||
font-size: $font-0;
|
|
||||||
&:not(.success) {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.social-link {
|
|
||||||
margin-left: 2px;
|
|
||||||
margin-right: 8px;
|
|
||||||
float: left;
|
|
||||||
font-size: $font-up-4;
|
|
||||||
}
|
|
||||||
.reply-as-new-topic {
|
|
||||||
float: left;
|
|
||||||
line-height: $line-height-large;
|
|
||||||
margin-left: 8px;
|
|
||||||
margin-top: 0.5em;
|
|
||||||
.fa {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.link {
|
|
||||||
margin-right: 2px;
|
|
||||||
float: right;
|
|
||||||
font-size: $font-up-3;
|
|
||||||
a {
|
|
||||||
color: dark-light-choose($primary-medium, $secondary-medium);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 5px 0;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date {
|
|
||||||
float: right;
|
|
||||||
margin: 5px;
|
|
||||||
color: dark-light-choose($primary-medium, $secondary-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"] {
|
|
||||||
font-size: $font-up-1;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.discourse-no-touch #share-link .share-for-touch {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.discourse-touch #share-link input[type="text"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
.share-and-invite.modal {
|
||||||
|
.modal-body {
|
||||||
|
max-width: 475px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-and-invite.modal .share.modal-panel {
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: $font-0;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-bottom: s(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.topic-share-url {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-share-url-for-touch {
|
||||||
|
width: 290px;
|
||||||
|
|
||||||
|
@extend .overflow-ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-share-url-for-touch,
|
||||||
|
.topic-share-url {
|
||||||
|
margin-bottom: s(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sources {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.social-link {
|
||||||
|
font-size: $font-up-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-and-invite.modal .invite.modal-panel {
|
||||||
|
.error-message,
|
||||||
|
.success-message {
|
||||||
|
margin-bottom: s(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
.invite-user-control,
|
||||||
|
.group-access-control,
|
||||||
|
.show-custom-message-control {
|
||||||
|
margin-bottom: s(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions {
|
||||||
|
margin-bottom: s(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-or-username-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
margin-right: s(2);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
$base-space: 4px;
|
||||||
|
|
||||||
|
@function s(
|
||||||
|
$sizes: (
|
||||||
|
0
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$spaces: ();
|
||||||
|
@each $size in $sizes {
|
||||||
|
@if ($size == 0) {
|
||||||
|
// strip units from 0 values
|
||||||
|
@return $size / ($size * 0 + 1);
|
||||||
|
}
|
||||||
|
$spaces: append($spaces, ($size * $base-space));
|
||||||
|
}
|
||||||
|
@return $spaces;
|
||||||
|
}
|
|
@ -587,11 +587,6 @@ video {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#share-link {
|
|
||||||
width: 365px;
|
|
||||||
margin-left: -4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-select {
|
.post-select {
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
font-size: $font-up-4;
|
font-size: $font-up-4;
|
||||||
padding: 10px 15px 5px 5px;
|
|
||||||
color: $primary;
|
color: $primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -350,12 +350,6 @@ iframe {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#share-link {
|
|
||||||
width: 290px;
|
|
||||||
left: auto;
|
|
||||||
right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-posts {
|
.selected-posts {
|
||||||
padding: 0.1em 0.7em;
|
padding: 0.1em 0.7em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,12 +135,12 @@ en:
|
||||||
next_month: "Next Month"
|
next_month: "Next Month"
|
||||||
placeholder: date
|
placeholder: date
|
||||||
share:
|
share:
|
||||||
topic: "share a link to this topic"
|
topic: "<b>Topic</b>: %{topicTitle}"
|
||||||
post: "post #%{postNumber}"
|
post: "<b>Post #%{postNumber}</b>, %{postDate}"
|
||||||
close: "close"
|
close: "close"
|
||||||
twitter: "share this link on Twitter"
|
twitter: "Share this link on Twitter"
|
||||||
facebook: "share this link on Facebook"
|
facebook: "Share this link on Facebook"
|
||||||
email: "send this link in an email"
|
email: "Send this link in an email"
|
||||||
|
|
||||||
action_codes:
|
action_codes:
|
||||||
public_topic: "made this topic public %{when}"
|
public_topic: "made this topic public %{when}"
|
||||||
|
@ -1948,6 +1948,7 @@ en:
|
||||||
|
|
||||||
share:
|
share:
|
||||||
title: "Share"
|
title: "Share"
|
||||||
|
extended_title: "Share a link"
|
||||||
help: "share a link to this topic"
|
help: "share a link to this topic"
|
||||||
|
|
||||||
print:
|
print:
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
|
acceptance("Share and Invite modal - desktop", {
|
||||||
|
loggedIn: true
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("Topic footer button", async assert => {
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists("#topic-footer-button-share-and-invite"),
|
||||||
|
"the button exists"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click("#topic-footer-button-share-and-invite");
|
||||||
|
|
||||||
|
assert.ok(exists(".share-and-invite.modal"), "it shows the modal");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.share"),
|
||||||
|
"it shows the share tab"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.share.is-active"),
|
||||||
|
"it activates the share tab by default"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.invite"),
|
||||||
|
"it shows the invite tab"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
find(".share-and-invite.modal .modal-panel.share .title").text(),
|
||||||
|
"Topic: Internationalization / localization",
|
||||||
|
"it shows the topic title"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
find(".share-and-invite.modal .modal-panel.share .topic-share-url")
|
||||||
|
.val()
|
||||||
|
.includes("/t/internationalization-localization/280?u=eviltrout"),
|
||||||
|
"it shows the topic sharing url"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
find(".share-and-invite.modal .social-link").length,
|
||||||
|
3,
|
||||||
|
"it shows social sources"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(".share-and-invite.modal .modal-tab.invite");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-panel.invite .send-invite:disabled"),
|
||||||
|
"send invite button is disabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(
|
||||||
|
".share-and-invite.modal .modal-panel.invite .generate-invite-link:disabled"
|
||||||
|
),
|
||||||
|
"generate invite button is disabled"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("Post date link", async assert => {
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
await click("#post_2 .post-info.post-date a");
|
||||||
|
|
||||||
|
assert.ok(exists(".share-and-invite.modal"), "it shows the modal");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.share"),
|
||||||
|
"it shows the share tab"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.share.single-tab"),
|
||||||
|
"it shows only one tab"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
!exists(".share-and-invite.modal .modal-tab.invite"),
|
||||||
|
"it doesn’t show the invite tab"
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
|
acceptance("Share and Invite modal - mobile", {
|
||||||
|
loggedIn: true,
|
||||||
|
mobileView: true
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("Topic footer mobile button", async assert => {
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
!exists("#topic-footer-button-share-and-invite"),
|
||||||
|
"the button doesn’t exist"
|
||||||
|
);
|
||||||
|
|
||||||
|
const subject = selectKit(".topic-footer-mobile-dropdown");
|
||||||
|
await subject.expand();
|
||||||
|
await subject.selectRowByValue("share-and-invite");
|
||||||
|
|
||||||
|
assert.ok(exists(".share-and-invite.modal"), "it shows the modal");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.share"),
|
||||||
|
"it shows the share tab"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.share.is-active"),
|
||||||
|
"it activates the share tab by default"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
!exists(".share-and-invite.modal .modal-tab.invite"),
|
||||||
|
"it doesn’t show the invite tab"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
find(".share-and-invite.modal .modal-panel.share .title").text(),
|
||||||
|
"Topic: Internationalization / localization",
|
||||||
|
"it shows the topic title"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
find(".share-and-invite.modal .modal-panel.share .topic-share-url")
|
||||||
|
.val()
|
||||||
|
.includes("/t/internationalization-localization/280?u=eviltrout"),
|
||||||
|
"it shows the topic sharing url"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
find(".share-and-invite.modal .social-link").length,
|
||||||
|
3,
|
||||||
|
"it shows social sources"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("Post date link", async assert => {
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
await click("#post_2 .post-info.post-date a");
|
||||||
|
|
||||||
|
assert.ok(exists(".share-and-invite.modal"), "it shows the modal");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.share"),
|
||||||
|
"it shows the share tab"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".share-and-invite.modal .modal-tab.share.single-tab"),
|
||||||
|
"it shows only one tab"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
!exists(".share-and-invite.modal .modal-tab.invite"),
|
||||||
|
"it doesn’t show the invite tab"
|
||||||
|
);
|
||||||
|
});
|
|
@ -22,29 +22,6 @@ acceptance("Topic", {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("Share Popup", async assert => {
|
|
||||||
await visit("/t/internationalization-localization/280");
|
|
||||||
assert.ok(!exists("#share-link.visible"), "it is not visible");
|
|
||||||
|
|
||||||
await click("button[data-share-url]");
|
|
||||||
assert.ok(exists("#share-link.visible"), "it shows the popup");
|
|
||||||
|
|
||||||
await click("#share-link .close-share");
|
|
||||||
assert.ok(!exists("#share-link.visible"), "it closes the popup");
|
|
||||||
|
|
||||||
// TODO tgxworld This fails on Travis but we need to push the security fix out
|
|
||||||
// first.
|
|
||||||
// click('#topic-footer-buttons .btn.create');
|
|
||||||
// fillIn('.d-editor-input', '<h2><div data-share-url="something">Click</button><h2>');
|
|
||||||
//
|
|
||||||
// click('#reply-control .btn.create');
|
|
||||||
// click('h2 div[data-share-url]');
|
|
||||||
//
|
|
||||||
// andThen(() => {
|
|
||||||
// ok(!exists('#share-link.visible'), 'it does not show the popup');
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
|
|
||||||
QUnit.test("Showing and hiding the edit controls", async assert => {
|
QUnit.test("Showing and hiding the edit controls", async assert => {
|
||||||
await visit("/t/internationalization-localization/280");
|
await visit("/t/internationalization-localization/280");
|
||||||
|
|
||||||
|
@ -98,69 +75,6 @@ QUnit.test("Marking a topic as wiki", async assert => {
|
||||||
assert.ok(find("a.wiki").length === 1, "it shows the wiki icon");
|
assert.ok(find("a.wiki").length === 1, "it shows the wiki icon");
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("Reply as new topic", async assert => {
|
|
||||||
await visit("/t/internationalization-localization/280");
|
|
||||||
await click("button.share:eq(0)");
|
|
||||||
await click(".reply-as-new-topic a");
|
|
||||||
|
|
||||||
assert.ok(exists(".d-editor-input"), "the composer input is visible");
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
find(".d-editor-input")
|
|
||||||
.val()
|
|
||||||
.trim(),
|
|
||||||
`Continuing the discussion from [Internationalization / localization](${
|
|
||||||
window.location.origin
|
|
||||||
}/t/internationalization-localization/280):`,
|
|
||||||
"it fills composer with the ring string"
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
selectKit(".category-chooser")
|
|
||||||
.header()
|
|
||||||
.value(),
|
|
||||||
"2",
|
|
||||||
"it fills category selector with the right category"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
QUnit.test("Reply as new message", async assert => {
|
|
||||||
await visit("/t/pm-for-testing/12");
|
|
||||||
await click("button.share:eq(0)");
|
|
||||||
await click(".reply-as-new-topic a");
|
|
||||||
|
|
||||||
assert.ok(exists(".d-editor-input"), "the composer input is visible");
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
find(".d-editor-input")
|
|
||||||
.val()
|
|
||||||
.trim(),
|
|
||||||
`Continuing the discussion from [PM for testing](${
|
|
||||||
window.location.origin
|
|
||||||
}/t/pm-for-testing/12):`,
|
|
||||||
"it fills composer with the ring string"
|
|
||||||
);
|
|
||||||
|
|
||||||
const targets = find(".item span", ".composer-fields");
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
$(targets[0]).text(),
|
|
||||||
"someguy",
|
|
||||||
"it fills up the composer with the right user to start the PM to"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
$(targets[1]).text(),
|
|
||||||
"test",
|
|
||||||
"it fills up the composer with the right user to start the PM to"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
$(targets[2]).text(),
|
|
||||||
"Group",
|
|
||||||
"it fills up the composer with the right group to start the PM to"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
QUnit.test("Visit topic routes", async assert => {
|
QUnit.test("Visit topic routes", async assert => {
|
||||||
await visit("/t/12");
|
await visit("/t/12");
|
||||||
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
import componentTest from "helpers/component-test";
|
|
||||||
moduleForComponent("share-button", { integration: true });
|
|
||||||
|
|
||||||
componentTest("share button", {
|
|
||||||
template: '{{share-button url="https://eviltrout.com"}}',
|
|
||||||
|
|
||||||
test(assert) {
|
|
||||||
assert.ok(this.$(`button.share`).length, "it has all the classes");
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
this.$(`button[data-share-url="https://eviltrout.com"]`).length,
|
|
||||||
"it has the data attribute for sharing"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -2136,6 +2136,7 @@ export default {
|
||||||
pinned: false,
|
pinned: false,
|
||||||
pinned_at: null,
|
pinned_at: null,
|
||||||
details: {
|
details: {
|
||||||
|
can_invite_via_email: true,
|
||||||
auto_close_at: null,
|
auto_close_at: null,
|
||||||
auto_close_hours: null,
|
auto_close_hours: null,
|
||||||
auto_close_based_on_last_post: false,
|
auto_close_based_on_last_post: false,
|
||||||
|
|
Loading…
Reference in New Issue