Ghost/ghost/admin/app/components/modal-members-modal-settings.js

272 lines
9.8 KiB
JavaScript
Raw Normal View History

import $ from 'jquery';
import ModalComponent from 'ghost-admin/components/modal-base';
import boundOneWay from '../utils/bound-one-way';
import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard';
import {alias, reads} from '@ember/object/computed';
import {computed} from '@ember/object';
import {htmlSafe} from '@ember/string';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
const ICON_EXTENSIONS = ['gif', 'jpg', 'jpeg', 'png', 'svg'];
const ICON_MAPPING = [
{
icon: 'portal-icon-1',
value: 'icon-1'
},
{
icon: 'portal-icon-2',
value: 'icon-2'
},
{
icon: 'portal-icon-3',
value: 'icon-3'
},
{
icon: 'portal-icon-4',
value: 'icon-4'
},
{
icon: 'portal-icon-5',
value: 'icon-5'
}
];
export default ModalComponent.extend({
settings: service(),
membersUtils: service(),
config: service(),
page: 'signup',
iconExtensions: null,
defaultButtonIcons: null,
isShowModalLink: true,
customIcon: null,
confirm() {},
signupButtonText: boundOneWay('settings.portalButtonSignupText'),
buttonIcon: boundOneWay('settings.portalButtonIcon'),
allowSelfSignup: alias('settings.membersAllowFreeSignup'),
isStripeConfigured: reads('membersUtils.isStripeEnabled'),
backgroundStyle: computed('settings.accentColor', function () {
let color = this.get('settings.accentColor') || '#ffffff';
return htmlSafe(`background-color: ${color}`);
}),
accentColor: computed('settings.accentColor', function () {
let color = this.get('settings.accentColor');
if (color && color[0] === '#') {
return color.slice(1);
}
return color;
}),
showModalLinkOrAttribute: computed('isShowModalLink', function () {
if (this.isShowModalLink) {
return `${this.config.get('blogUrl')}/#/portal`;
}
return `data-portal`;
}),
portalPreviewUrl: computed('selectedButtonStyle', 'buttonIcon', 'signupButtonText', 'page', 'isFreeChecked', 'isMonthlyChecked', 'isYearlyChecked', 'settings.{portalName,portalButton,accentColor}', function () {
const baseUrl = this.config.get('blogUrl');
const portalBase = '/#/portal/preview';
const settingsParam = new URLSearchParams();
settingsParam.append('button', this.settings.get('portalButton'));
settingsParam.append('name', this.settings.get('portalName'));
settingsParam.append('isFree', this.isFreeChecked);
settingsParam.append('isMonthly', this.isMonthlyChecked);
settingsParam.append('isYearly', this.isYearlyChecked);
settingsParam.append('page', this.page);
if (this.buttonIcon) {
settingsParam.append('buttonIcon', encodeURIComponent(this.buttonIcon));
}
settingsParam.append('signupButtonText', encodeURIComponent(this.signupButtonText));
if (this.settings.get('accentColor')) {
settingsParam.append('accentColor', encodeURIComponent(`${this.settings.get('accentColor')}`));
}
if (this.selectedButtonStyle) {
settingsParam.append('buttonStyle', encodeURIComponent(this.selectedButtonStyle.name));
}
return `${baseUrl}${portalBase}?${settingsParam.toString()}`;
}),
showIconSetting: computed('selectedButtonStyle', function () {
const selectedButtonStyle = this.get('selectedButtonStyle.name') || '';
return selectedButtonStyle.includes('icon');
}),
showButtonTextSetting: computed('selectedButtonStyle', function () {
const selectedButtonStyle = this.get('selectedButtonStyle.name') || '';
return selectedButtonStyle.includes('text');
}),
isFreeChecked: computed('settings.portalPlans.[]', 'allowSelfSignup', function () {
const allowedPlans = this.settings.get('portalPlans') || [];
return (this.allowSelfSignup && allowedPlans.includes('free'));
}),
isMonthlyChecked: computed('settings.portalPlans.[]', 'isStripeConfigured', function () {
const allowedPlans = this.settings.get('portalPlans') || [];
return (this.isStripeConfigured && allowedPlans.includes('monthly'));
}),
isYearlyChecked: computed('settings.portalPlans.[]', 'isStripeConfigured', function () {
const allowedPlans = this.settings.get('portalPlans') || [];
return (this.isStripeConfigured && allowedPlans.includes('yearly'));
}),
selectedButtonStyle: computed('settings.portalButtonStyle', function () {
return this.buttonStyleOptions.find((buttonStyle) => {
return (buttonStyle.name === this.settings.get('portalButtonStyle'));
});
}),
init() {
this._super(...arguments);
this.buttonStyleOptions = [
{name: 'icon-and-text', label: 'Icon and text'},
{name: 'icon-only', label: 'Icon only'},
{name: 'text-only', label: 'Text only'}
];
this.defaultButtonIcons = ICON_MAPPING;
this.iconExtensions = ICON_EXTENSIONS;
const portalButtonIcon = this.settings.get('portalButtonIcon') || '';
const defaultIconKeys = this.defaultButtonIcons.map(buttonIcon => buttonIcon.value);
if (portalButtonIcon && !defaultIconKeys.includes(portalButtonIcon)) {
this.set('customIcon', this.settings.get('portalButtonIcon'));
}
},
actions: {
toggleFreePlan(isChecked) {
this.updateAllowedPlan('free', isChecked);
},
toggleMonthlyPlan(isChecked) {
this.updateAllowedPlan('monthly', isChecked);
},
toggleYearlyPlan(isChecked) {
this.updateAllowedPlan('yearly', isChecked);
},
togglePortalButton(showButton) {
this.settings.set('portalButton', showButton);
},
togglePortalName(showSignupName) {
this.settings.set('portalName', showSignupName);
},
confirm() {
return this.saveTask.perform();
},
isPlanSelected(plan) {
const allowedPlans = this.settings.get('portalPlans');
return allowedPlans.includes(plan);
},
switchPreviewPage(page) {
this.set('page', page);
},
validateAccentColor() {
let newColor = this.get('accentColor');
let oldColor = this.get('settings.accentColor');
let errMessage = '';
// reset errors and validation
this.get('settings.errors').remove('accentColor');
this.get('settings.hasValidated').removeObject('accentColor');
if (newColor === '') {
// Clear out the accent color
this.set('settings.accentColor', '');
return;
}
// accentColor will be null unless the user has input something
if (!newColor) {
newColor = oldColor;
}
if (newColor[0] !== '#') {
newColor = `#${newColor}`;
}
if (newColor.match(/#[0-9A-Fa-f]{6}$/)) {
this.set('settings.accentColor', '');
run.schedule('afterRender', this, function () {
this.set('settings.accentColor', newColor);
});
} else {
errMessage = 'The color should be in valid hex format';
this.get('settings.errors').add('accentColor', errMessage);
this.get('settings.hasValidated').pushObject('accentColor');
return;
}
},
setButtonStyle(buttonStyle) {
this.set('selectedButtonStyle', buttonStyle);
},
setSignupButtonText(event) {
this.set('signupButtonText', event.target.value);
},
/**
* Fired after an image upload completes
* @param {string} property - Property name to be set on `this.settings`
* @param {UploadResult[]} results - Array of UploadResult objects
* @return {string} The URL that was set on `this.settings.property`
*/
imageUploaded(property, results) {
if (results[0]) {
this.set('customIcon', results[0].url);
this.set('buttonIcon', results[0].url);
}
},
/**
* Opens a file selection dialog - Triggered by "Upload Image" buttons,
* searches for the hidden file input within the .gh-setting element
* containing the clicked button then simulates a click
* @param {MouseEvent} event - MouseEvent fired by the button click
*/
triggerFileDialog(event) {
// simulate click to open file dialog
// using jQuery because IE11 doesn't support MouseEvent
$(event.target)
.closest('.gh-setting-action')
.find('input[type="file"]')
.click();
},
selectDefaultIcon(icon) {
this.set('buttonIcon', icon);
}
},
updateAllowedPlan(plan, isChecked) {
const allowedPlans = this.settings.get('portalPlans') || [];
if (!isChecked) {
this.settings.set('portalPlans', allowedPlans.filter(p => p !== plan));
} else {
allowedPlans.push(plan);
this.settings.set('portalPlans', [...allowedPlans]);
}
},
copyLinkOrAttribute: task(function* () {
copyTextToClipboard(this.showModalLinkOrAttribute);
yield timeout(this.isTesting ? 50 : 3000);
}),
saveTask: task(function* () {
this.settings.set('portalButtonStyle', this.selectedButtonStyle.name);
this.settings.set('portalButtonSignupText', this.signupButtonText);
this.settings.set('portalButtonIcon', this.buttonIcon);
yield this.settings.save();
this.closeModal();
}).drop()
});