mirror of
https://github.com/flarum/core.git
synced 2025-07-12 12:26:23 +02:00
Run prettier for all JS files
This commit is contained in:
@ -12,9 +12,9 @@ export default class AdminApplication extends Application {
|
||||
canGoBack: () => true,
|
||||
getPrevious: () => {},
|
||||
backUrl: () => this.forum.attribute('baseUrl'),
|
||||
back: function() {
|
||||
back: function () {
|
||||
window.location = this.backUrl();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
constructor() {
|
||||
@ -27,7 +27,7 @@ export default class AdminApplication extends Application {
|
||||
* @inheritdoc
|
||||
*/
|
||||
mount() {
|
||||
m.mount(document.getElementById('app-navigation'), Navigation.component({className: 'App-backControl', drawer: true}));
|
||||
m.mount(document.getElementById('app-navigation'), Navigation.component({ className: 'App-backControl', drawer: true }));
|
||||
m.mount(document.getElementById('header-navigation'), Navigation.component());
|
||||
m.mount(document.getElementById('header-primary'), HeaderPrimary.component());
|
||||
m.mount(document.getElementById('header-secondary'), HeaderSecondary.component());
|
||||
@ -59,5 +59,5 @@ export default class AdminApplication extends Application {
|
||||
}
|
||||
|
||||
return required;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,6 @@ export default Object.assign(compat, {
|
||||
'components/AdminNav': AdminNav,
|
||||
'components/EditCustomCssModal': EditCustomCssModal,
|
||||
'components/EditGroupModal': EditGroupModal,
|
||||
'routes': routes,
|
||||
'AdminApplication': AdminApplication
|
||||
routes: routes,
|
||||
AdminApplication: AdminApplication,
|
||||
});
|
||||
|
@ -22,8 +22,10 @@ export default class AddExtensionModal extends Modal {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<p>{app.translator.trans('core.admin.add_extension.temporary_text')}</p>
|
||||
<p>{app.translator.trans('core.admin.add_extension.install_text', {a: <a href="https://discuss.flarum.org/t/extensions" target="_blank"/>})}</p>
|
||||
<p>{app.translator.trans('core.admin.add_extension.developer_text', {a: <a href="http://flarum.org/docs/extend" target="_blank"/>})}</p>
|
||||
<p>
|
||||
{app.translator.trans('core.admin.add_extension.install_text', { a: <a href="https://discuss.flarum.org/t/extensions" target="_blank" /> })}
|
||||
</p>
|
||||
<p>{app.translator.trans('core.admin.add_extension.developer_text', { a: <a href="http://flarum.org/docs/extend" target="_blank" /> })}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -13,11 +13,7 @@ export default class AdminLinkButton extends LinkButton {
|
||||
getButtonContent() {
|
||||
const content = super.getButtonContent();
|
||||
|
||||
content.push(
|
||||
<div className="AdminLinkButton-description">
|
||||
{this.props.description}
|
||||
</div>
|
||||
);
|
||||
content.push(<div className="AdminLinkButton-description">{this.props.description}</div>);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
@ -15,9 +15,7 @@ import ItemList from '../../common/utils/ItemList';
|
||||
export default class AdminNav extends Component {
|
||||
view() {
|
||||
return (
|
||||
<SelectDropdown
|
||||
className="AdminNav App-titleControl"
|
||||
buttonClassName="Button">
|
||||
<SelectDropdown className="AdminNav App-titleControl" buttonClassName="Button">
|
||||
{this.items().toArray()}
|
||||
</SelectDropdown>
|
||||
);
|
||||
@ -31,47 +29,65 @@ export default class AdminNav extends Component {
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('dashboard', AdminLinkButton.component({
|
||||
items.add(
|
||||
'dashboard',
|
||||
AdminLinkButton.component({
|
||||
href: app.route('dashboard'),
|
||||
icon: 'far fa-chart-bar',
|
||||
children: app.translator.trans('core.admin.nav.dashboard_button'),
|
||||
description: app.translator.trans('core.admin.nav.dashboard_text')
|
||||
}));
|
||||
description: app.translator.trans('core.admin.nav.dashboard_text'),
|
||||
})
|
||||
);
|
||||
|
||||
items.add('basics', AdminLinkButton.component({
|
||||
items.add(
|
||||
'basics',
|
||||
AdminLinkButton.component({
|
||||
href: app.route('basics'),
|
||||
icon: 'fas fa-pencil-alt',
|
||||
children: app.translator.trans('core.admin.nav.basics_button'),
|
||||
description: app.translator.trans('core.admin.nav.basics_text')
|
||||
}));
|
||||
description: app.translator.trans('core.admin.nav.basics_text'),
|
||||
})
|
||||
);
|
||||
|
||||
items.add('mail', AdminLinkButton.component({
|
||||
items.add(
|
||||
'mail',
|
||||
AdminLinkButton.component({
|
||||
href: app.route('mail'),
|
||||
icon: 'fas fa-envelope',
|
||||
children: app.translator.trans('core.admin.nav.email_button'),
|
||||
description: app.translator.trans('core.admin.nav.email_text')
|
||||
}));
|
||||
description: app.translator.trans('core.admin.nav.email_text'),
|
||||
})
|
||||
);
|
||||
|
||||
items.add('permissions', AdminLinkButton.component({
|
||||
items.add(
|
||||
'permissions',
|
||||
AdminLinkButton.component({
|
||||
href: app.route('permissions'),
|
||||
icon: 'fas fa-key',
|
||||
children: app.translator.trans('core.admin.nav.permissions_button'),
|
||||
description: app.translator.trans('core.admin.nav.permissions_text')
|
||||
}));
|
||||
description: app.translator.trans('core.admin.nav.permissions_text'),
|
||||
})
|
||||
);
|
||||
|
||||
items.add('appearance', AdminLinkButton.component({
|
||||
items.add(
|
||||
'appearance',
|
||||
AdminLinkButton.component({
|
||||
href: app.route('appearance'),
|
||||
icon: 'fas fa-paint-brush',
|
||||
children: app.translator.trans('core.admin.nav.appearance_button'),
|
||||
description: app.translator.trans('core.admin.nav.appearance_text')
|
||||
}));
|
||||
description: app.translator.trans('core.admin.nav.appearance_text'),
|
||||
})
|
||||
);
|
||||
|
||||
items.add('extensions', AdminLinkButton.component({
|
||||
items.add(
|
||||
'extensions',
|
||||
AdminLinkButton.component({
|
||||
href: app.route('extensions'),
|
||||
icon: 'fas fa-puzzle-piece',
|
||||
children: app.translator.trans('core.admin.nav.extensions_button'),
|
||||
description: app.translator.trans('core.admin.nav.extensions_text')
|
||||
}));
|
||||
description: app.translator.trans('core.admin.nav.extensions_text'),
|
||||
})
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
@ -24,85 +24,85 @@ export default class AppearancePage extends Page {
|
||||
<form onsubmit={this.onsubmit.bind(this)}>
|
||||
<fieldset className="AppearancePage-colors">
|
||||
<legend>{app.translator.trans('core.admin.appearance.colors_heading')}</legend>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.appearance.colors_text')}
|
||||
</div>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.colors_text')}</div>
|
||||
|
||||
<div className="AppearancePage-colors-input">
|
||||
<input className="FormControl" type="text" placeholder="#aaaaaa" value={this.primaryColor()} onchange={m.withAttr('value', this.primaryColor)}/>
|
||||
<input className="FormControl" type="text" placeholder="#aaaaaa" value={this.secondaryColor()} onchange={m.withAttr('value', this.secondaryColor)}/>
|
||||
<input
|
||||
className="FormControl"
|
||||
type="text"
|
||||
placeholder="#aaaaaa"
|
||||
value={this.primaryColor()}
|
||||
onchange={m.withAttr('value', this.primaryColor)}
|
||||
/>
|
||||
<input
|
||||
className="FormControl"
|
||||
type="text"
|
||||
placeholder="#aaaaaa"
|
||||
value={this.secondaryColor()}
|
||||
onchange={m.withAttr('value', this.secondaryColor)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{Switch.component({
|
||||
state: this.darkMode(),
|
||||
children: app.translator.trans('core.admin.appearance.dark_mode_label'),
|
||||
onchange: this.darkMode
|
||||
onchange: this.darkMode,
|
||||
})}
|
||||
|
||||
{Switch.component({
|
||||
state: this.coloredHeader(),
|
||||
children: app.translator.trans('core.admin.appearance.colored_header_label'),
|
||||
onchange: this.coloredHeader
|
||||
onchange: this.coloredHeader,
|
||||
})}
|
||||
|
||||
{Button.component({
|
||||
className: 'Button Button--primary',
|
||||
type: 'submit',
|
||||
children: app.translator.trans('core.admin.appearance.submit_button'),
|
||||
loading: this.loading
|
||||
loading: this.loading,
|
||||
})}
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<fieldset>
|
||||
<legend>{app.translator.trans('core.admin.appearance.logo_heading')}</legend>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.appearance.logo_text')}
|
||||
</div>
|
||||
<UploadImageButton name="logo"/>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.logo_text')}</div>
|
||||
<UploadImageButton name="logo" />
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{app.translator.trans('core.admin.appearance.favicon_heading')}</legend>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.appearance.favicon_text')}
|
||||
</div>
|
||||
<UploadImageButton name="favicon"/>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.favicon_text')}</div>
|
||||
<UploadImageButton name="favicon" />
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{app.translator.trans('core.admin.appearance.custom_header_heading')}</legend>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.appearance.custom_header_text')}
|
||||
</div>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.custom_header_text')}</div>
|
||||
{Button.component({
|
||||
className: 'Button',
|
||||
children: app.translator.trans('core.admin.appearance.edit_header_button'),
|
||||
onclick: () => app.modal.show(new EditCustomHeaderModal())
|
||||
onclick: () => app.modal.show(new EditCustomHeaderModal()),
|
||||
})}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{app.translator.trans('core.admin.appearance.custom_footer_heading')}</legend>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.appearance.custom_footer_text')}
|
||||
</div>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.custom_footer_text')}</div>
|
||||
{Button.component({
|
||||
className: 'Button',
|
||||
children: app.translator.trans('core.admin.appearance.edit_footer_button'),
|
||||
onclick: () => app.modal.show(new EditCustomFooterModal())
|
||||
onclick: () => app.modal.show(new EditCustomFooterModal()),
|
||||
})}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>{app.translator.trans('core.admin.appearance.custom_styles_heading')}</legend>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.appearance.custom_styles_text')}
|
||||
</div>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.custom_styles_text')}</div>
|
||||
{Button.component({
|
||||
className: 'Button',
|
||||
children: app.translator.trans('core.admin.appearance.edit_css_button'),
|
||||
onclick: () => app.modal.show(new EditCustomCssModal())
|
||||
onclick: () => app.modal.show(new EditCustomCssModal()),
|
||||
})}
|
||||
</fieldset>
|
||||
</div>
|
||||
@ -126,7 +126,7 @@ export default class AppearancePage extends Page {
|
||||
theme_primary_color: this.primaryColor(),
|
||||
theme_secondary_color: this.secondaryColor(),
|
||||
theme_dark_mode: this.darkMode(),
|
||||
theme_colored_header: this.coloredHeader()
|
||||
theme_colored_header: this.coloredHeader(),
|
||||
}).then(() => window.location.reload());
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ export default class BasicsPage extends Page {
|
||||
'show_language_selector',
|
||||
'default_route',
|
||||
'welcome_title',
|
||||
'welcome_message'
|
||||
'welcome_message',
|
||||
];
|
||||
this.values = {};
|
||||
|
||||
const settings = app.data.settings;
|
||||
this.fields.forEach(key => this.values[key] = m.prop(settings[key]));
|
||||
this.fields.forEach((key) => (this.values[key] = m.prop(settings[key])));
|
||||
|
||||
this.localeOptions = {};
|
||||
const locales = app.data.locales;
|
||||
@ -33,7 +33,7 @@ export default class BasicsPage extends Page {
|
||||
this.localeOptions[i] = `${locales[i]} (${i})`;
|
||||
}
|
||||
|
||||
if (typeof this.values.show_language_selector() !== "number") this.values.show_language_selector(1);
|
||||
if (typeof this.values.show_language_selector() !== 'number') this.values.show_language_selector(1);
|
||||
}
|
||||
|
||||
view() {
|
||||
@ -43,19 +43,19 @@ export default class BasicsPage extends Page {
|
||||
<form onsubmit={this.onsubmit.bind(this)}>
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.basics.forum_title_heading'),
|
||||
children: [
|
||||
<input className="FormControl" value={this.values.forum_title()} oninput={m.withAttr('value', this.values.forum_title)}/>
|
||||
]
|
||||
children: [<input className="FormControl" value={this.values.forum_title()} oninput={m.withAttr('value', this.values.forum_title)} />],
|
||||
})}
|
||||
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.basics.forum_description_heading'),
|
||||
children: [
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.basics.forum_description_text')}
|
||||
</div>,
|
||||
<textarea className="FormControl" value={this.values.forum_description()} oninput={m.withAttr('value', this.values.forum_description)}/>
|
||||
]
|
||||
<div className="helpText">{app.translator.trans('core.admin.basics.forum_description_text')}</div>,
|
||||
<textarea
|
||||
className="FormControl"
|
||||
value={this.values.forum_description()}
|
||||
oninput={m.withAttr('value', this.values.forum_description)}
|
||||
/>,
|
||||
],
|
||||
})}
|
||||
|
||||
{Object.keys(this.localeOptions).length > 1
|
||||
@ -65,14 +65,14 @@ export default class BasicsPage extends Page {
|
||||
Select.component({
|
||||
options: this.localeOptions,
|
||||
value: this.values.default_locale(),
|
||||
onchange: this.values.default_locale
|
||||
onchange: this.values.default_locale,
|
||||
}),
|
||||
Switch.component({
|
||||
state: this.values.show_language_selector(),
|
||||
onchange: this.values.show_language_selector,
|
||||
children: app.translator.trans('core.admin.basics.show_language_selector_label'),
|
||||
})
|
||||
]
|
||||
}),
|
||||
],
|
||||
})
|
||||
: ''}
|
||||
|
||||
@ -80,30 +80,38 @@ export default class BasicsPage extends Page {
|
||||
label: app.translator.trans('core.admin.basics.home_page_heading'),
|
||||
className: 'BasicsPage-homePage',
|
||||
children: [
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.basics.home_page_text')}
|
||||
</div>,
|
||||
this.homePageItems().toArray().map(({path, label}) =>
|
||||
<div className="helpText">{app.translator.trans('core.admin.basics.home_page_text')}</div>,
|
||||
this.homePageItems()
|
||||
.toArray()
|
||||
.map(({ path, label }) => (
|
||||
<label className="checkbox">
|
||||
<input type="radio" name="homePage" value={path} checked={this.values.default_route() === path} onclick={m.withAttr('value', this.values.default_route)}/>
|
||||
<input
|
||||
type="radio"
|
||||
name="homePage"
|
||||
value={path}
|
||||
checked={this.values.default_route() === path}
|
||||
onclick={m.withAttr('value', this.values.default_route)}
|
||||
/>
|
||||
{label}
|
||||
</label>
|
||||
)
|
||||
]
|
||||
)),
|
||||
],
|
||||
})}
|
||||
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.basics.welcome_banner_heading'),
|
||||
className: 'BasicsPage-welcomeBanner',
|
||||
children: [
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.basics.welcome_banner_text')}
|
||||
</div>,
|
||||
<div className="helpText">{app.translator.trans('core.admin.basics.welcome_banner_text')}</div>,
|
||||
<div className="BasicsPage-welcomeBanner-input">
|
||||
<input className="FormControl" value={this.values.welcome_title()} oninput={m.withAttr('value', this.values.welcome_title)}/>
|
||||
<textarea className="FormControl" value={this.values.welcome_message()} oninput={m.withAttr('value', this.values.welcome_message)}/>
|
||||
</div>
|
||||
]
|
||||
<input className="FormControl" value={this.values.welcome_title()} oninput={m.withAttr('value', this.values.welcome_title)} />
|
||||
<textarea
|
||||
className="FormControl"
|
||||
value={this.values.welcome_message()}
|
||||
oninput={m.withAttr('value', this.values.welcome_message)}
|
||||
/>
|
||||
</div>,
|
||||
],
|
||||
})}
|
||||
|
||||
{Button.component({
|
||||
@ -111,7 +119,7 @@ export default class BasicsPage extends Page {
|
||||
className: 'Button Button--primary',
|
||||
children: app.translator.trans('core.admin.basics.submit_button'),
|
||||
loading: this.loading,
|
||||
disabled: !this.changed()
|
||||
disabled: !this.changed(),
|
||||
})}
|
||||
</form>
|
||||
</div>
|
||||
@ -120,7 +128,7 @@ export default class BasicsPage extends Page {
|
||||
}
|
||||
|
||||
changed() {
|
||||
return this.fields.some(key => this.values[key]() !== app.data.settings[key]);
|
||||
return this.fields.some((key) => this.values[key]() !== app.data.settings[key]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,7 +143,7 @@ export default class BasicsPage extends Page {
|
||||
|
||||
items.add('allDiscussions', {
|
||||
path: '/all',
|
||||
label: app.translator.trans('core.admin.basics.all_discussions_label')
|
||||
label: app.translator.trans('core.admin.basics.all_discussions_label'),
|
||||
});
|
||||
|
||||
return items;
|
||||
@ -151,11 +159,11 @@ export default class BasicsPage extends Page {
|
||||
|
||||
const settings = {};
|
||||
|
||||
this.fields.forEach(key => settings[key] = this.values[key]());
|
||||
this.fields.forEach((key) => (settings[key] = this.values[key]()));
|
||||
|
||||
saveSettings(settings)
|
||||
.then(() => {
|
||||
app.alerts.show(this.successAlert = new Alert({type: 'success', children: app.translator.trans('core.admin.basics.saved_message')}));
|
||||
app.alerts.show((this.successAlert = new Alert({ type: 'success', children: app.translator.trans('core.admin.basics.saved_message') })));
|
||||
})
|
||||
.catch(() => {})
|
||||
.then(() => {
|
||||
|
@ -5,14 +5,12 @@ export default class DashboardPage extends Page {
|
||||
view() {
|
||||
return (
|
||||
<div className="DashboardPage">
|
||||
<div className="container">
|
||||
{this.availableWidgets()}
|
||||
</div>
|
||||
<div className="container">{this.availableWidgets()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
availableWidgets() {
|
||||
return [<StatusWidget/>];
|
||||
return [<StatusWidget />];
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,7 @@ import Component from '../../common/Component';
|
||||
|
||||
export default class Widget extends Component {
|
||||
view() {
|
||||
return (
|
||||
<div className={"Widget "+this.className()}>
|
||||
{this.content()}
|
||||
</div>
|
||||
);
|
||||
return <div className={'Widget ' + this.className()}>{this.content()}</div>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,10 +11,14 @@ export default class EditCustomCssModal extends SettingsModal {
|
||||
|
||||
form() {
|
||||
return [
|
||||
<p>{app.translator.trans('core.admin.edit_css.customize_text', {a: <a href="https://github.com/flarum/core/tree/master/less" target="_blank"/>})}</p>,
|
||||
<p>
|
||||
{app.translator.trans('core.admin.edit_css.customize_text', {
|
||||
a: <a href="https://github.com/flarum/core/tree/master/less" target="_blank" />,
|
||||
})}
|
||||
</p>,
|
||||
<div className="Form-group">
|
||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_less')}/>
|
||||
</div>
|
||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_less')} />
|
||||
</div>,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,8 @@ export default class EditCustomFooterModal extends SettingsModal {
|
||||
return [
|
||||
<p>{app.translator.trans('core.admin.edit_footer.customize_text')}</p>,
|
||||
<div className="Form-group">
|
||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_footer')}/>
|
||||
</div>
|
||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_footer')} />
|
||||
</div>,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,8 @@ export default class EditCustomHeaderModal extends SettingsModal {
|
||||
return [
|
||||
<p>{app.translator.trans('core.admin.edit_header.customize_text')}</p>,
|
||||
<div className="Form-group">
|
||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_header')}/>
|
||||
</div>
|
||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_header')} />
|
||||
</div>,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -24,21 +24,21 @@ export default class EditGroupModal extends Modal {
|
||||
|
||||
title() {
|
||||
return [
|
||||
this.color() || this.icon() ? Badge.component({
|
||||
this.color() || this.icon()
|
||||
? Badge.component({
|
||||
icon: this.icon(),
|
||||
style: {backgroundColor: this.color()}
|
||||
}) : '',
|
||||
style: { backgroundColor: this.color() },
|
||||
})
|
||||
: '',
|
||||
' ',
|
||||
this.namePlural() || app.translator.trans('core.admin.edit_group.title')
|
||||
this.namePlural() || app.translator.trans('core.admin.edit_group.title'),
|
||||
];
|
||||
}
|
||||
|
||||
content() {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<div className="Form">
|
||||
{this.fields().toArray()}
|
||||
</div>
|
||||
<div className="Form">{this.fields().toArray()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -46,40 +46,68 @@ export default class EditGroupModal extends Modal {
|
||||
fields() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('name', <div className="Form-group">
|
||||
items.add(
|
||||
'name',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.admin.edit_group.name_label')}</label>
|
||||
<div className="EditGroupModal-name-input">
|
||||
<input className="FormControl" placeholder={app.translator.trans('core.admin.edit_group.singular_placeholder')} value={this.nameSingular()} oninput={m.withAttr('value', this.nameSingular)}/>
|
||||
<input className="FormControl" placeholder={app.translator.trans('core.admin.edit_group.plural_placeholder')} value={this.namePlural()} oninput={m.withAttr('value', this.namePlural)}/>
|
||||
<input
|
||||
className="FormControl"
|
||||
placeholder={app.translator.trans('core.admin.edit_group.singular_placeholder')}
|
||||
value={this.nameSingular()}
|
||||
oninput={m.withAttr('value', this.nameSingular)}
|
||||
/>
|
||||
<input
|
||||
className="FormControl"
|
||||
placeholder={app.translator.trans('core.admin.edit_group.plural_placeholder')}
|
||||
value={this.namePlural()}
|
||||
oninput={m.withAttr('value', this.namePlural)}
|
||||
/>
|
||||
</div>
|
||||
</div>, 30);
|
||||
</div>,
|
||||
30
|
||||
);
|
||||
|
||||
items.add('color', <div className="Form-group">
|
||||
items.add(
|
||||
'color',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.admin.edit_group.color_label')}</label>
|
||||
<input className="FormControl" placeholder="#aaaaaa" value={this.color()} oninput={m.withAttr('value', this.color)}/>
|
||||
</div>, 20);
|
||||
<input className="FormControl" placeholder="#aaaaaa" value={this.color()} oninput={m.withAttr('value', this.color)} />
|
||||
</div>,
|
||||
20
|
||||
);
|
||||
|
||||
items.add('icon', <div className="Form-group">
|
||||
items.add(
|
||||
'icon',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.admin.edit_group.icon_label')}</label>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.edit_group.icon_text', {a: <a href="https://fontawesome.com/icons?m=free" tabindex="-1"/>})}
|
||||
{app.translator.trans('core.admin.edit_group.icon_text', { a: <a href="https://fontawesome.com/icons?m=free" tabindex="-1" /> })}
|
||||
</div>
|
||||
<input className="FormControl" placeholder="fas fa-bolt" value={this.icon()} oninput={m.withAttr('value', this.icon)}/>
|
||||
</div>, 10);
|
||||
<input className="FormControl" placeholder="fas fa-bolt" value={this.icon()} oninput={m.withAttr('value', this.icon)} />
|
||||
</div>,
|
||||
10
|
||||
);
|
||||
|
||||
items.add('submit', <div className="Form-group">
|
||||
items.add(
|
||||
'submit',
|
||||
<div className="Form-group">
|
||||
{Button.component({
|
||||
type: 'submit',
|
||||
className: 'Button Button--primary EditGroupModal-save',
|
||||
loading: this.loading,
|
||||
children: app.translator.trans('core.admin.edit_group.submit_button')
|
||||
children: app.translator.trans('core.admin.edit_group.submit_button'),
|
||||
})}
|
||||
{this.group.exists && this.group.id() !== Group.ADMINISTRATOR_ID ? (
|
||||
<button type="button" className="Button EditGroupModal-delete" onclick={this.deleteGroup.bind(this)}>
|
||||
{app.translator.trans('core.admin.edit_group.delete_button')}
|
||||
</button>
|
||||
) : ''}
|
||||
</div>, -10);
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>,
|
||||
-10
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -89,7 +117,7 @@ export default class EditGroupModal extends Modal {
|
||||
nameSingular: this.nameSingular(),
|
||||
namePlural: this.namePlural(),
|
||||
color: this.color(),
|
||||
icon: this.icon()
|
||||
icon: this.icon(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -98,7 +126,8 @@ export default class EditGroupModal extends Modal {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
this.group.save(this.submitData(), {errorHandler: this.onerror.bind(this)})
|
||||
this.group
|
||||
.save(this.submitData(), { errorHandler: this.onerror.bind(this) })
|
||||
.then(this.hide.bind(this))
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
|
@ -19,7 +19,7 @@ export default class ExtensionsPage extends Page {
|
||||
children: app.translator.trans('core.admin.extensions.add_button'),
|
||||
icon: 'fas fa-plus',
|
||||
className: 'Button Button--primary',
|
||||
onclick: () => app.modal.show(new AddExtensionModal())
|
||||
onclick: () => app.modal.show(new AddExtensionModal()),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
@ -27,12 +27,12 @@ export default class ExtensionsPage extends Page {
|
||||
<div className="ExtensionsPage-list">
|
||||
<div className="container">
|
||||
<ul className="ExtensionList">
|
||||
{Object.keys(app.data.extensions)
|
||||
.map(id => {
|
||||
{Object.keys(app.data.extensions).map((id) => {
|
||||
const extension = app.data.extensions[id];
|
||||
const controls = this.controlItems(extension.id).toArray();
|
||||
|
||||
return <li className={'ExtensionListItem ' + (!this.isEnabled(extension.id) ? 'disabled' : '')}>
|
||||
return (
|
||||
<li className={'ExtensionListItem ' + (!this.isEnabled(extension.id) ? 'disabled' : '')}>
|
||||
<div className="ExtensionListItem-content">
|
||||
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
|
||||
{extension.icon ? icon(extension.icon.name) : ''}
|
||||
@ -42,20 +42,24 @@ export default class ExtensionsPage extends Page {
|
||||
className="ExtensionListItem-controls"
|
||||
buttonClassName="Button Button--icon Button--flat"
|
||||
menuClassName="Dropdown-menu--right"
|
||||
icon="fas fa-ellipsis-h">
|
||||
icon="fas fa-ellipsis-h"
|
||||
>
|
||||
{controls}
|
||||
</Dropdown>
|
||||
) : ''}
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<div className="ExtensionListItem-main">
|
||||
<label className="ExtensionListItem-title">
|
||||
<input type="checkbox" checked={this.isEnabled(extension.id)} onclick={this.toggle.bind(this, extension.id)}/> {' '}
|
||||
<input type="checkbox" checked={this.isEnabled(extension.id)} onclick={this.toggle.bind(this, extension.id)} />{' '}
|
||||
{extension.extra['flarum-extension'].title}
|
||||
</label>
|
||||
<div className="ExtensionListItem-version">{extension.version}</div>
|
||||
<div className="ExtensionListItem-description">{extension.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>;
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
@ -69,26 +73,34 @@ export default class ExtensionsPage extends Page {
|
||||
const enabled = this.isEnabled(name);
|
||||
|
||||
if (app.extensionSettings[name]) {
|
||||
items.add('settings', Button.component({
|
||||
items.add(
|
||||
'settings',
|
||||
Button.component({
|
||||
icon: 'fas fa-cog',
|
||||
children: app.translator.trans('core.admin.extensions.settings_button'),
|
||||
onclick: app.extensionSettings[name]
|
||||
}));
|
||||
onclick: app.extensionSettings[name],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
items.add('uninstall', Button.component({
|
||||
items.add(
|
||||
'uninstall',
|
||||
Button.component({
|
||||
icon: 'far fa-trash-alt',
|
||||
children: app.translator.trans('core.admin.extensions.uninstall_button'),
|
||||
onclick: () => {
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
url: app.forum.attribute('apiUrl') + '/extensions/' + name,
|
||||
method: 'DELETE'
|
||||
}).then(() => window.location.reload());
|
||||
method: 'DELETE',
|
||||
})
|
||||
.then(() => window.location.reload());
|
||||
|
||||
app.modal.show(new LoadingModal());
|
||||
}
|
||||
}));
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
@ -103,11 +115,13 @@ export default class ExtensionsPage extends Page {
|
||||
toggle(id) {
|
||||
const enabled = this.isEnabled(id);
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
url: app.forum.attribute('apiUrl') + '/extensions/' + id,
|
||||
method: 'PATCH',
|
||||
data: {enabled: !enabled}
|
||||
}).then(() => {
|
||||
data: { enabled: !enabled },
|
||||
})
|
||||
.then(() => {
|
||||
if (!enabled) localStorage.setItem('enabledExtension', id);
|
||||
window.location.reload();
|
||||
});
|
||||
|
@ -8,11 +8,7 @@ import listItems from '../../common/helpers/listItems';
|
||||
*/
|
||||
export default class HeaderPrimary extends Component {
|
||||
view() {
|
||||
return (
|
||||
<ul className="Header-controls">
|
||||
{listItems(this.items().toArray())}
|
||||
</ul>
|
||||
);
|
||||
return <ul className="Header-controls">{listItems(this.items().toArray())}</ul>;
|
||||
}
|
||||
|
||||
config(isInitialized, context) {
|
||||
|
@ -8,11 +8,7 @@ import listItems from '../../common/helpers/listItems';
|
||||
*/
|
||||
export default class HeaderSecondary extends Component {
|
||||
view() {
|
||||
return (
|
||||
<ul className="Header-controls">
|
||||
{listItems(this.items().toArray())}
|
||||
</ul>
|
||||
);
|
||||
return <ul className="Header-controls">{listItems(this.items().toArray())}</ul>;
|
||||
}
|
||||
|
||||
config(isInitialized, context) {
|
||||
|
@ -20,15 +20,17 @@ export default class MailPage extends Page {
|
||||
this.driverFields = {};
|
||||
this.fields = ['mail_driver', 'mail_from'];
|
||||
this.values = {};
|
||||
this.status = {sending: false, errors: {}};
|
||||
this.status = { sending: false, errors: {} };
|
||||
|
||||
const settings = app.data.settings;
|
||||
this.fields.forEach(key => this.values[key] = m.prop(settings[key]));
|
||||
this.fields.forEach((key) => (this.values[key] = m.prop(settings[key])));
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
method: 'GET',
|
||||
url: app.forum.attribute('apiUrl') + '/mail-settings'
|
||||
}).then(response => {
|
||||
url: app.forum.attribute('apiUrl') + '/mail-settings',
|
||||
})
|
||||
.then((response) => {
|
||||
this.driverFields = response['data']['attributes']['fields'];
|
||||
this.status.sending = response['data']['attributes']['sending'];
|
||||
this.status.errors = response['data']['attributes']['errors'];
|
||||
@ -64,9 +66,7 @@ export default class MailPage extends Page {
|
||||
<div className="container">
|
||||
<form onsubmit={this.onsubmit.bind(this)}>
|
||||
<h2>{app.translator.trans('core.admin.email.heading')}</h2>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('core.admin.email.text')}
|
||||
</div>
|
||||
<div className="helpText">{app.translator.trans('core.admin.email.text')}</div>
|
||||
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.email.addresses_heading'),
|
||||
@ -77,8 +77,8 @@ export default class MailPage extends Page {
|
||||
{app.translator.trans('core.admin.email.from_label')}
|
||||
<input className="FormControl" value={this.values.mail_from() || ''} oninput={m.withAttr('value', this.values.mail_from)} />
|
||||
</label>
|
||||
</div>
|
||||
]
|
||||
</div>,
|
||||
],
|
||||
})}
|
||||
|
||||
{FieldSet.component({
|
||||
@ -88,38 +88,44 @@ export default class MailPage extends Page {
|
||||
<div className="MailPage-MailSettings-input">
|
||||
<label>
|
||||
{app.translator.trans('core.admin.email.driver_label')}
|
||||
<Select value={this.values.mail_driver()} options={Object.keys(this.driverFields).reduce((memo, val) => ({...memo, [val]: val}), {})} onchange={this.values.mail_driver} />
|
||||
<Select
|
||||
value={this.values.mail_driver()}
|
||||
options={Object.keys(this.driverFields).reduce((memo, val) => ({ ...memo, [val]: val }), {})}
|
||||
onchange={this.values.mail_driver}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
]
|
||||
</div>,
|
||||
],
|
||||
})}
|
||||
|
||||
{this.status.sending || Alert.component({
|
||||
{this.status.sending ||
|
||||
Alert.component({
|
||||
children: app.translator.trans('core.admin.email.not_sending_message'),
|
||||
dismissible: false,
|
||||
})}
|
||||
|
||||
{fieldKeys.length > 0 && FieldSet.component({
|
||||
{fieldKeys.length > 0 &&
|
||||
FieldSet.component({
|
||||
label: app.translator.trans(`core.admin.email.${this.values.mail_driver()}_heading`),
|
||||
className: 'MailPage-MailSettings',
|
||||
children: [
|
||||
<div className="MailPage-MailSettings-input">
|
||||
{fieldKeys.map(field => [
|
||||
{fieldKeys.map((field) => [
|
||||
<label>
|
||||
{app.translator.trans(`core.admin.email.${field}_label`)}
|
||||
{this.renderField(field)}
|
||||
</label>,
|
||||
this.status.errors[field] && <p className='ValidationError'>{this.status.errors[field]}</p>,
|
||||
this.status.errors[field] && <p className="ValidationError">{this.status.errors[field]}</p>,
|
||||
])}
|
||||
</div>
|
||||
]
|
||||
</div>,
|
||||
],
|
||||
})}
|
||||
|
||||
{Button.component({
|
||||
type: 'submit',
|
||||
className: 'Button Button--primary',
|
||||
children: app.translator.trans('core.admin.email.submit_button'),
|
||||
disabled: !this.changed()
|
||||
disabled: !this.changed(),
|
||||
})}
|
||||
</form>
|
||||
</div>
|
||||
@ -140,7 +146,7 @@ export default class MailPage extends Page {
|
||||
}
|
||||
|
||||
changed() {
|
||||
return this.fields.some(key => this.values[key]() !== app.data.settings[key]);
|
||||
return this.fields.some((key) => this.values[key]() !== app.data.settings[key]);
|
||||
}
|
||||
|
||||
onsubmit(e) {
|
||||
@ -153,11 +159,11 @@ export default class MailPage extends Page {
|
||||
|
||||
const settings = {};
|
||||
|
||||
this.fields.forEach(key => settings[key] = this.values[key]());
|
||||
this.fields.forEach((key) => (settings[key] = this.values[key]()));
|
||||
|
||||
saveSettings(settings)
|
||||
.then(() => {
|
||||
app.alerts.show(this.successAlert = new Alert({type: 'success', children: app.translator.trans('core.admin.basics.saved_message')}));
|
||||
app.alerts.show((this.successAlert = new Alert({ type: 'success', children: app.translator.trans('core.admin.basics.saved_message') })));
|
||||
})
|
||||
.catch(() => {})
|
||||
.then(() => {
|
||||
|
@ -8,22 +8,21 @@ import GroupBadge from '../../common/components/GroupBadge';
|
||||
function badgeForId(id) {
|
||||
const group = app.store.getById('groups', id);
|
||||
|
||||
return group ? GroupBadge.component({group, label: null}) : '';
|
||||
return group ? GroupBadge.component({ group, label: null }) : '';
|
||||
}
|
||||
|
||||
function filterByRequiredPermissions(groupIds, permission) {
|
||||
app.getRequiredPermissions(permission)
|
||||
.forEach(required => {
|
||||
app.getRequiredPermissions(permission).forEach((required) => {
|
||||
const restrictToGroupIds = app.data.permissions[required] || [];
|
||||
|
||||
if (restrictToGroupIds.indexOf(Group.GUEST_ID) !== -1) {
|
||||
// do nothing
|
||||
} else if (restrictToGroupIds.indexOf(Group.MEMBER_ID) !== -1) {
|
||||
groupIds = groupIds.filter(id => id !== Group.GUEST_ID);
|
||||
groupIds = groupIds.filter((id) => id !== Group.GUEST_ID);
|
||||
} else if (groupIds.indexOf(Group.MEMBER_ID) !== -1) {
|
||||
groupIds = restrictToGroupIds;
|
||||
} else {
|
||||
groupIds = restrictToGroupIds.filter(id => groupIds.indexOf(id) !== -1);
|
||||
groupIds = restrictToGroupIds.filter((id) => groupIds.indexOf(id) !== -1);
|
||||
}
|
||||
|
||||
groupIds = filterByRequiredPermissions(groupIds, required);
|
||||
@ -52,34 +51,31 @@ export default class PermissionDropdown extends Dropdown {
|
||||
const adminGroup = app.store.getById('groups', Group.ADMINISTRATOR_ID);
|
||||
|
||||
if (everyone) {
|
||||
this.props.label = Badge.component({icon: 'fas fa-globe'});
|
||||
this.props.label = Badge.component({ icon: 'fas fa-globe' });
|
||||
} else if (members) {
|
||||
this.props.label = Badge.component({icon: 'fas fa-user'});
|
||||
this.props.label = Badge.component({ icon: 'fas fa-user' });
|
||||
} else {
|
||||
this.props.label = [
|
||||
badgeForId(Group.ADMINISTRATOR_ID),
|
||||
groupIds.map(badgeForId)
|
||||
];
|
||||
this.props.label = [badgeForId(Group.ADMINISTRATOR_ID), groupIds.map(badgeForId)];
|
||||
}
|
||||
|
||||
if (this.showing) {
|
||||
if (this.props.allowGuest) {
|
||||
this.props.children.push(
|
||||
Button.component({
|
||||
children: [Badge.component({icon: 'fas fa-globe'}), ' ', app.translator.trans('core.admin.permissions_controls.everyone_button')],
|
||||
children: [Badge.component({ icon: 'fas fa-globe' }), ' ', app.translator.trans('core.admin.permissions_controls.everyone_button')],
|
||||
icon: everyone ? 'fas fa-check' : true,
|
||||
onclick: () => this.save([Group.GUEST_ID]),
|
||||
disabled: this.isGroupDisabled(Group.GUEST_ID)
|
||||
disabled: this.isGroupDisabled(Group.GUEST_ID),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.props.children.push(
|
||||
Button.component({
|
||||
children: [Badge.component({icon: 'fas fa-user'}), ' ', app.translator.trans('core.admin.permissions_controls.members_button')],
|
||||
children: [Badge.component({ icon: 'fas fa-user' }), ' ', app.translator.trans('core.admin.permissions_controls.members_button')],
|
||||
icon: members ? 'fas fa-check' : true,
|
||||
onclick: () => this.save([Group.MEMBER_ID]),
|
||||
disabled: this.isGroupDisabled(Group.MEMBER_ID)
|
||||
disabled: this.isGroupDisabled(Group.MEMBER_ID),
|
||||
}),
|
||||
|
||||
Separator.component(),
|
||||
@ -88,26 +84,29 @@ export default class PermissionDropdown extends Dropdown {
|
||||
children: [badgeForId(adminGroup.id()), ' ', adminGroup.namePlural()],
|
||||
icon: !everyone && !members ? 'fas fa-check' : true,
|
||||
disabled: !everyone && !members,
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
if (e.shiftKey) e.stopPropagation();
|
||||
this.save([]);
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
[].push.apply(
|
||||
this.props.children,
|
||||
app.store.all('groups')
|
||||
.filter(group => [Group.ADMINISTRATOR_ID, Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||
.map(group => Button.component({
|
||||
app.store
|
||||
.all('groups')
|
||||
.filter((group) => [Group.ADMINISTRATOR_ID, Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||
.map((group) =>
|
||||
Button.component({
|
||||
children: [badgeForId(group.id()), ' ', group.namePlural()],
|
||||
icon: groupIds.indexOf(group.id()) !== -1 ? 'fas fa-check' : true,
|
||||
onclick: (e) => {
|
||||
if (e.shiftKey) e.stopPropagation();
|
||||
this.toggle(group.id());
|
||||
},
|
||||
disabled: this.isGroupDisabled(group.id()) && this.isGroupDisabled(Group.MEMBER_ID) && this.isGroupDisabled(Group.GUEST_ID)
|
||||
}))
|
||||
disabled: this.isGroupDisabled(group.id()) && this.isGroupDisabled(Group.MEMBER_ID) && this.isGroupDisabled(Group.GUEST_ID),
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -122,7 +121,7 @@ export default class PermissionDropdown extends Dropdown {
|
||||
app.request({
|
||||
method: 'POST',
|
||||
url: app.forum.attribute('apiUrl') + '/permission',
|
||||
data: {permission, groupIds}
|
||||
data: { permission, groupIds },
|
||||
});
|
||||
}
|
||||
|
||||
@ -137,7 +136,7 @@ export default class PermissionDropdown extends Dropdown {
|
||||
groupIds.splice(index, 1);
|
||||
} else {
|
||||
groupIds.push(groupId);
|
||||
groupIds = groupIds.filter(id => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(id) === -1);
|
||||
groupIds = groupIds.filter((id) => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(id) === -1);
|
||||
}
|
||||
|
||||
this.save(groupIds);
|
||||
|
@ -13,12 +13,8 @@ export default class PermissionGrid extends Component {
|
||||
view() {
|
||||
const scopes = this.scopeItems().toArray();
|
||||
|
||||
const permissionCells = permission => {
|
||||
return scopes.map(scope => (
|
||||
<td>
|
||||
{scope.render(permission)}
|
||||
</td>
|
||||
));
|
||||
const permissionCells = (permission) => {
|
||||
return scopes.map((scope) => <td>{scope.render(permission)}</td>);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -26,27 +22,32 @@ export default class PermissionGrid extends Component {
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
{scopes.map(scope => (
|
||||
{scopes.map((scope) => (
|
||||
<th>
|
||||
{scope.label}{' '}
|
||||
{scope.onremove ? Button.component({icon: 'fas fa-times', className: 'Button Button--text PermissionGrid-removeScope', onclick: scope.onremove}) : ''}
|
||||
{scope.onremove
|
||||
? Button.component({ icon: 'fas fa-times', className: 'Button Button--text PermissionGrid-removeScope', onclick: scope.onremove })
|
||||
: ''}
|
||||
</th>
|
||||
))}
|
||||
<th>{this.scopeControlItems().toArray()}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{this.permissions.map(section => (
|
||||
{this.permissions.map((section) => (
|
||||
<tbody>
|
||||
<tr className="PermissionGrid-section">
|
||||
<th>{section.label}</th>
|
||||
{permissionCells(section)}
|
||||
<td/>
|
||||
<td />
|
||||
</tr>
|
||||
{section.children.map(child => (
|
||||
{section.children.map((child) => (
|
||||
<tr className="PermissionGrid-child">
|
||||
<th>{icon(child.icon)}{child.label}</th>
|
||||
<th>
|
||||
{icon(child.icon)}
|
||||
{child.label}
|
||||
</th>
|
||||
{permissionCells(child)}
|
||||
<td/>
|
||||
<td />
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@ -58,25 +59,41 @@ export default class PermissionGrid extends Component {
|
||||
permissionItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('view', {
|
||||
items.add(
|
||||
'view',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.read_heading'),
|
||||
children: this.viewItems().toArray()
|
||||
}, 100);
|
||||
children: this.viewItems().toArray(),
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add('start', {
|
||||
items.add(
|
||||
'start',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.create_heading'),
|
||||
children: this.startItems().toArray()
|
||||
}, 90);
|
||||
children: this.startItems().toArray(),
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
items.add('reply', {
|
||||
items.add(
|
||||
'reply',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.participate_heading'),
|
||||
children: this.replyItems().toArray()
|
||||
}, 80);
|
||||
children: this.replyItems().toArray(),
|
||||
},
|
||||
80
|
||||
);
|
||||
|
||||
items.add('moderate', {
|
||||
items.add(
|
||||
'moderate',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.moderate_heading'),
|
||||
children: this.moderateItems().toArray()
|
||||
}, 70);
|
||||
children: this.moderateItems().toArray(),
|
||||
},
|
||||
70
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -84,31 +101,44 @@ export default class PermissionGrid extends Component {
|
||||
viewItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('viewDiscussions', {
|
||||
items.add(
|
||||
'viewDiscussions',
|
||||
{
|
||||
icon: 'fas fa-eye',
|
||||
label: app.translator.trans('core.admin.permissions.view_discussions_label'),
|
||||
permission: 'viewDiscussions',
|
||||
allowGuest: true
|
||||
}, 100);
|
||||
allowGuest: true,
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add('viewUserList', {
|
||||
items.add(
|
||||
'viewUserList',
|
||||
{
|
||||
icon: 'fas fa-users',
|
||||
label: app.translator.trans('core.admin.permissions.view_user_list_label'),
|
||||
permission: 'viewUserList',
|
||||
allowGuest: true
|
||||
}, 100);
|
||||
allowGuest: true,
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add('signUp', {
|
||||
items.add(
|
||||
'signUp',
|
||||
{
|
||||
icon: 'fas fa-user-plus',
|
||||
label: app.translator.trans('core.admin.permissions.sign_up_label'),
|
||||
setting: () => SettingDropdown.component({
|
||||
setting: () =>
|
||||
SettingDropdown.component({
|
||||
key: 'allow_sign_up',
|
||||
options: [
|
||||
{value: '1', label: app.translator.trans('core.admin.permissions_controls.signup_open_button')},
|
||||
{value: '0', label: app.translator.trans('core.admin.permissions_controls.signup_closed_button')}
|
||||
]
|
||||
})
|
||||
}, 90);
|
||||
{ value: '1', label: app.translator.trans('core.admin.permissions_controls.signup_open_button') },
|
||||
{ value: '0', label: app.translator.trans('core.admin.permissions_controls.signup_closed_button') },
|
||||
],
|
||||
}),
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
items.add('viewLastSeenAt', {
|
||||
icon: 'far fa-clock',
|
||||
@ -122,13 +152,19 @@ export default class PermissionGrid extends Component {
|
||||
startItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('start', {
|
||||
items.add(
|
||||
'start',
|
||||
{
|
||||
icon: 'fas fa-edit',
|
||||
label: app.translator.trans('core.admin.permissions.start_discussions_label'),
|
||||
permission: 'startDiscussion'
|
||||
}, 100);
|
||||
permission: 'startDiscussion',
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add('allowRenaming', {
|
||||
items.add(
|
||||
'allowRenaming',
|
||||
{
|
||||
icon: 'fas fa-i-cursor',
|
||||
label: app.translator.trans('core.admin.permissions.allow_renaming_label'),
|
||||
setting: () => {
|
||||
@ -136,17 +172,19 @@ export default class PermissionGrid extends Component {
|
||||
|
||||
return SettingDropdown.component({
|
||||
defaultLabel: minutes
|
||||
? app.translator.transChoice('core.admin.permissions_controls.allow_some_minutes_button', minutes, {count: minutes})
|
||||
? app.translator.transChoice('core.admin.permissions_controls.allow_some_minutes_button', minutes, { count: minutes })
|
||||
: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'),
|
||||
key: 'allow_renaming',
|
||||
options: [
|
||||
{value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button')},
|
||||
{value: '10', label: app.translator.trans('core.admin.permissions_controls.allow_ten_minutes_button')},
|
||||
{value: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button')}
|
||||
]
|
||||
{ value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button') },
|
||||
{ value: '10', label: app.translator.trans('core.admin.permissions_controls.allow_ten_minutes_button') },
|
||||
{ value: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button') },
|
||||
],
|
||||
});
|
||||
}
|
||||
}, 90);
|
||||
},
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -154,13 +192,19 @@ export default class PermissionGrid extends Component {
|
||||
replyItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('reply', {
|
||||
items.add(
|
||||
'reply',
|
||||
{
|
||||
icon: 'fas fa-reply',
|
||||
label: app.translator.trans('core.admin.permissions.reply_to_discussions_label'),
|
||||
permission: 'discussion.reply'
|
||||
}, 100);
|
||||
permission: 'discussion.reply',
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add('allowPostEditing', {
|
||||
items.add(
|
||||
'allowPostEditing',
|
||||
{
|
||||
icon: 'fas fa-pencil-alt',
|
||||
label: app.translator.trans('core.admin.permissions.allow_post_editing_label'),
|
||||
setting: () => {
|
||||
@ -168,17 +212,19 @@ export default class PermissionGrid extends Component {
|
||||
|
||||
return SettingDropdown.component({
|
||||
defaultLabel: minutes
|
||||
? app.translator.transChoice('core.admin.permissions_controls.allow_some_minutes_button', minutes, {count: minutes})
|
||||
? app.translator.transChoice('core.admin.permissions_controls.allow_some_minutes_button', minutes, { count: minutes })
|
||||
: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'),
|
||||
key: 'allow_post_editing',
|
||||
options: [
|
||||
{value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button')},
|
||||
{value: '10', label: app.translator.trans('core.admin.permissions_controls.allow_ten_minutes_button')},
|
||||
{value: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button')}
|
||||
]
|
||||
{ value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button') },
|
||||
{ value: '10', label: app.translator.trans('core.admin.permissions_controls.allow_ten_minutes_button') },
|
||||
{ value: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button') },
|
||||
],
|
||||
});
|
||||
}
|
||||
}, 90);
|
||||
},
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -186,59 +232,95 @@ export default class PermissionGrid extends Component {
|
||||
moderateItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('viewIpsPosts', {
|
||||
items.add(
|
||||
'viewIpsPosts',
|
||||
{
|
||||
icon: 'fas fa-bullseye',
|
||||
label: app.translator.trans('core.admin.permissions.view_post_ips_label'),
|
||||
permission: 'discussion.viewIpsPosts'
|
||||
}, 110);
|
||||
permission: 'discussion.viewIpsPosts',
|
||||
},
|
||||
110
|
||||
);
|
||||
|
||||
items.add('renameDiscussions', {
|
||||
items.add(
|
||||
'renameDiscussions',
|
||||
{
|
||||
icon: 'fas fa-i-cursor',
|
||||
label: app.translator.trans('core.admin.permissions.rename_discussions_label'),
|
||||
permission: 'discussion.rename'
|
||||
}, 100);
|
||||
permission: 'discussion.rename',
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
items.add('hideDiscussions', {
|
||||
items.add(
|
||||
'hideDiscussions',
|
||||
{
|
||||
icon: 'far fa-trash-alt',
|
||||
label: app.translator.trans('core.admin.permissions.delete_discussions_label'),
|
||||
permission: 'discussion.hide'
|
||||
}, 90);
|
||||
permission: 'discussion.hide',
|
||||
},
|
||||
90
|
||||
);
|
||||
|
||||
items.add('deleteDiscussions', {
|
||||
items.add(
|
||||
'deleteDiscussions',
|
||||
{
|
||||
icon: 'fas fa-times',
|
||||
label: app.translator.trans('core.admin.permissions.delete_discussions_forever_label'),
|
||||
permission: 'discussion.delete'
|
||||
}, 80);
|
||||
permission: 'discussion.delete',
|
||||
},
|
||||
80
|
||||
);
|
||||
|
||||
items.add('postWithoutThrottle', {
|
||||
items.add(
|
||||
'postWithoutThrottle',
|
||||
{
|
||||
icon: 'fas fa-swimmer',
|
||||
label: app.translator.trans('core.admin.permissions.post_without_throttle_label'),
|
||||
permission: 'postWithoutThrottle'
|
||||
}, 70);
|
||||
permission: 'postWithoutThrottle',
|
||||
},
|
||||
70
|
||||
);
|
||||
|
||||
items.add('editPosts', {
|
||||
items.add(
|
||||
'editPosts',
|
||||
{
|
||||
icon: 'fas fa-pencil-alt',
|
||||
label: app.translator.trans('core.admin.permissions.edit_posts_label'),
|
||||
permission: 'discussion.editPosts'
|
||||
}, 70);
|
||||
permission: 'discussion.editPosts',
|
||||
},
|
||||
70
|
||||
);
|
||||
|
||||
items.add('hidePosts', {
|
||||
items.add(
|
||||
'hidePosts',
|
||||
{
|
||||
icon: 'far fa-trash-alt',
|
||||
label: app.translator.trans('core.admin.permissions.delete_posts_label'),
|
||||
permission: 'discussion.hidePosts'
|
||||
}, 60);
|
||||
permission: 'discussion.hidePosts',
|
||||
},
|
||||
60
|
||||
);
|
||||
|
||||
items.add('deletePosts', {
|
||||
items.add(
|
||||
'deletePosts',
|
||||
{
|
||||
icon: 'fas fa-times',
|
||||
label: app.translator.trans('core.admin.permissions.delete_posts_forever_label'),
|
||||
permission: 'discussion.deletePosts'
|
||||
}, 60);
|
||||
permission: 'discussion.deletePosts',
|
||||
},
|
||||
60
|
||||
);
|
||||
|
||||
items.add('userEdit', {
|
||||
items.add(
|
||||
'userEdit',
|
||||
{
|
||||
icon: 'fas fa-user-cog',
|
||||
label: app.translator.trans('core.admin.permissions.edit_users_label'),
|
||||
permission: 'user.edit'
|
||||
}, 60);
|
||||
permission: 'user.edit',
|
||||
},
|
||||
60
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -246,21 +328,25 @@ export default class PermissionGrid extends Component {
|
||||
scopeItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('global', {
|
||||
items.add(
|
||||
'global',
|
||||
{
|
||||
label: app.translator.trans('core.admin.permissions.global_heading'),
|
||||
render: item => {
|
||||
render: (item) => {
|
||||
if (item.setting) {
|
||||
return item.setting();
|
||||
} else if (item.permission) {
|
||||
return PermissionDropdown.component({
|
||||
permission: item.permission,
|
||||
allowGuest: item.allowGuest
|
||||
allowGuest: item.allowGuest,
|
||||
});
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
@ -11,29 +11,28 @@ export default class PermissionsPage extends Page {
|
||||
<div className="PermissionsPage">
|
||||
<div className="PermissionsPage-groups">
|
||||
<div className="container">
|
||||
{app.store.all('groups')
|
||||
.filter(group => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||
.map(group => (
|
||||
<button className="Button Group" onclick={() => app.modal.show(new EditGroupModal({group}))}>
|
||||
{app.store
|
||||
.all('groups')
|
||||
.filter((group) => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||
.map((group) => (
|
||||
<button className="Button Group" onclick={() => app.modal.show(new EditGroupModal({ group }))}>
|
||||
{GroupBadge.component({
|
||||
group,
|
||||
className: 'Group-icon',
|
||||
label: null
|
||||
label: null,
|
||||
})}
|
||||
<span className="Group-name">{group.namePlural()}</span>
|
||||
</button>
|
||||
))}
|
||||
<button className="Button Group Group--add" onclick={() => app.modal.show(new EditGroupModal())}>
|
||||
{icon('fas fa-plus', {className: 'Group-icon'})}
|
||||
{icon('fas fa-plus', { className: 'Group-icon' })}
|
||||
<span className="Group-name">{app.translator.trans('core.admin.permissions.new_group_button')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="PermissionsPage-permissions">
|
||||
<div className="container">
|
||||
{PermissionGrid.component()}
|
||||
</div>
|
||||
<div className="container">{PermissionGrid.component()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -26,10 +26,7 @@ export default class SessionDropdown extends Dropdown {
|
||||
getButtonContent() {
|
||||
const user = app.session.user;
|
||||
|
||||
return [
|
||||
avatar(user), ' ',
|
||||
<span className="Button-label">{username(user)}</span>
|
||||
];
|
||||
return [avatar(user), ' ', <span className="Button-label">{username(user)}</span>];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,11 +37,12 @@ export default class SessionDropdown extends Dropdown {
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('logOut',
|
||||
items.add(
|
||||
'logOut',
|
||||
Button.component({
|
||||
icon: 'fas fa-sign-out-alt',
|
||||
children: app.translator.trans('core.admin.header.log_out_button'),
|
||||
onclick: app.session.logout.bind(app.session)
|
||||
onclick: app.session.logout.bind(app.session),
|
||||
}),
|
||||
-100
|
||||
);
|
||||
|
@ -11,14 +11,14 @@ export default class SettingDropdown extends SelectDropdown {
|
||||
props.caretIcon = 'fas fa-caret-down';
|
||||
props.defaultLabel = 'Custom';
|
||||
|
||||
props.children = props.options.map(({value, label}) => {
|
||||
props.children = props.options.map(({ value, label }) => {
|
||||
const active = app.data.settings[props.key] === value;
|
||||
|
||||
return Button.component({
|
||||
children: label,
|
||||
icon: active ? 'fas fa-check' : true,
|
||||
onclick: saveSettings.bind(this, {[props.key]: value}),
|
||||
active
|
||||
onclick: saveSettings.bind(this, { [props.key]: value }),
|
||||
active,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -18,9 +18,7 @@ export default class SettingsModal extends Modal {
|
||||
<div className="Form">
|
||||
{this.form()}
|
||||
|
||||
<div className="Form-group">
|
||||
{this.submitButton()}
|
||||
</div>
|
||||
<div className="Form-group">{this.submitButton()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -28,11 +26,7 @@ export default class SettingsModal extends Modal {
|
||||
|
||||
submitButton() {
|
||||
return (
|
||||
<Button
|
||||
type="submit"
|
||||
className="Button Button--primary"
|
||||
loading={this.loading}
|
||||
disabled={!this.changed()}>
|
||||
<Button type="submit" className="Button Button--primary" loading={this.loading} disabled={!this.changed()}>
|
||||
{app.translator.trans('core.admin.settings.submit_button')}
|
||||
</Button>
|
||||
);
|
||||
@ -47,7 +41,7 @@ export default class SettingsModal extends Modal {
|
||||
dirty() {
|
||||
const dirty = {};
|
||||
|
||||
Object.keys(this.settings).forEach(key => {
|
||||
Object.keys(this.settings).forEach((key) => {
|
||||
const value = this.settings[key]();
|
||||
|
||||
if (value !== app.data.settings[key]) {
|
||||
@ -67,10 +61,7 @@ export default class SettingsModal extends Modal {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
saveSettings(this.dirty()).then(
|
||||
this.onsaved.bind(this),
|
||||
this.loaded.bind(this)
|
||||
);
|
||||
saveSettings(this.dirty()).then(this.onsaved.bind(this), this.loaded.bind(this));
|
||||
}
|
||||
|
||||
onsaved() {
|
||||
|
@ -20,29 +20,27 @@ export default class StatusWidget extends DashboardWidget {
|
||||
}
|
||||
|
||||
content() {
|
||||
return (
|
||||
<ul>{listItems(this.items().toArray())}</ul>
|
||||
);
|
||||
return <ul>{listItems(this.items().toArray())}</ul>;
|
||||
}
|
||||
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('tools', (
|
||||
items.add(
|
||||
'tools',
|
||||
<Dropdown
|
||||
label={app.translator.trans('core.admin.dashboard.tools_button')}
|
||||
icon="fas fa-cog"
|
||||
buttonClassName="Button"
|
||||
menuClassName="Dropdown-menu--right">
|
||||
<Button onclick={this.handleClearCache.bind(this)}>
|
||||
{app.translator.trans('core.admin.dashboard.clear_cache_button')}
|
||||
</Button>
|
||||
menuClassName="Dropdown-menu--right"
|
||||
>
|
||||
<Button onclick={this.handleClearCache.bind(this)}>{app.translator.trans('core.admin.dashboard.clear_cache_button')}</Button>
|
||||
</Dropdown>
|
||||
));
|
||||
);
|
||||
|
||||
items.add('version-flarum', [<strong>Flarum</strong>, <br/>, app.forum.attribute('version')]);
|
||||
items.add('version-php', [<strong>PHP</strong>, <br/>, app.data.phpVersion]);
|
||||
items.add('version-mysql', [<strong>MySQL</strong>, <br/>, app.data.mysqlVersion]);
|
||||
items.add('version-flarum', [<strong>Flarum</strong>, <br />, app.forum.attribute('version')]);
|
||||
items.add('version-php', [<strong>PHP</strong>, <br />, app.data.phpVersion]);
|
||||
items.add('version-mysql', [<strong>MySQL</strong>, <br />, app.data.mysqlVersion]);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -50,9 +48,11 @@ export default class StatusWidget extends DashboardWidget {
|
||||
handleClearCache(e) {
|
||||
app.modal.show(new LoadingModal());
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
method: 'DELETE',
|
||||
url: app.forum.attribute('apiUrl') + '/cache'
|
||||
}).then(() => window.location.reload());
|
||||
url: app.forum.attribute('apiUrl') + '/cache',
|
||||
})
|
||||
.then(() => window.location.reload());
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ export default class UploadImageButton extends Button {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p><img src={app.forum.attribute(this.props.name+'Url')} alt=""/></p>
|
||||
<p>
|
||||
<img src={app.forum.attribute(this.props.name + 'Url')} alt="" />
|
||||
</p>
|
||||
<p>{super.view()}</p>
|
||||
</div>
|
||||
);
|
||||
@ -35,22 +37,25 @@ export default class UploadImageButton extends Button {
|
||||
|
||||
const $input = $('<input type="file">');
|
||||
|
||||
$input.appendTo('body').hide().click().on('change', e => {
|
||||
$input
|
||||
.appendTo('body')
|
||||
.hide()
|
||||
.click()
|
||||
.on('change', (e) => {
|
||||
const data = new FormData();
|
||||
data.append(this.props.name, $(e.target)[0].files[0]);
|
||||
|
||||
this.loading = true;
|
||||
m.redraw();
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
method: 'POST',
|
||||
url: this.resourceUrl(),
|
||||
serialize: raw => raw,
|
||||
data
|
||||
}).then(
|
||||
this.success.bind(this),
|
||||
this.failure.bind(this)
|
||||
);
|
||||
serialize: (raw) => raw,
|
||||
data,
|
||||
})
|
||||
.then(this.success.bind(this), this.failure.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
@ -61,13 +66,12 @@ export default class UploadImageButton extends Button {
|
||||
this.loading = true;
|
||||
m.redraw();
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
method: 'DELETE',
|
||||
url: this.resourceUrl()
|
||||
}).then(
|
||||
this.success.bind(this),
|
||||
this.failure.bind(this)
|
||||
);
|
||||
url: this.resourceUrl(),
|
||||
})
|
||||
.then(this.success.bind(this), this.failure.bind(this));
|
||||
}
|
||||
|
||||
resourceUrl() {
|
||||
|
@ -11,11 +11,7 @@ import Component from '../../common/Component';
|
||||
|
||||
export default class DashboardWidget extends Component {
|
||||
view() {
|
||||
return (
|
||||
<div className={"DashboardWidget "+this.className()}>
|
||||
{this.content()}
|
||||
</div>
|
||||
);
|
||||
return <div className={'DashboardWidget ' + this.className()}>{this.content()}</div>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,7 +9,6 @@ export { app };
|
||||
|
||||
// Export public API
|
||||
|
||||
|
||||
// Export compat API
|
||||
import compat from './compat';
|
||||
|
||||
|
@ -10,13 +10,13 @@ import MailPage from './components/MailPage';
|
||||
*
|
||||
* @param {App} app
|
||||
*/
|
||||
export default function(app) {
|
||||
export default function (app) {
|
||||
app.routes = {
|
||||
'dashboard': {path: '/', component: DashboardPage.component()},
|
||||
'basics': {path: '/basics', component: BasicsPage.component()},
|
||||
'permissions': {path: '/permissions', component: PermissionsPage.component()},
|
||||
'appearance': {path: '/appearance', component: AppearancePage.component()},
|
||||
'extensions': {path: '/extensions', component: ExtensionsPage.component()},
|
||||
'mail': {path: '/mail', component: MailPage.component()}
|
||||
dashboard: { path: '/', component: DashboardPage.component() },
|
||||
basics: { path: '/basics', component: BasicsPage.component() },
|
||||
permissions: { path: '/permissions', component: PermissionsPage.component() },
|
||||
appearance: { path: '/appearance', component: AppearancePage.component() },
|
||||
extensions: { path: '/extensions', component: ExtensionsPage.component() },
|
||||
mail: { path: '/mail', component: MailPage.component() },
|
||||
};
|
||||
}
|
||||
|
@ -3,11 +3,13 @@ export default function saveSettings(settings) {
|
||||
|
||||
Object.assign(app.data.settings, settings);
|
||||
|
||||
return app.request({
|
||||
return app
|
||||
.request({
|
||||
method: 'POST',
|
||||
url: app.forum.attribute('apiUrl') + '/settings',
|
||||
data: settings
|
||||
}).catch(error => {
|
||||
data: settings,
|
||||
})
|
||||
.catch((error) => {
|
||||
app.data.settings = oldSettings;
|
||||
throw error;
|
||||
});
|
||||
|
@ -86,7 +86,7 @@ export default class Application {
|
||||
discussions: Discussion,
|
||||
posts: Post,
|
||||
groups: Group,
|
||||
notifications: Notification
|
||||
notifications: Notification,
|
||||
});
|
||||
|
||||
/**
|
||||
@ -126,22 +126,19 @@ export default class Application {
|
||||
}
|
||||
|
||||
boot() {
|
||||
this.initializers.toArray().forEach(initializer => initializer(this));
|
||||
this.initializers.toArray().forEach((initializer) => initializer(this));
|
||||
|
||||
this.store.pushPayload({data: this.data.resources});
|
||||
this.store.pushPayload({ data: this.data.resources });
|
||||
|
||||
this.forum = this.store.getById('forums', 1);
|
||||
|
||||
this.session = new Session(
|
||||
this.store.getById('users', this.data.session.userId),
|
||||
this.data.session.csrfToken
|
||||
);
|
||||
this.session = new Session(this.store.getById('users', this.data.session.userId), this.data.session.csrfToken);
|
||||
|
||||
this.mount();
|
||||
}
|
||||
|
||||
bootExtensions(extensions) {
|
||||
Object.keys(extensions).forEach(name => {
|
||||
Object.keys(extensions).forEach((name) => {
|
||||
const extension = extensions[name];
|
||||
|
||||
const extenders = flattenDeep(extension.extend);
|
||||
@ -153,26 +150,20 @@ export default class Application {
|
||||
}
|
||||
|
||||
mount(basePath = '') {
|
||||
this.modal = m.mount(document.getElementById('modal'), <ModalManager/>);
|
||||
this.alerts = m.mount(document.getElementById('alerts'), <AlertManager/>);
|
||||
this.modal = m.mount(document.getElementById('modal'), <ModalManager />);
|
||||
this.alerts = m.mount(document.getElementById('alerts'), <AlertManager />);
|
||||
|
||||
this.drawer = new Drawer();
|
||||
|
||||
m.route(
|
||||
document.getElementById('content'),
|
||||
basePath + '/',
|
||||
mapRoutes(this.routes, basePath)
|
||||
);
|
||||
m.route(document.getElementById('content'), basePath + '/', mapRoutes(this.routes, basePath));
|
||||
|
||||
// Add a class to the body which indicates that the page has been scrolled
|
||||
// down.
|
||||
new ScrollListener(top => {
|
||||
new ScrollListener((top) => {
|
||||
const $app = $('#app');
|
||||
const offset = $app.offset().top;
|
||||
|
||||
$app
|
||||
.toggleClass('affix', top >= offset)
|
||||
.toggleClass('scrolled', top > offset);
|
||||
$app.toggleClass('affix', top >= offset).toggleClass('scrolled', top > offset);
|
||||
}).start();
|
||||
|
||||
$(() => {
|
||||
@ -220,9 +211,7 @@ export default class Application {
|
||||
}
|
||||
|
||||
updateTitle() {
|
||||
document.title = (this.titleCount ? `(${this.titleCount}) ` : '') +
|
||||
(this.title ? this.title + ' - ' : '') +
|
||||
this.forum.attribute('title');
|
||||
document.title = (this.titleCount ? `(${this.titleCount}) ` : '') + (this.title ? this.title + ' - ' : '') + this.forum.attribute('title');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -256,9 +245,11 @@ export default class Application {
|
||||
// When we deserialize JSON data, if for some reason the server has provided
|
||||
// a dud response, we don't want the application to crash. We'll show an
|
||||
// error message to the user instead.
|
||||
options.deserialize = options.deserialize || (responseText => responseText);
|
||||
options.deserialize = options.deserialize || ((responseText) => responseText);
|
||||
|
||||
options.errorHandler = options.errorHandler || (error => {
|
||||
options.errorHandler =
|
||||
options.errorHandler ||
|
||||
((error) => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
@ -266,7 +257,7 @@ export default class Application {
|
||||
// response code and show an error message to the user if something's gone
|
||||
// awry.
|
||||
const original = options.extract;
|
||||
options.extract = xhr => {
|
||||
options.extract = (xhr) => {
|
||||
let responseText;
|
||||
|
||||
if (original) {
|
||||
@ -299,7 +290,9 @@ export default class Application {
|
||||
// returned and show an alert containing its contents.
|
||||
const deferred = m.deferred();
|
||||
|
||||
m.request(options).then(response => deferred.resolve(response), error => {
|
||||
m.request(options).then(
|
||||
(response) => deferred.resolve(response),
|
||||
(error) => {
|
||||
this.requestError = error;
|
||||
|
||||
let children;
|
||||
@ -307,7 +300,7 @@ export default class Application {
|
||||
switch (error.status) {
|
||||
case 422:
|
||||
children = error.response.errors
|
||||
.map(error => [error.detail, <br/>])
|
||||
.map((error) => [error.detail, <br />])
|
||||
.reduce((a, b) => a.concat(b), [])
|
||||
.slice(0, -1);
|
||||
break;
|
||||
@ -336,8 +329,10 @@ export default class Application {
|
||||
type: 'error',
|
||||
children,
|
||||
controls: isDebug && [
|
||||
<Button className="Button Button--link" onclick={this.showDebug.bind(this, error)}>Debug</Button>
|
||||
]
|
||||
<Button className="Button Button--link" onclick={this.showDebug.bind(this, error)}>
|
||||
Debug
|
||||
</Button>,
|
||||
],
|
||||
});
|
||||
|
||||
try {
|
||||
@ -347,7 +342,8 @@ export default class Application {
|
||||
}
|
||||
|
||||
deferred.reject(error);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
@ -359,7 +355,7 @@ export default class Application {
|
||||
showDebug(error) {
|
||||
this.alerts.dismiss(this.requestError.alert);
|
||||
|
||||
this.modal.show(new RequestErrorModal({error}));
|
||||
this.modal.show(new RequestErrorModal({ error }));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,8 +70,7 @@ export default class Component {
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
init() {
|
||||
}
|
||||
init() {}
|
||||
|
||||
/**
|
||||
* Called when the component is destroyed, i.e. after a redraw where it is no
|
||||
@ -81,8 +80,7 @@ export default class Component {
|
||||
* @param {Object} e
|
||||
* @public
|
||||
*/
|
||||
onunload() {
|
||||
}
|
||||
onunload() {}
|
||||
|
||||
/**
|
||||
* Get the renderable virtual DOM that represents the component's view.
|
||||
@ -99,7 +97,7 @@ export default class Component {
|
||||
* @public
|
||||
*/
|
||||
render() {
|
||||
const vdom = this.retain ? {subtree: 'retain'} : this.view();
|
||||
const vdom = this.retain ? { subtree: 'retain' } : this.view();
|
||||
|
||||
// Override the root element's config attribute with our own function, which
|
||||
// will set the component instance's element property to the root DOM
|
||||
@ -148,8 +146,7 @@ export default class Component {
|
||||
* @param {Object} vdom
|
||||
* @public
|
||||
*/
|
||||
config() {
|
||||
}
|
||||
config() {}
|
||||
|
||||
/**
|
||||
* Get the virtual DOM that represents the component's view.
|
||||
@ -201,14 +198,14 @@ export default class Component {
|
||||
controller: this.bind(undefined, componentProps),
|
||||
view: view,
|
||||
props: componentProps,
|
||||
component: this
|
||||
component: this,
|
||||
};
|
||||
|
||||
// If a `key` prop was set, then we'll assume that we want that to actually
|
||||
// show up as an attribute on the component object so that Mithril's key
|
||||
// algorithm can be applied.
|
||||
if (componentProps.key) {
|
||||
output.attrs = {key: componentProps.key};
|
||||
output.attrs = { key: componentProps.key };
|
||||
}
|
||||
|
||||
return output;
|
||||
@ -220,6 +217,5 @@ export default class Component {
|
||||
* @param {Object} props
|
||||
* @public
|
||||
*/
|
||||
static initProps(props) {
|
||||
}
|
||||
static initProps(props) {}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ export default class Model {
|
||||
// relationship data object.
|
||||
for (const innerKey in data[key]) {
|
||||
if (data[key][innerKey] instanceof Model) {
|
||||
data[key][innerKey] = {data: Model.getIdentifier(data[key][innerKey])};
|
||||
data[key][innerKey] = { data: Model.getIdentifier(data[key][innerKey]) };
|
||||
}
|
||||
this.data[key][innerKey] = data[key][innerKey];
|
||||
}
|
||||
@ -109,7 +109,7 @@ export default class Model {
|
||||
* @public
|
||||
*/
|
||||
pushAttributes(attributes) {
|
||||
this.pushData({attributes});
|
||||
this.pushData({ attributes });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,7 +125,7 @@ export default class Model {
|
||||
const data = {
|
||||
type: this.data.type,
|
||||
id: this.data.id,
|
||||
attributes
|
||||
attributes,
|
||||
};
|
||||
|
||||
// If a 'relationships' key exists, extract it from the attributes hash and
|
||||
@ -138,9 +138,7 @@ export default class Model {
|
||||
const model = attributes.relationships[key];
|
||||
|
||||
data.relationships[key] = {
|
||||
data: model instanceof Array
|
||||
? model.map(Model.getIdentifier)
|
||||
: Model.getIdentifier(model)
|
||||
data: model instanceof Array ? model.map(Model.getIdentifier) : Model.getIdentifier(model),
|
||||
};
|
||||
}
|
||||
|
||||
@ -154,18 +152,25 @@ export default class Model {
|
||||
|
||||
this.pushData(data);
|
||||
|
||||
const request = {data};
|
||||
const request = { data };
|
||||
if (options.meta) request.meta = options.meta;
|
||||
|
||||
return app.request(Object.assign({
|
||||
return app
|
||||
.request(
|
||||
Object.assign(
|
||||
{
|
||||
method: this.exists ? 'PATCH' : 'POST',
|
||||
url: app.forum.attribute('apiUrl') + this.apiEndpoint(),
|
||||
data: request
|
||||
}, options)).then(
|
||||
data: request,
|
||||
},
|
||||
options
|
||||
)
|
||||
)
|
||||
.then(
|
||||
// If everything went well, we'll make sure the store knows that this
|
||||
// model exists now (if it didn't already), and we'll push the data that
|
||||
// the API returned into the store.
|
||||
payload => {
|
||||
(payload) => {
|
||||
this.store.data[payload.data.type] = this.store.data[payload.data.type] || {};
|
||||
this.store.data[payload.data.type][payload.data.id] = this;
|
||||
return this.store.pushPayload(payload);
|
||||
@ -173,7 +178,7 @@ export default class Model {
|
||||
|
||||
// If something went wrong, though... good thing we backed up our model's
|
||||
// old data! We'll revert to that and let others handle the error.
|
||||
response => {
|
||||
(response) => {
|
||||
this.pushData(oldData);
|
||||
m.lazyRedraw();
|
||||
throw response;
|
||||
@ -192,11 +197,18 @@ export default class Model {
|
||||
delete(data, options = {}) {
|
||||
if (!this.exists) return m.deferred().resolve().promise;
|
||||
|
||||
return app.request(Object.assign({
|
||||
return app
|
||||
.request(
|
||||
Object.assign(
|
||||
{
|
||||
method: 'DELETE',
|
||||
url: app.forum.attribute('apiUrl') + this.apiEndpoint(),
|
||||
data
|
||||
}, options)).then(() => {
|
||||
data,
|
||||
},
|
||||
options
|
||||
)
|
||||
)
|
||||
.then(() => {
|
||||
this.exists = false;
|
||||
this.store.remove(this);
|
||||
});
|
||||
@ -225,7 +237,7 @@ export default class Model {
|
||||
* @public
|
||||
*/
|
||||
static attribute(name, transform) {
|
||||
return function() {
|
||||
return function () {
|
||||
const value = this.data.attributes && this.data.attributes[name];
|
||||
|
||||
return transform ? transform(value) : value;
|
||||
@ -243,7 +255,7 @@ export default class Model {
|
||||
* @public
|
||||
*/
|
||||
static hasOne(name) {
|
||||
return function() {
|
||||
return function () {
|
||||
if (this.data.relationships) {
|
||||
const relationship = this.data.relationships[name];
|
||||
|
||||
@ -267,12 +279,12 @@ export default class Model {
|
||||
* @public
|
||||
*/
|
||||
static hasMany(name) {
|
||||
return function() {
|
||||
return function () {
|
||||
if (this.data.relationships) {
|
||||
const relationship = this.data.relationships[name];
|
||||
|
||||
if (relationship) {
|
||||
return relationship.data.map(data => app.store.getById(data.type, data.id));
|
||||
return relationship.data.map((data) => app.store.getById(data.type, data.id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,7 +313,7 @@ export default class Model {
|
||||
static getIdentifier(model) {
|
||||
return {
|
||||
type: model.data.type,
|
||||
id: model.data.id
|
||||
id: model.data.id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -31,11 +31,16 @@ export default class Session {
|
||||
* @public
|
||||
*/
|
||||
login(data, options = {}) {
|
||||
return app.request(Object.assign({
|
||||
return app.request(
|
||||
Object.assign(
|
||||
{
|
||||
method: 'POST',
|
||||
url: app.forum.attribute('baseUrl') + '/login',
|
||||
data
|
||||
}, options));
|
||||
data,
|
||||
},
|
||||
options
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,9 +34,7 @@ export default class Store {
|
||||
pushPayload(payload) {
|
||||
if (payload.included) payload.included.map(this.pushObject.bind(this));
|
||||
|
||||
const result = payload.data instanceof Array
|
||||
? payload.data.map(this.pushObject.bind(this))
|
||||
: this.pushObject(payload.data);
|
||||
const result = payload.data instanceof Array ? payload.data.map(this.pushObject.bind(this)) : this.pushObject(payload.data);
|
||||
|
||||
// Attach the original payload to the model that we give back. This is
|
||||
// useful to consumers as it allows them to access meta information
|
||||
@ -58,7 +56,7 @@ export default class Store {
|
||||
pushObject(data) {
|
||||
if (!this.models[data.type]) return null;
|
||||
|
||||
const type = this.data[data.type] = this.data[data.type] || {};
|
||||
const type = (this.data[data.type] = this.data[data.type] || {});
|
||||
|
||||
if (type[data.id]) {
|
||||
type[data.id].pushData(data);
|
||||
@ -95,11 +93,18 @@ export default class Store {
|
||||
url += '/' + id;
|
||||
}
|
||||
|
||||
return app.request(Object.assign({
|
||||
return app
|
||||
.request(
|
||||
Object.assign(
|
||||
{
|
||||
method: 'GET',
|
||||
url,
|
||||
data
|
||||
}, options)).then(this.pushPayload.bind(this));
|
||||
data,
|
||||
},
|
||||
options
|
||||
)
|
||||
)
|
||||
.then(this.pushPayload.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,7 +129,7 @@ export default class Store {
|
||||
* @public
|
||||
*/
|
||||
getBy(type, key, value) {
|
||||
return this.all(type).filter(model => model[key]() === value)[0];
|
||||
return this.all(type).filter((model) => model[key]() === value)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,7 +142,7 @@ export default class Store {
|
||||
all(type) {
|
||||
const records = this.data[type];
|
||||
|
||||
return records ? Object.keys(records).map(id => records[id]) : [];
|
||||
return records ? Object.keys(records).map((id) => records[id]) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,6 +165,6 @@ export default class Store {
|
||||
createRecord(type, data = {}) {
|
||||
data.type = data.type || type;
|
||||
|
||||
return new (this.models[type])(data, this);
|
||||
return new this.models[type](data, this);
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ export default class Translator {
|
||||
const hydrated = [];
|
||||
const open = [hydrated];
|
||||
|
||||
translation.forEach(part => {
|
||||
translation.forEach((part) => {
|
||||
const match = part.match(new RegExp('{([a-z0-9_]+)}|<(/?)([a-z0-9_]+)>', 'i'));
|
||||
|
||||
if (match) {
|
||||
@ -77,7 +77,7 @@ export default class Translator {
|
||||
if (match[2]) {
|
||||
open.shift();
|
||||
} else {
|
||||
let tag = input[match[3]] || {tag: match[3], children: []};
|
||||
let tag = input[match[3]] || { tag: match[3], children: [] };
|
||||
open[0].push(tag);
|
||||
open.unshift(tag.children || tag);
|
||||
}
|
||||
@ -87,7 +87,7 @@ export default class Translator {
|
||||
}
|
||||
});
|
||||
|
||||
return hydrated.filter(part => part);
|
||||
return hydrated.filter((part) => part);
|
||||
}
|
||||
|
||||
pluralize(translation, number) {
|
||||
@ -97,7 +97,7 @@ export default class Translator {
|
||||
standardRules = [],
|
||||
explicitRules = [];
|
||||
|
||||
translation.split('|').forEach(part => {
|
||||
translation.split('|').forEach((part) => {
|
||||
if (cPluralRegex.test(part)) {
|
||||
const matches = part.match(cPluralRegex);
|
||||
explicitRules[matches[0]] = matches[matches.length - 1];
|
||||
@ -125,8 +125,10 @@ export default class Translator {
|
||||
var leftNumber = this.convertNumber(matches[4]);
|
||||
var rightNumber = this.convertNumber(matches[5]);
|
||||
|
||||
if (('[' === matches[3] ? number >= leftNumber : number > leftNumber) &&
|
||||
(']' === matches[6] ? number <= rightNumber : number < rightNumber)) {
|
||||
if (
|
||||
('[' === matches[3] ? number >= leftNumber : number > leftNumber) &&
|
||||
(']' === matches[6] ? number <= rightNumber : number < rightNumber)
|
||||
) {
|
||||
return explicitRules[e];
|
||||
}
|
||||
}
|
||||
@ -223,7 +225,7 @@ export default class Translator {
|
||||
case 'tr':
|
||||
case 'ur':
|
||||
case 'zu':
|
||||
return (number == 1) ? 0 : 1;
|
||||
return number == 1 ? 0 : 1;
|
||||
|
||||
case 'am':
|
||||
case 'bh':
|
||||
@ -237,7 +239,7 @@ export default class Translator {
|
||||
case 'xbr':
|
||||
case 'ti':
|
||||
case 'wa':
|
||||
return ((number === 0) || (number == 1)) ? 0 : 1;
|
||||
return number === 0 || number == 1 ? 0 : 1;
|
||||
|
||||
case 'be':
|
||||
case 'bs':
|
||||
@ -245,41 +247,41 @@ export default class Translator {
|
||||
case 'ru':
|
||||
case 'sr':
|
||||
case 'uk':
|
||||
return ((number % 10 == 1) && (number % 100 != 11)) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2);
|
||||
return number % 10 == 1 && number % 100 != 11 ? 0 : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20) ? 1 : 2;
|
||||
|
||||
case 'cs':
|
||||
case 'sk':
|
||||
return (number == 1) ? 0 : (((number >= 2) && (number <= 4)) ? 1 : 2);
|
||||
return number == 1 ? 0 : number >= 2 && number <= 4 ? 1 : 2;
|
||||
|
||||
case 'ga':
|
||||
return (number == 1) ? 0 : ((number == 2) ? 1 : 2);
|
||||
return number == 1 ? 0 : number == 2 ? 1 : 2;
|
||||
|
||||
case 'lt':
|
||||
return ((number % 10 == 1) && (number % 100 != 11)) ? 0 : (((number % 10 >= 2) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2);
|
||||
return number % 10 == 1 && number % 100 != 11 ? 0 : number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20) ? 1 : 2;
|
||||
|
||||
case 'sl':
|
||||
return (number % 100 == 1) ? 0 : ((number % 100 == 2) ? 1 : (((number % 100 == 3) || (number % 100 == 4)) ? 2 : 3));
|
||||
return number % 100 == 1 ? 0 : number % 100 == 2 ? 1 : number % 100 == 3 || number % 100 == 4 ? 2 : 3;
|
||||
|
||||
case 'mk':
|
||||
return (number % 10 == 1) ? 0 : 1;
|
||||
return number % 10 == 1 ? 0 : 1;
|
||||
|
||||
case 'mt':
|
||||
return (number == 1) ? 0 : (((number === 0) || ((number % 100 > 1) && (number % 100 < 11))) ? 1 : (((number % 100 > 10) && (number % 100 < 20)) ? 2 : 3));
|
||||
return number == 1 ? 0 : number === 0 || (number % 100 > 1 && number % 100 < 11) ? 1 : number % 100 > 10 && number % 100 < 20 ? 2 : 3;
|
||||
|
||||
case 'lv':
|
||||
return (number === 0) ? 0 : (((number % 10 == 1) && (number % 100 != 11)) ? 1 : 2);
|
||||
return number === 0 ? 0 : number % 10 == 1 && number % 100 != 11 ? 1 : 2;
|
||||
|
||||
case 'pl':
|
||||
return (number == 1) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 12) || (number % 100 > 14))) ? 1 : 2);
|
||||
return number == 1 ? 0 : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14) ? 1 : 2;
|
||||
|
||||
case 'cy':
|
||||
return (number == 1) ? 0 : ((number == 2) ? 1 : (((number == 8) || (number == 11)) ? 2 : 3));
|
||||
return number == 1 ? 0 : number == 2 ? 1 : number == 8 || number == 11 ? 2 : 3;
|
||||
|
||||
case 'ro':
|
||||
return (number == 1) ? 0 : (((number === 0) || ((number % 100 > 0) && (number % 100 < 20))) ? 1 : 2);
|
||||
return number == 1 ? 0 : number === 0 || (number % 100 > 0 && number % 100 < 20) ? 1 : 2;
|
||||
|
||||
case 'ar':
|
||||
return (number === 0) ? 0 : ((number == 1) ? 1 : ((number == 2) ? 2 : (((number >= 3) && (number <= 10)) ? 3 : (((number >= 11) && (number <= 99)) ? 4 : 5))));
|
||||
return number === 0 ? 0 : number == 1 ? 1 : number == 2 ? 2 : number >= 3 && number <= 10 ? 3 : number >= 11 && number <= 99 ? 4 : 5;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
|
@ -62,9 +62,9 @@ import userOnline from './helpers/userOnline';
|
||||
import listItems from './helpers/listItems';
|
||||
|
||||
export default {
|
||||
'extend': extend,
|
||||
'Session': Session,
|
||||
'Store': Store,
|
||||
extend: extend,
|
||||
Session: Session,
|
||||
Store: Store,
|
||||
'utils/evented': evented,
|
||||
'utils/liveHumanTimes': liveHumanTimes,
|
||||
'utils/ItemList': ItemList,
|
||||
@ -91,8 +91,8 @@ export default {
|
||||
'models/Discussion': Discussion,
|
||||
'models/Group': Group,
|
||||
'models/Forum': Forum,
|
||||
'Component': Component,
|
||||
'Translator': Translator,
|
||||
Component: Component,
|
||||
Translator: Translator,
|
||||
'components/AlertManager': AlertManager,
|
||||
'components/Switch': Switch,
|
||||
'components/Badge': Badge,
|
||||
@ -113,8 +113,8 @@ export default {
|
||||
'components/Button': Button,
|
||||
'components/Modal': Modal,
|
||||
'components/GroupBadge': GroupBadge,
|
||||
'Model': Model,
|
||||
'Application': Application,
|
||||
Model: Model,
|
||||
Application: Application,
|
||||
'helpers/fullTime': fullTime,
|
||||
'helpers/avatar': avatar,
|
||||
'helpers/icon': icon,
|
||||
@ -123,5 +123,5 @@ export default {
|
||||
'helpers/highlight': highlight,
|
||||
'helpers/username': username,
|
||||
'helpers/userOnline': userOnline,
|
||||
'helpers/listItems': listItems
|
||||
'helpers/listItems': listItems,
|
||||
};
|
||||
|
@ -35,22 +35,13 @@ export default class Alert extends Component {
|
||||
const dismissControl = [];
|
||||
|
||||
if (dismissible || dismissible === undefined) {
|
||||
dismissControl.push(
|
||||
<Button
|
||||
icon="fas fa-times"
|
||||
className="Button Button--link Button--icon Alert-dismiss"
|
||||
onclick={ondismiss}/>
|
||||
);
|
||||
dismissControl.push(<Button icon="fas fa-times" className="Button Button--link Button--icon Alert-dismiss" onclick={ondismiss} />);
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...attrs}>
|
||||
<span className="Alert-body">
|
||||
{children}
|
||||
</span>
|
||||
<ul className="Alert-controls">
|
||||
{listItems(controls.concat(dismissControl))}
|
||||
</ul>
|
||||
<span className="Alert-body">{children}</span>
|
||||
<ul className="Alert-controls">{listItems(controls.concat(dismissControl))}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ export default class AlertManager extends Component {
|
||||
view() {
|
||||
return (
|
||||
<div className="AlertManager">
|
||||
{this.components.map(component => <div className="AlertManager-alert">{component}</div>)}
|
||||
{this.components.map((component) => (
|
||||
<div className="AlertManager-alert">{component}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -24,16 +24,12 @@ export default class Badge extends Component {
|
||||
attrs.className = 'Badge ' + (type ? 'Badge--' + type : '') + ' ' + (attrs.className || '');
|
||||
attrs.title = extract(attrs, 'label') || '';
|
||||
|
||||
return (
|
||||
<span {...attrs}>
|
||||
{iconName ? icon(iconName, {className: 'Badge-icon'}) : m.trust(' ')}
|
||||
</span>
|
||||
);
|
||||
return <span {...attrs}>{iconName ? icon(iconName, { className: 'Badge-icon' }) : m.trust(' ')}</span>;
|
||||
}
|
||||
|
||||
config(isInitialized) {
|
||||
if (isInitialized) return;
|
||||
|
||||
if (this.props.label) this.$().tooltip({container: 'body'});
|
||||
if (this.props.label) this.$().tooltip({ container: 'body' });
|
||||
}
|
||||
}
|
||||
|
@ -62,9 +62,9 @@ export default class Button extends Component {
|
||||
const iconName = this.props.icon;
|
||||
|
||||
return [
|
||||
iconName && iconName !== true ? icon(iconName, {className: 'Button-icon'}) : '',
|
||||
iconName && iconName !== true ? icon(iconName, { className: 'Button-icon' }) : '',
|
||||
this.props.children ? <span className="Button-label">{this.props.children}</span> : '',
|
||||
this.props.loading ? LoadingIndicator.component({size: 'tiny', className: 'LoadingIndicator--inline'}) : ''
|
||||
this.props.loading ? LoadingIndicator.component({ size: 'tiny', className: 'LoadingIndicator--inline' }) : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -31,13 +31,8 @@ export default class Checkbox extends Component {
|
||||
|
||||
return (
|
||||
<label className={className}>
|
||||
<input type="checkbox"
|
||||
checked={this.props.state}
|
||||
disabled={this.props.disabled}
|
||||
onchange={m.withAttr('checked', this.onchange.bind(this))}/>
|
||||
<div className="Checkbox-display">
|
||||
{this.getDisplay()}
|
||||
</div>
|
||||
<input type="checkbox" checked={this.props.state} disabled={this.props.disabled} onchange={m.withAttr('checked', this.onchange.bind(this))} />
|
||||
<div className="Checkbox-display">{this.getDisplay()}</div>
|
||||
{this.props.children}
|
||||
</label>
|
||||
);
|
||||
@ -50,9 +45,7 @@ export default class Checkbox extends Component {
|
||||
* @protected
|
||||
*/
|
||||
getDisplay() {
|
||||
return this.loading
|
||||
? LoadingIndicator.component({size: 'tiny'})
|
||||
: icon(this.props.state ? 'fas fa-check' : 'fas fa-times');
|
||||
return this.loading ? LoadingIndicator.component({ size: 'tiny' }) : icon(this.props.state ? 'fas fa-check' : 'fas fa-times');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,19 +64,13 @@ export default class Dropdown extends Component {
|
||||
|
||||
$menu.removeClass('Dropdown-menu--top Dropdown-menu--right');
|
||||
|
||||
$menu.toggleClass(
|
||||
'Dropdown-menu--top',
|
||||
$menu.offset().top + $menu.height() > $(window).scrollTop() + $(window).height()
|
||||
);
|
||||
$menu.toggleClass('Dropdown-menu--top', $menu.offset().top + $menu.height() > $(window).scrollTop() + $(window).height());
|
||||
|
||||
if ($menu.offset().top < 0) {
|
||||
$menu.removeClass('Dropdown-menu--top');
|
||||
}
|
||||
|
||||
$menu.toggleClass(
|
||||
'Dropdown-menu--right',
|
||||
isRight || $menu.offset().left + $menu.width() > $(window).scrollLeft() + $(window).width()
|
||||
);
|
||||
$menu.toggleClass('Dropdown-menu--right', isRight || $menu.offset().left + $menu.width() > $(window).scrollLeft() + $(window).width());
|
||||
});
|
||||
|
||||
this.$().on('hidden.bs.dropdown', () => {
|
||||
@ -98,10 +92,7 @@ export default class Dropdown extends Component {
|
||||
*/
|
||||
getButton() {
|
||||
return (
|
||||
<button
|
||||
className={'Dropdown-toggle ' + this.props.buttonClassName}
|
||||
data-toggle="dropdown"
|
||||
onclick={this.props.onclick}>
|
||||
<button className={'Dropdown-toggle ' + this.props.buttonClassName} data-toggle="dropdown" onclick={this.props.onclick}>
|
||||
{this.getButtonContent()}
|
||||
</button>
|
||||
);
|
||||
@ -115,17 +106,13 @@ export default class Dropdown extends Component {
|
||||
*/
|
||||
getButtonContent() {
|
||||
return [
|
||||
this.props.icon ? icon(this.props.icon, {className: 'Button-icon'}) : '',
|
||||
this.props.icon ? icon(this.props.icon, { className: 'Button-icon' }) : '',
|
||||
<span className="Button-label">{this.props.label}</span>,
|
||||
this.props.caretIcon ? icon(this.props.caretIcon, {className: 'Button-caret'}) : ''
|
||||
this.props.caretIcon ? icon(this.props.caretIcon, { className: 'Button-caret' }) : '',
|
||||
];
|
||||
}
|
||||
|
||||
getMenu(items) {
|
||||
return (
|
||||
<ul className={'Dropdown-menu dropdown-menu ' + this.props.menuClassName}>
|
||||
{items}
|
||||
</ul>
|
||||
);
|
||||
return <ul className={'Dropdown-menu dropdown-menu ' + this.props.menuClassName}>{items}</ul>;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ export default class GroupBadge extends Badge {
|
||||
|
||||
if (props.group) {
|
||||
props.icon = props.group.icon();
|
||||
props.style = {backgroundColor: props.group.color()};
|
||||
props.style = { backgroundColor: props.group.color() };
|
||||
props.label = typeof props.label === 'undefined' ? props.group.nameSingular() : props.label;
|
||||
props.type = 'group--' + props.group.id();
|
||||
|
||||
|
@ -33,8 +33,6 @@ export default class LinkButton extends Button {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
static isActive(props) {
|
||||
return typeof props.active !== 'undefined'
|
||||
? props.active
|
||||
: m.route() === props.href;
|
||||
return typeof props.active !== 'undefined' ? props.active : m.route() === props.href;
|
||||
}
|
||||
}
|
||||
|
@ -31,10 +31,12 @@ export default class Modal extends Component {
|
||||
{Button.component({
|
||||
icon: 'fas fa-times',
|
||||
onclick: this.hide.bind(this),
|
||||
className: 'Button Button--icon Button--link'
|
||||
className: 'Button Button--icon Button--link',
|
||||
})}
|
||||
</div>
|
||||
) : ''}
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
<form onsubmit={this.onsubmit.bind(this)}>
|
||||
<div className="Modal-header">
|
||||
@ -65,8 +67,7 @@ export default class Modal extends Component {
|
||||
* @return {String}
|
||||
* @abstract
|
||||
*/
|
||||
className() {
|
||||
}
|
||||
className() {}
|
||||
|
||||
/**
|
||||
* Get the title of the modal dialog.
|
||||
@ -74,8 +75,7 @@ export default class Modal extends Component {
|
||||
* @return {String}
|
||||
* @abstract
|
||||
*/
|
||||
title() {
|
||||
}
|
||||
title() {}
|
||||
|
||||
/**
|
||||
* Get the content of the modal.
|
||||
@ -83,16 +83,14 @@ export default class Modal extends Component {
|
||||
* @return {VirtualElement}
|
||||
* @abstract
|
||||
*/
|
||||
content() {
|
||||
}
|
||||
content() {}
|
||||
|
||||
/**
|
||||
* Handle the modal form's submit event.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
onsubmit() {
|
||||
}
|
||||
onsubmit() {}
|
||||
|
||||
/**
|
||||
* Focus on the first input when the modal is ready to be used.
|
||||
@ -101,8 +99,7 @@ export default class Modal extends Component {
|
||||
this.$('form').find('input, select, textarea').first().focus().select();
|
||||
}
|
||||
|
||||
onhide() {
|
||||
}
|
||||
onhide() {}
|
||||
|
||||
/**
|
||||
* Hide the modal.
|
||||
|
@ -13,11 +13,7 @@ export default class ModalManager extends Component {
|
||||
}
|
||||
|
||||
view() {
|
||||
return (
|
||||
<div className="ModalManager modal fade">
|
||||
{this.component && this.component.render()}
|
||||
</div>
|
||||
);
|
||||
return <div className="ModalManager modal fade">{this.component && this.component.render()}</div>;
|
||||
}
|
||||
|
||||
config(isInitialized, context) {
|
||||
@ -28,9 +24,7 @@ export default class ModalManager extends Component {
|
||||
// to be retained across route changes.
|
||||
context.retain = true;
|
||||
|
||||
this.$()
|
||||
.on('hidden.bs.modal', this.clear.bind(this))
|
||||
.on('shown.bs.modal', this.onready.bind(this));
|
||||
this.$().on('hidden.bs.modal', this.clear.bind(this)).on('shown.bs.modal', this.onready.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,10 +48,12 @@ export default class ModalManager extends Component {
|
||||
m.redraw(true);
|
||||
|
||||
const dismissible = !!this.component.isDismissible();
|
||||
this.$().modal({
|
||||
this.$()
|
||||
.modal({
|
||||
backdrop: dismissible || 'static',
|
||||
keyboard: dismissible
|
||||
}).modal('show');
|
||||
keyboard: dismissible,
|
||||
})
|
||||
.modal('show');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,15 +19,15 @@ import LinkButton from './LinkButton';
|
||||
*/
|
||||
export default class Navigation extends Component {
|
||||
view() {
|
||||
const {history, pane} = app;
|
||||
const { history, pane } = app;
|
||||
|
||||
return (
|
||||
<div className={'Navigation ButtonGroup ' + (this.props.className || '')}
|
||||
<div
|
||||
className={'Navigation ButtonGroup ' + (this.props.className || '')}
|
||||
onmouseenter={pane && pane.show.bind(pane)}
|
||||
onmouseleave={pane && pane.onmouseleave.bind(pane)}>
|
||||
{history.canGoBack()
|
||||
? [this.getBackButton(), this.getPaneButton()]
|
||||
: this.getDrawerButton()}
|
||||
onmouseleave={pane && pane.onmouseleave.bind(pane)}
|
||||
>
|
||||
{history.canGoBack() ? [this.getBackButton(), this.getPaneButton()] : this.getDrawerButton()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -46,7 +46,7 @@ export default class Navigation extends Component {
|
||||
* @protected
|
||||
*/
|
||||
getBackButton() {
|
||||
const {history} = app;
|
||||
const { history } = app;
|
||||
const previous = history.getPrevious() || {};
|
||||
|
||||
return LinkButton.component({
|
||||
@ -55,11 +55,11 @@ export default class Navigation extends Component {
|
||||
icon: 'fas fa-chevron-left',
|
||||
title: previous.title,
|
||||
config: () => {},
|
||||
onclick: e => {
|
||||
onclick: (e) => {
|
||||
if (e.shiftKey || e.ctrlKey || e.metaKey || e.which === 2) return;
|
||||
e.preventDefault();
|
||||
history.back();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -70,14 +70,14 @@ export default class Navigation extends Component {
|
||||
* @protected
|
||||
*/
|
||||
getPaneButton() {
|
||||
const {pane} = app;
|
||||
const { pane } = app;
|
||||
|
||||
if (!pane || !pane.active) return '';
|
||||
|
||||
return Button.component({
|
||||
className: 'Button Button--icon Navigation-pin' + (pane.pinned ? ' active' : ''),
|
||||
onclick: pane.togglePinned.bind(pane),
|
||||
icon: 'fas fa-thumbtack'
|
||||
icon: 'fas fa-thumbtack',
|
||||
});
|
||||
}
|
||||
|
||||
@ -90,17 +90,16 @@ export default class Navigation extends Component {
|
||||
getDrawerButton() {
|
||||
if (!this.props.drawer) return '';
|
||||
|
||||
const {drawer} = app;
|
||||
const { drawer } = app;
|
||||
const user = app.session.user;
|
||||
|
||||
return Button.component({
|
||||
className: 'Button Button--icon Navigation-drawer' +
|
||||
(user && user.newNotificationCount() ? ' new' : ''),
|
||||
onclick: e => {
|
||||
className: 'Button Button--icon Navigation-drawer' + (user && user.newNotificationCount() ? ' new' : ''),
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
drawer.show();
|
||||
},
|
||||
icon: 'fas fa-bars'
|
||||
icon: 'fas fa-bars',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,7 @@ export default class RequestErrorModal extends Modal {
|
||||
}
|
||||
|
||||
title() {
|
||||
return this.props.error.xhr
|
||||
? this.props.error.xhr.status+' '+this.props.error.xhr.statusText
|
||||
: '';
|
||||
return this.props.error.xhr ? this.props.error.xhr.status + ' ' + this.props.error.xhr.statusText : '';
|
||||
}
|
||||
|
||||
content() {
|
||||
@ -20,11 +18,15 @@ export default class RequestErrorModal extends Modal {
|
||||
responseText = this.props.error.responseText;
|
||||
}
|
||||
|
||||
return <div className="Modal-body">
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<pre>
|
||||
{this.props.error.options.method} {this.props.error.options.url}<br/><br/>
|
||||
{this.props.error.options.method} {this.props.error.options.url}
|
||||
<br />
|
||||
<br />
|
||||
{responseText}
|
||||
</pre>
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -12,14 +12,21 @@ import icon from '../helpers/icon';
|
||||
*/
|
||||
export default class Select extends Component {
|
||||
view() {
|
||||
const {options, onchange, value, disabled} = this.props;
|
||||
const { options, onchange, value, disabled } = this.props;
|
||||
|
||||
return (
|
||||
<span className="Select">
|
||||
<select className="Select-input FormControl" onchange={onchange ? m.withAttr('value', onchange.bind(this)) : undefined} value={value} disabled={disabled}>
|
||||
{Object.keys(options).map(key => <option value={key}>{options[key]}</option>)}
|
||||
<select
|
||||
className="Select-input FormControl"
|
||||
onchange={onchange ? m.withAttr('value', onchange.bind(this)) : undefined}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
>
|
||||
{Object.keys(options).map((key) => (
|
||||
<option value={key}>{options[key]}</option>
|
||||
))}
|
||||
</select>
|
||||
{icon('fas fa-sort', {className: 'Select-caret'})}
|
||||
{icon('fas fa-sort', { className: 'Select-caret' })}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -21,14 +21,11 @@ export default class SelectDropdown extends Dropdown {
|
||||
}
|
||||
|
||||
getButtonContent() {
|
||||
const activeChild = this.props.children.filter(child => child.props.active)[0];
|
||||
let label = activeChild && activeChild.props.children || this.props.defaultLabel;
|
||||
const activeChild = this.props.children.filter((child) => child.props.active)[0];
|
||||
let label = (activeChild && activeChild.props.children) || this.props.defaultLabel;
|
||||
|
||||
if (label instanceof Array) label = label[0];
|
||||
|
||||
return [
|
||||
<span className="Button-label">{label}</span>,
|
||||
icon(this.props.caretIcon, {className: 'Button-caret'})
|
||||
];
|
||||
return [<span className="Button-label">{label}</span>, icon(this.props.caretIcon, { className: 'Button-caret' })];
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import Component from '../Component';
|
||||
*/
|
||||
class Separator extends Component {
|
||||
view() {
|
||||
return <li className="Dropdown-separator"/>;
|
||||
return <li className="Dropdown-separator" />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,12 +24,10 @@ export default class SplitDropdown extends Dropdown {
|
||||
|
||||
return [
|
||||
Button.component(buttonProps),
|
||||
<button
|
||||
className={'Dropdown-toggle Button Button--icon ' + this.props.buttonClassName}
|
||||
data-toggle="dropdown">
|
||||
{icon(this.props.icon, {className: 'Button-icon'})}
|
||||
{icon('fas fa-caret-down', {className: 'Button-caret'})}
|
||||
</button>
|
||||
<button className={'Dropdown-toggle Button Button--icon ' + this.props.buttonClassName} data-toggle="dropdown">
|
||||
{icon(this.props.icon, { className: 'Button-icon' })}
|
||||
{icon('fas fa-caret-down', { className: 'Button-caret' })}
|
||||
</button>,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
export function extend(object, method, callback) {
|
||||
const original = object[method];
|
||||
|
||||
object[method] = function(...args) {
|
||||
object[method] = function (...args) {
|
||||
const value = original ? original.apply(this, args) : undefined;
|
||||
|
||||
callback.apply(this, [value].concat(args));
|
||||
@ -57,7 +57,7 @@ export function extend(object, method, callback) {
|
||||
export function override(object, method, newMethod) {
|
||||
const original = object[method];
|
||||
|
||||
object[method] = function(...args) {
|
||||
object[method] = function (...args) {
|
||||
return newMethod.apply(this, [original.bind(this)].concat(args));
|
||||
};
|
||||
|
||||
|
@ -34,8 +34,8 @@ export default class Routes {
|
||||
|
||||
const model = app.store.models[this.type];
|
||||
|
||||
this.attributes.forEach(name => model.prototype[name] = model.attribute(name));
|
||||
this.hasOnes.forEach(name => model.prototype[name] = model.hasOne(name));
|
||||
this.hasManys.forEach(name => model.prototype[name] = model.hasMany(name));
|
||||
this.attributes.forEach((name) => (model.prototype[name] = model.attribute(name)));
|
||||
this.hasOnes.forEach((name) => (model.prototype[name] = model.hasOne(name)));
|
||||
this.hasManys.forEach((name) => (model.prototype[name] = model.hasMany(name)));
|
||||
}
|
||||
}
|
@ -25,11 +25,11 @@ export default function avatar(user, attrs = {}) {
|
||||
if (hasTitle) attrs.title = attrs.title || username;
|
||||
|
||||
if (avatarUrl) {
|
||||
return <img {...attrs} src={avatarUrl}/>;
|
||||
return <img {...attrs} src={avatarUrl} />;
|
||||
}
|
||||
|
||||
content = username.charAt(0).toUpperCase();
|
||||
attrs.style = {background: user.color()};
|
||||
attrs.style = { background: user.color() };
|
||||
}
|
||||
|
||||
return <span {...attrs}>{content}</span>;
|
||||
|
@ -11,5 +11,9 @@ export default function fullTime(time) {
|
||||
const datetime = mo.format();
|
||||
const full = mo.format('LLLL');
|
||||
|
||||
return <time pubdate datetime={datetime}>{full}</time>;
|
||||
return (
|
||||
<time pubdate datetime={datetime}>
|
||||
{full}
|
||||
</time>
|
||||
);
|
||||
}
|
||||
|
@ -15,5 +15,9 @@ export default function humanTime(time) {
|
||||
const full = mo.format('LLLL');
|
||||
const ago = humanTimeUtil(time);
|
||||
|
||||
return <time pubdate datetime={datetime} title={full} data-humantime>{ago}</time>;
|
||||
return (
|
||||
<time pubdate datetime={datetime} title={full} data-humantime>
|
||||
{ago}
|
||||
</time>
|
||||
);
|
||||
}
|
||||
|
@ -8,5 +8,5 @@
|
||||
export default function icon(fontClass, attrs = {}) {
|
||||
attrs.className = 'icon ' + fontClass + ' ' + (attrs.className || '');
|
||||
|
||||
return <i {...attrs}/>;
|
||||
return <i {...attrs} />;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ function withoutUnnecessarySeparators(items) {
|
||||
export default function listItems(items) {
|
||||
if (!(items instanceof Array)) items = [items];
|
||||
|
||||
return withoutUnnecessarySeparators(items).map(item => {
|
||||
return withoutUnnecessarySeparators(items).map((item) => {
|
||||
const isListItem = item.component && item.component.isListItem;
|
||||
const active = item.component && item.component.isActive && item.component.isActive(item.props);
|
||||
const className = item.props ? item.props.itemClassName : item.itemClassName;
|
||||
@ -39,15 +39,12 @@ export default function listItems(items) {
|
||||
item.attrs.key = item.attrs.key || item.itemName;
|
||||
}
|
||||
|
||||
return isListItem
|
||||
? item
|
||||
: <li className={classList([
|
||||
(item.itemName ? 'item-' + item.itemName : ''),
|
||||
className,
|
||||
(active ? 'active' : '')
|
||||
])}
|
||||
key={item.itemName}>
|
||||
return isListItem ? (
|
||||
item
|
||||
) : (
|
||||
<li className={classList([item.itemName ? 'item-' + item.itemName : '', className, active ? 'active' : ''])} key={item.itemName}>
|
||||
{item}
|
||||
</li>;
|
||||
</li>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ export default function punctuateSeries(items) {
|
||||
if (items.length === 2) {
|
||||
return app.translator.trans('core.lib.series.two_text', {
|
||||
first: items[0],
|
||||
second: items[1]
|
||||
second: items[1],
|
||||
});
|
||||
} else if (items.length >= 3) {
|
||||
// If there are three or more items, we will join all but the first and
|
||||
@ -27,7 +27,7 @@ export default function punctuateSeries(items) {
|
||||
return app.translator.trans('core.lib.series.three_text', {
|
||||
first: items[0],
|
||||
second,
|
||||
third: items[items.length - 1]
|
||||
third: items[items.length - 1],
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -19,18 +19,18 @@ Object.assign(Discussion.prototype, {
|
||||
lastPostNumber: Model.attribute('lastPostNumber'),
|
||||
|
||||
commentCount: Model.attribute('commentCount'),
|
||||
replyCount: computed('commentCount', commentCount => Math.max(0, commentCount - 1)),
|
||||
replyCount: computed('commentCount', (commentCount) => Math.max(0, commentCount - 1)),
|
||||
posts: Model.hasMany('posts'),
|
||||
mostRelevantPost: Model.hasOne('mostRelevantPost'),
|
||||
|
||||
lastReadAt: Model.attribute('lastReadAt', Model.transformDate),
|
||||
lastReadPostNumber: Model.attribute('lastReadPostNumber'),
|
||||
isUnread: computed('unreadCount', unreadCount => !!unreadCount),
|
||||
isRead: computed('unreadCount', unreadCount => app.session.user && !unreadCount),
|
||||
isUnread: computed('unreadCount', (unreadCount) => !!unreadCount),
|
||||
isRead: computed('unreadCount', (unreadCount) => app.session.user && !unreadCount),
|
||||
|
||||
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
||||
hiddenUser: Model.hasOne('hiddenUser'),
|
||||
isHidden: computed('hiddenAt', hiddenAt => !!hiddenAt),
|
||||
isHidden: computed('hiddenAt', (hiddenAt) => !!hiddenAt),
|
||||
|
||||
canReply: Model.attribute('canReply'),
|
||||
canRename: Model.attribute('canRename'),
|
||||
@ -84,7 +84,7 @@ Object.assign(Discussion.prototype, {
|
||||
const items = new ItemList();
|
||||
|
||||
if (this.isHidden()) {
|
||||
items.add('hidden', <Badge type="hidden" icon="fas fa-trash" label={app.translator.trans('core.lib.badge.hidden_tooltip')}/>);
|
||||
items.add('hidden', <Badge type="hidden" icon="fas fa-trash" label={app.translator.trans('core.lib.badge.hidden_tooltip')} />);
|
||||
}
|
||||
|
||||
return items;
|
||||
@ -99,6 +99,6 @@ Object.assign(Discussion.prototype, {
|
||||
postIds() {
|
||||
const posts = this.data.relationships.posts;
|
||||
|
||||
return posts ? posts.data.map(link => link.id) : [];
|
||||
}
|
||||
return posts ? posts.data.map((link) => link.id) : [];
|
||||
},
|
||||
});
|
||||
|
@ -6,7 +6,7 @@ Object.assign(Group.prototype, {
|
||||
nameSingular: Model.attribute('nameSingular'),
|
||||
namePlural: Model.attribute('namePlural'),
|
||||
color: Model.attribute('color'),
|
||||
icon: Model.attribute('icon')
|
||||
icon: Model.attribute('icon'),
|
||||
});
|
||||
|
||||
Group.ADMINISTRATOR_ID = '1';
|
||||
|
@ -11,5 +11,5 @@ Object.assign(Notification.prototype, {
|
||||
|
||||
user: Model.hasOne('user'),
|
||||
fromUser: Model.hasOne('fromUser'),
|
||||
subject: Model.hasOne('subject')
|
||||
subject: Model.hasOne('subject'),
|
||||
});
|
||||
|
@ -17,13 +17,13 @@ Object.assign(Post.prototype, {
|
||||
|
||||
editedAt: Model.attribute('editedAt', Model.transformDate),
|
||||
editedUser: Model.hasOne('editedUser'),
|
||||
isEdited: computed('editedAt', editedAt => !!editedAt),
|
||||
isEdited: computed('editedAt', (editedAt) => !!editedAt),
|
||||
|
||||
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
||||
hiddenUser: Model.hasOne('hiddenUser'),
|
||||
isHidden: computed('hiddenAt', hiddenAt => !!hiddenAt),
|
||||
isHidden: computed('hiddenAt', (hiddenAt) => !!hiddenAt),
|
||||
|
||||
canEdit: Model.attribute('canEdit'),
|
||||
canHide: Model.attribute('canHide'),
|
||||
canDelete: Model.attribute('canDelete')
|
||||
canDelete: Model.attribute('canDelete'),
|
||||
});
|
||||
|
@ -32,7 +32,7 @@ Object.assign(User.prototype, {
|
||||
canDelete: Model.attribute('canDelete'),
|
||||
|
||||
avatarColor: null,
|
||||
color: computed('username', 'avatarUrl', 'avatarColor', function(username, avatarUrl, avatarColor) {
|
||||
color: computed('username', 'avatarUrl', 'avatarColor', function (username, avatarUrl, avatarColor) {
|
||||
// If we've already calculated and cached the dominant color of the user's
|
||||
// avatar, then we can return that in RGB format. If we haven't, we'll want
|
||||
// to calculate it. Unless the user doesn't have an avatar, in which case
|
||||
@ -67,8 +67,8 @@ Object.assign(User.prototype, {
|
||||
const groups = this.groups();
|
||||
|
||||
if (groups) {
|
||||
groups.forEach(group => {
|
||||
items.add('group' + group.id(), GroupBadge.component({group}));
|
||||
groups.forEach((group) => {
|
||||
items.add('group' + group.id(), GroupBadge.component({ group }));
|
||||
});
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ Object.assign(User.prototype, {
|
||||
const image = new Image();
|
||||
const user = this;
|
||||
|
||||
image.onload = function() {
|
||||
image.onload = function () {
|
||||
const colorThief = new ColorThief();
|
||||
user.avatarColor = colorThief.getColor(this);
|
||||
user.freshness = new Date();
|
||||
@ -106,6 +106,6 @@ Object.assign(User.prototype, {
|
||||
|
||||
Object.assign(preferences, newPreferences);
|
||||
|
||||
return this.save({preferences});
|
||||
}
|
||||
return this.save({ preferences });
|
||||
},
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ export default class Drawer {
|
||||
constructor() {
|
||||
// Set up an event handler so that whenever the content area is tapped,
|
||||
// the drawer will close.
|
||||
$('#content').click(e => {
|
||||
$('#content').click((e) => {
|
||||
if (this.isOpen()) {
|
||||
e.preventDefault();
|
||||
this.hide();
|
||||
|
@ -28,7 +28,7 @@ export default class ItemList {
|
||||
*/
|
||||
isEmpty() {
|
||||
for (const i in this.items) {
|
||||
if(this.items.hasOwnProperty(i)) {
|
||||
if (this.items.hasOwnProperty(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -147,14 +147,15 @@ export default class ItemList {
|
||||
}
|
||||
}
|
||||
|
||||
return items.sort((a, b) => {
|
||||
return items
|
||||
.sort((a, b) => {
|
||||
if (a.priority === b.priority) {
|
||||
return a.key - b.key;
|
||||
} else if (a.priority > b.priority) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}).map(item => item.content);
|
||||
})
|
||||
.map((item) => item.content);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
const later = window.requestAnimationFrame ||
|
||||
const later =
|
||||
window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
(callback => window.setTimeout(callback, 1000 / 60));
|
||||
((callback) => window.setTimeout(callback, 1000 / 60));
|
||||
|
||||
/**
|
||||
* The `ScrollListener` class sets up a listener that handles window scroll
|
||||
@ -57,10 +58,7 @@ export default class ScrollListener {
|
||||
*/
|
||||
start() {
|
||||
if (!this.active) {
|
||||
window.addEventListener(
|
||||
'scroll',
|
||||
this.active = this.loop.bind(this)
|
||||
);
|
||||
window.addEventListener('scroll', (this.active = this.loop.bind(this)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ export default class SubtreeRetainer {
|
||||
}
|
||||
});
|
||||
|
||||
return needsRebuild ? false : {subtree: 'retain'};
|
||||
return needsRebuild ? false : { subtree: 'retain' };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,7 @@ export default function classList(classes) {
|
||||
let classNames;
|
||||
|
||||
if (classes instanceof Array) {
|
||||
classNames = classes.filter(name => name);
|
||||
classNames = classes.filter((name) => name);
|
||||
} else {
|
||||
classNames = [];
|
||||
|
||||
|
@ -14,12 +14,12 @@ export default function computed(...dependentKeys) {
|
||||
const dependentValues = {};
|
||||
let computedValue;
|
||||
|
||||
return function() {
|
||||
return function () {
|
||||
let recompute = false;
|
||||
|
||||
// Read all of the dependent values. If any of them have changed since last
|
||||
// time, then we'll want to recompute our output.
|
||||
keys.forEach(key => {
|
||||
keys.forEach((key) => {
|
||||
const value = typeof this[key] === 'function' ? this[key]() : this[key];
|
||||
|
||||
if (dependentValues[key] !== value) {
|
||||
@ -29,7 +29,10 @@ export default function computed(...dependentKeys) {
|
||||
});
|
||||
|
||||
if (recompute) {
|
||||
computedValue = compute.apply(this, keys.map(key => dependentValues[key]));
|
||||
computedValue = compute.apply(
|
||||
this,
|
||||
keys.map((key) => dependentValues[key])
|
||||
);
|
||||
}
|
||||
|
||||
return computedValue;
|
||||
|
@ -34,7 +34,7 @@ export default {
|
||||
* @public
|
||||
*/
|
||||
trigger(event, ...args) {
|
||||
this.getHandlers(event).forEach(handler => handler.apply(this, args));
|
||||
this.getHandlers(event).forEach((handler) => handler.apply(this, args));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -55,7 +55,7 @@ export default {
|
||||
* @param {function} handler The function to handle the event.
|
||||
*/
|
||||
one(event, handler) {
|
||||
const wrapper = function() {
|
||||
const wrapper = function () {
|
||||
handler.apply(this, arguments);
|
||||
|
||||
this.off(event, wrapper);
|
||||
@ -77,5 +77,5 @@ export default {
|
||||
if (index !== -1) {
|
||||
handlers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
export default function extractText(vdom) {
|
||||
if (vdom instanceof Array) {
|
||||
return vdom.map(element => extractText(element)).join('');
|
||||
return vdom.map((element) => extractText(element)).join('');
|
||||
} else if (typeof vdom === 'object' && vdom !== null) {
|
||||
return extractText(vdom.children);
|
||||
} else {
|
||||
|
@ -33,4 +33,4 @@ export default function humanTime(time) {
|
||||
}
|
||||
|
||||
return ago;
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import humanTimeUtil from './humanTime';
|
||||
|
||||
function updateHumanTimes() {
|
||||
$('[data-humantime]').each(function() {
|
||||
$('[data-humantime]').each(function () {
|
||||
const $this = $(this);
|
||||
const ago = humanTimeUtil($this.attr('datetime'));
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
export default function mixin(Parent, ...mixins) {
|
||||
class Mixed extends Parent {}
|
||||
|
||||
mixins.forEach(object => {
|
||||
mixins.forEach((object) => {
|
||||
Object.assign(Mixed.prototype, object);
|
||||
});
|
||||
|
||||
|
@ -3,11 +3,11 @@ import Component from '../Component';
|
||||
export default function patchMithril(global) {
|
||||
const mo = global.m;
|
||||
|
||||
const m = function(comp, ...args) {
|
||||
const m = function (comp, ...args) {
|
||||
if (comp.prototype && comp.prototype instanceof Component) {
|
||||
let children = args.slice(1);
|
||||
if (children.length === 1 && Array.isArray(children[0])) {
|
||||
children = children[0]
|
||||
children = children[0];
|
||||
}
|
||||
|
||||
return comp.component(args[0], children);
|
||||
@ -29,14 +29,14 @@ export default function patchMithril(global) {
|
||||
return node;
|
||||
};
|
||||
|
||||
Object.keys(mo).forEach(key => m[key] = mo[key]);
|
||||
Object.keys(mo).forEach((key) => (m[key] = mo[key]));
|
||||
|
||||
/**
|
||||
* Redraw only if not in the middle of a computation (e.g. a route change).
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
m.lazyRedraw = function() {
|
||||
m.lazyRedraw = function () {
|
||||
m.startComputation();
|
||||
m.endComputation();
|
||||
};
|
||||
|
@ -7,9 +7,7 @@
|
||||
* @return {String}
|
||||
*/
|
||||
export function truncate(string, length, start = 0) {
|
||||
return (start > 0 ? '...' : '') +
|
||||
string.substring(start, start + length) +
|
||||
(string.length > start + length ? '...' : '');
|
||||
return (start > 0 ? '...' : '') + string.substring(start, start + length) + (string.length > start + length ? '...' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -24,7 +22,8 @@ export function truncate(string, length, start = 0) {
|
||||
* @return {String}
|
||||
*/
|
||||
export function slug(string) {
|
||||
return string.toLowerCase()
|
||||
return string
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]/gi, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.replace(/-$|^-/g, '');
|
||||
@ -38,9 +37,7 @@ export function slug(string) {
|
||||
* @return {String}
|
||||
*/
|
||||
export function getPlainContent(string) {
|
||||
const html = string
|
||||
.replace(/(<\/p>|<br>)/g, '$1 ')
|
||||
.replace(/<img\b[^>]*>/ig, ' ');
|
||||
const html = string.replace(/(<\/p>|<br>)/g, '$1 ').replace(/<img\b[^>]*>/gi, ' ');
|
||||
|
||||
const dom = $('<div/>').html(html);
|
||||
|
||||
|
@ -10,18 +10,42 @@ function hsvToRgb(h, s, v) {
|
||||
const t = v * (1 - (1 - f) * s);
|
||||
|
||||
switch (i % 6) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
case 0:
|
||||
r = v;
|
||||
g = t;
|
||||
b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q;
|
||||
g = v;
|
||||
b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p;
|
||||
g = v;
|
||||
b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p;
|
||||
g = q;
|
||||
b = v;
|
||||
break;
|
||||
case 4:
|
||||
r = t;
|
||||
g = p;
|
||||
b = v;
|
||||
break;
|
||||
case 5:
|
||||
r = v;
|
||||
g = p;
|
||||
b = q;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
r: Math.floor(r * 255),
|
||||
g: Math.floor(g * 255),
|
||||
b: Math.floor(b * 255)
|
||||
b: Math.floor(b * 255),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ export default class ForumApplication extends Application {
|
||||
* @type {Object}
|
||||
*/
|
||||
notificationComponents = {
|
||||
discussionRenamed: DiscussionRenamedNotification
|
||||
discussionRenamed: DiscussionRenamedNotification,
|
||||
};
|
||||
/**
|
||||
* A map of post types to their components.
|
||||
@ -31,7 +31,7 @@ export default class ForumApplication extends Application {
|
||||
*/
|
||||
postComponents = {
|
||||
comment: CommentPost,
|
||||
discussionRenamed: DiscussionRenamedPost
|
||||
discussionRenamed: DiscussionRenamedPost,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -87,7 +87,7 @@ export default class ForumApplication extends Application {
|
||||
this.routes[defaultAction].path = '/';
|
||||
this.history.push(defaultAction, this.translator.trans('core.forum.header.back_to_index_tooltip'), '/');
|
||||
|
||||
m.mount(document.getElementById('app-navigation'), Navigation.component({className: 'App-backControl', drawer: true}));
|
||||
m.mount(document.getElementById('app-navigation'), Navigation.component({ className: 'App-backControl', drawer: true }));
|
||||
m.mount(document.getElementById('header-navigation'), Navigation.component());
|
||||
m.mount(document.getElementById('header-primary'), HeaderPrimary.component());
|
||||
m.mount(document.getElementById('header-secondary'), HeaderSecondary.component());
|
||||
@ -102,7 +102,7 @@ export default class ForumApplication extends Application {
|
||||
|
||||
// Route the home link back home when clicked. We do not want it to register
|
||||
// if the user is opening it in a new tab, however.
|
||||
$('#home-link').click(e => {
|
||||
$('#home-link').click((e) => {
|
||||
if (e.ctrlKey || e.metaKey || e.which === 2) return;
|
||||
e.preventDefault();
|
||||
app.history.home();
|
||||
@ -123,9 +123,11 @@ export default class ForumApplication extends Application {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
composingReplyTo(discussion) {
|
||||
return this.composer.component instanceof ReplyComposer &&
|
||||
return (
|
||||
this.composer.component instanceof ReplyComposer &&
|
||||
this.composer.component.props.discussion === discussion &&
|
||||
this.composer.position !== Composer.PositionEnum.HIDDEN;
|
||||
this.composer.position !== Composer.PositionEnum.HIDDEN
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,8 +137,7 @@ export default class ForumApplication extends Application {
|
||||
* @return {Boolean}
|
||||
*/
|
||||
viewingDiscussion(discussion) {
|
||||
return this.current instanceof DiscussionPage &&
|
||||
this.current.discussion === discussion;
|
||||
return this.current instanceof DiscussionPage && this.current.discussion === discussion;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,6 +134,6 @@ export default Object.assign(compat, {
|
||||
'components/DiscussionListItem': DiscussionListItem,
|
||||
'components/LoadingPost': LoadingPost,
|
||||
'components/PostsUserPage': PostsUserPage,
|
||||
'routes': routes,
|
||||
'ForumApplication': ForumApplication
|
||||
routes: routes,
|
||||
ForumApplication: ForumApplication,
|
||||
});
|
||||
|
@ -44,7 +44,8 @@ export default class AvatarEditor extends Component {
|
||||
return (
|
||||
<div className={'AvatarEditor Dropdown ' + this.props.className + (this.loading ? ' loading' : '') + (this.isDraggedOver ? ' dragover' : '')}>
|
||||
{avatar(user)}
|
||||
<a className={ user.avatarUrl() ? "Dropdown-toggle" : "Dropdown-toggle AvatarEditor--noAvatar" }
|
||||
<a
|
||||
className={user.avatarUrl() ? 'Dropdown-toggle' : 'Dropdown-toggle AvatarEditor--noAvatar'}
|
||||
title={app.translator.trans('core.forum.user.avatar_upload_tooltip')}
|
||||
data-toggle="dropdown"
|
||||
onclick={this.quickUpload.bind(this)}
|
||||
@ -52,12 +53,11 @@ export default class AvatarEditor extends Component {
|
||||
ondragenter={this.enableDragover.bind(this)}
|
||||
ondragleave={this.disableDragover.bind(this)}
|
||||
ondragend={this.disableDragover.bind(this)}
|
||||
ondrop={this.dropUpload.bind(this)}>
|
||||
{this.loading ? LoadingIndicator.component() : (user.avatarUrl() ? icon('fas fa-pencil-alt') : icon('fas fa-plus-circle'))}
|
||||
ondrop={this.dropUpload.bind(this)}
|
||||
>
|
||||
{this.loading ? LoadingIndicator.component() : user.avatarUrl() ? icon('fas fa-pencil-alt') : icon('fas fa-plus-circle')}
|
||||
</a>
|
||||
<ul className="Dropdown-menu Menu">
|
||||
{listItems(this.controlItems().toArray())}
|
||||
</ul>
|
||||
<ul className="Dropdown-menu Menu">{listItems(this.controlItems().toArray())}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -70,19 +70,21 @@ export default class AvatarEditor extends Component {
|
||||
controlItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('upload',
|
||||
items.add(
|
||||
'upload',
|
||||
Button.component({
|
||||
icon: 'fas fa-upload',
|
||||
children: app.translator.trans('core.forum.user.avatar_upload_button'),
|
||||
onclick: this.openPicker.bind(this)
|
||||
onclick: this.openPicker.bind(this),
|
||||
})
|
||||
);
|
||||
|
||||
items.add('remove',
|
||||
items.add(
|
||||
'remove',
|
||||
Button.component({
|
||||
icon: 'fas fa-times',
|
||||
children: app.translator.trans('core.forum.user.avatar_remove_button'),
|
||||
onclick: this.remove.bind(this)
|
||||
onclick: this.remove.bind(this),
|
||||
})
|
||||
);
|
||||
|
||||
@ -150,7 +152,11 @@ export default class AvatarEditor extends Component {
|
||||
const user = this.props.user;
|
||||
const $input = $('<input type="file">');
|
||||
|
||||
$input.appendTo('body').hide().click().on('input', e => {
|
||||
$input
|
||||
.appendTo('body')
|
||||
.hide()
|
||||
.click()
|
||||
.on('input', (e) => {
|
||||
this.upload($(e.target)[0].files[0]);
|
||||
});
|
||||
}
|
||||
@ -170,15 +176,14 @@ export default class AvatarEditor extends Component {
|
||||
this.loading = true;
|
||||
m.redraw();
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
method: 'POST',
|
||||
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar',
|
||||
serialize: raw => raw,
|
||||
data
|
||||
}).then(
|
||||
this.success.bind(this),
|
||||
this.failure.bind(this)
|
||||
);
|
||||
serialize: (raw) => raw,
|
||||
data,
|
||||
})
|
||||
.then(this.success.bind(this), this.failure.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,13 +195,12 @@ export default class AvatarEditor extends Component {
|
||||
this.loading = true;
|
||||
m.redraw();
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
method: 'DELETE',
|
||||
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar'
|
||||
}).then(
|
||||
this.success.bind(this),
|
||||
this.failure.bind(this)
|
||||
);
|
||||
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar',
|
||||
})
|
||||
.then(this.success.bind(this), this.failure.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,9 @@ export default class ChangeEmailModal extends Modal {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<div className="Form Form--centered">
|
||||
<p className="helpText">{app.translator.trans('core.forum.change_email.confirmation_message', {email: <strong>{this.email()}</strong>})}</p>
|
||||
<p className="helpText">
|
||||
{app.translator.trans('core.forum.change_email.confirmation_message', { email: <strong>{this.email()}</strong> })}
|
||||
</p>
|
||||
<div className="Form-group">
|
||||
<Button className="Button Button--primary Button--block" onclick={this.hide.bind(this)}>
|
||||
{app.translator.trans('core.forum.change_email.dismiss_button')}
|
||||
@ -59,23 +61,31 @@ export default class ChangeEmailModal extends Modal {
|
||||
<div className="Modal-body">
|
||||
<div className="Form Form--centered">
|
||||
<div className="Form-group">
|
||||
<input type="email" name="email" className="FormControl"
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
className="FormControl"
|
||||
placeholder={app.session.user.email()}
|
||||
bidi={this.email}
|
||||
disabled={this.loading}/>
|
||||
disabled={this.loading}
|
||||
/>
|
||||
</div>
|
||||
<div className="Form-group">
|
||||
<input type="password" name="password" className="FormControl"
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
className="FormControl"
|
||||
placeholder={app.translator.trans('core.forum.change_email.confirm_password_placeholder')}
|
||||
bidi={this.password}
|
||||
disabled={this.loading}/>
|
||||
disabled={this.loading}
|
||||
/>
|
||||
</div>
|
||||
<div className="Form-group">
|
||||
{Button.component({
|
||||
className: 'Button Button--primary Button--block',
|
||||
type: 'submit',
|
||||
loading: this.loading,
|
||||
children: app.translator.trans('core.forum.change_email.submit_button')
|
||||
children: app.translator.trans('core.forum.change_email.submit_button'),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
@ -97,11 +107,15 @@ export default class ChangeEmailModal extends Modal {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
app.session.user.save({email: this.email()}, {
|
||||
app.session.user
|
||||
.save(
|
||||
{ email: this.email() },
|
||||
{
|
||||
errorHandler: this.onerror.bind(this),
|
||||
meta: {password: this.password()}
|
||||
})
|
||||
.then(() => this.success = true)
|
||||
meta: { password: this.password() },
|
||||
}
|
||||
)
|
||||
.then(() => (this.success = true))
|
||||
.catch(() => {})
|
||||
.then(this.loaded.bind(this));
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export default class ChangePasswordModal extends Modal {
|
||||
className: 'Button Button--primary Button--block',
|
||||
type: 'submit',
|
||||
loading: this.loading,
|
||||
children: app.translator.trans('core.forum.change_password.send_button')
|
||||
children: app.translator.trans('core.forum.change_password.send_button'),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
@ -37,13 +37,12 @@ export default class ChangePasswordModal extends Modal {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
method: 'POST',
|
||||
url: app.forum.attribute('apiUrl') + '/forgot',
|
||||
data: {email: app.session.user.email()}
|
||||
}).then(
|
||||
this.hide.bind(this),
|
||||
this.loaded.bind(this)
|
||||
);
|
||||
data: { email: app.session.user.email() },
|
||||
})
|
||||
.then(this.hide.bind(this), this.loaded.bind(this));
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ export default class CommentPost extends Post {
|
||||
|
||||
// Create an instance of the component that displays the post's author so
|
||||
// that we can force the post to rerender when the user card is shown.
|
||||
this.postUser = new PostUser({post: this.props.post});
|
||||
this.postUser = new PostUser({ post: this.props.post });
|
||||
this.subtree.check(
|
||||
() => this.postUser.cardVisible,
|
||||
() => this.isEditing()
|
||||
@ -44,13 +44,13 @@ export default class CommentPost extends Post {
|
||||
// Note: we avoid using JSX for the <ul> below because it results in some
|
||||
// weirdness in Mithril.js 0.1.x (see flarum/core#975). This workaround can
|
||||
// be reverted when we upgrade to Mithril 1.0.
|
||||
return super.content().concat([
|
||||
return super
|
||||
.content()
|
||||
.concat([
|
||||
<header className="Post-header">{m('ul', listItems(this.headerItems().toArray()))}</header>,
|
||||
<div className="Post-body">
|
||||
{this.isEditing()
|
||||
? <div className="Post-preview" config={this.configPreview.bind(this)}/>
|
||||
: m.trust(this.props.post.contentHtml())}
|
||||
</div>
|
||||
{this.isEditing() ? <div className="Post-preview" config={this.configPreview.bind(this)} /> : m.trust(this.props.post.contentHtml())}
|
||||
</div>,
|
||||
]);
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ export default class CommentPost extends Post {
|
||||
// all of the <script> tags in the content and evaluate them. This is
|
||||
// necessary because TextFormatter outputs them for e.g. syntax highlighting.
|
||||
if (context.contentHtml !== contentHtml) {
|
||||
this.$('.Post-body script').each(function() {
|
||||
this.$('.Post-body script').each(function () {
|
||||
eval.call(window, $(this).text());
|
||||
});
|
||||
}
|
||||
@ -72,20 +72,22 @@ export default class CommentPost extends Post {
|
||||
}
|
||||
|
||||
isEditing() {
|
||||
return app.composer.component instanceof EditPostComposer &&
|
||||
app.composer.component.props.post === this.props.post;
|
||||
return app.composer.component instanceof EditPostComposer && app.composer.component.props.post === this.props.post;
|
||||
}
|
||||
|
||||
attrs() {
|
||||
const post = this.props.post;
|
||||
const attrs = super.attrs();
|
||||
|
||||
attrs.className = (attrs.className || '') + ' ' + classList({
|
||||
'CommentPost': true,
|
||||
attrs.className =
|
||||
(attrs.className || '') +
|
||||
' ' +
|
||||
classList({
|
||||
CommentPost: true,
|
||||
'Post--hidden': post.isHidden(),
|
||||
'Post--edited': post.isEdited(),
|
||||
'revealContent': this.revealContent,
|
||||
'editing': this.isEditing()
|
||||
revealContent: this.revealContent,
|
||||
editing: this.isEditing(),
|
||||
});
|
||||
|
||||
return attrs;
|
||||
@ -127,7 +129,7 @@ export default class CommentPost extends Post {
|
||||
headerItems() {
|
||||
const items = new ItemList();
|
||||
const post = this.props.post;
|
||||
const props = {post};
|
||||
const props = { post };
|
||||
|
||||
items.add('user', this.postUser.render(), 100);
|
||||
items.add('meta', PostMeta.component(props));
|
||||
@ -139,13 +141,14 @@ export default class CommentPost extends Post {
|
||||
// If the post is hidden, add a button that allows toggling the visibility
|
||||
// of the post's content.
|
||||
if (post.isHidden()) {
|
||||
items.add('toggle', (
|
||||
items.add(
|
||||
'toggle',
|
||||
Button.component({
|
||||
className: 'Button Button--default Button--more',
|
||||
icon: 'fas fa-ellipsis-h',
|
||||
onclick: this.toggleContent.bind(this)
|
||||
onclick: this.toggleContent.bind(this),
|
||||
})
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
|
@ -36,10 +36,10 @@ class Composer extends Component {
|
||||
|
||||
view() {
|
||||
const classes = {
|
||||
'normal': this.position === Composer.PositionEnum.NORMAL,
|
||||
'minimized': this.position === Composer.PositionEnum.MINIMIZED,
|
||||
'fullScreen': this.position === Composer.PositionEnum.FULLSCREEN,
|
||||
'active': this.active
|
||||
normal: this.position === Composer.PositionEnum.NORMAL,
|
||||
minimized: this.position === Composer.PositionEnum.MINIMIZED,
|
||||
fullScreen: this.position === Composer.PositionEnum.FULLSCREEN,
|
||||
active: this.active,
|
||||
};
|
||||
classes.visible = classes.normal || classes.minimized || classes.fullScreen;
|
||||
|
||||
@ -52,7 +52,7 @@ class Composer extends Component {
|
||||
|
||||
return (
|
||||
<div className={'Composer ' + classList(classes)}>
|
||||
<div className="Composer-handle" config={this.configHandle.bind(this)}/>
|
||||
<div className="Composer-handle" config={this.configHandle.bind(this)} />
|
||||
<ul className="Composer-controls">{listItems(this.controlItems().toArray())}</ul>
|
||||
<div className="Composer-content" onclick={showIfMinimized}>
|
||||
{this.component ? this.component.render() : ''}
|
||||
@ -77,7 +77,7 @@ class Composer extends Component {
|
||||
|
||||
// Whenever any of the inputs inside the composer are have focus, we want to
|
||||
// add a class to the composer to draw attention to it.
|
||||
this.$().on('focus blur', ':input', e => {
|
||||
this.$().on('focus blur', ':input', (e) => {
|
||||
this.active = e.type === 'focusin';
|
||||
m.redraw();
|
||||
});
|
||||
@ -94,18 +94,18 @@ class Composer extends Component {
|
||||
|
||||
const handlers = {};
|
||||
|
||||
$(window).on('resize', handlers.onresize = this.updateHeight.bind(this)).resize();
|
||||
$(window)
|
||||
.on('resize', (handlers.onresize = this.updateHeight.bind(this)))
|
||||
.resize();
|
||||
|
||||
$(document)
|
||||
.on('mousemove', handlers.onmousemove = this.onmousemove.bind(this))
|
||||
.on('mouseup', handlers.onmouseup = this.onmouseup.bind(this));
|
||||
.on('mousemove', (handlers.onmousemove = this.onmousemove.bind(this)))
|
||||
.on('mouseup', (handlers.onmouseup = this.onmouseup.bind(this)));
|
||||
|
||||
context.onunload = () => {
|
||||
$(window).off('resize', handlers.onresize);
|
||||
|
||||
$(document)
|
||||
.off('mousemove', handlers.onmousemove)
|
||||
.off('mouseup', handlers.onmouseup);
|
||||
$(document).off('mousemove', handlers.onmousemove).off('mouseup', handlers.onmouseup);
|
||||
};
|
||||
}
|
||||
|
||||
@ -121,9 +121,10 @@ class Composer extends Component {
|
||||
|
||||
const composer = this;
|
||||
|
||||
$(element).css('cursor', 'row-resize')
|
||||
.bind('dragstart mousedown', e => e.preventDefault())
|
||||
.mousedown(function(e) {
|
||||
$(element)
|
||||
.css('cursor', 'row-resize')
|
||||
.bind('dragstart mousedown', (e) => e.preventDefault())
|
||||
.mousedown(function (e) {
|
||||
composer.mouseStart = e.clientY;
|
||||
composer.heightStart = composer.$().height();
|
||||
composer.handle = $(this);
|
||||
@ -191,15 +192,12 @@ class Composer extends Component {
|
||||
* scrolled right to the bottom.
|
||||
*/
|
||||
updateBodyPadding() {
|
||||
const visible = this.position !== Composer.PositionEnum.HIDDEN &&
|
||||
this.position !== Composer.PositionEnum.MINIMIZED &&
|
||||
this.$().css('position') !== 'absolute';
|
||||
const visible =
|
||||
this.position !== Composer.PositionEnum.HIDDEN && this.position !== Composer.PositionEnum.MINIMIZED && this.$().css('position') !== 'absolute';
|
||||
|
||||
const paddingBottom = visible
|
||||
? this.computedHeight() - parseInt($('#app').css('padding-bottom'), 10)
|
||||
: 0;
|
||||
const paddingBottom = visible ? this.computedHeight() - parseInt($('#app').css('padding-bottom'), 10) : 0;
|
||||
|
||||
$('#content').css({paddingBottom});
|
||||
$('#content').css({ paddingBottom });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -289,12 +287,12 @@ class Composer extends Component {
|
||||
const newHeight = $composer.outerHeight();
|
||||
|
||||
if (oldPosition === Composer.PositionEnum.HIDDEN) {
|
||||
$composer.css({bottom: -newHeight, height: newHeight});
|
||||
$composer.css({ bottom: -newHeight, height: newHeight });
|
||||
} else {
|
||||
$composer.css({height: oldHeight});
|
||||
$composer.css({ height: oldHeight });
|
||||
}
|
||||
|
||||
$composer.animate({bottom: 0, height: newHeight}, 'fast', () => this.component.focus());
|
||||
$composer.animate({ bottom: 0, height: newHeight }, 'fast', () => this.component.focus());
|
||||
|
||||
this.updateBodyPadding();
|
||||
$(window).scrollTop(scrollTop);
|
||||
@ -304,9 +302,7 @@ class Composer extends Component {
|
||||
* Show the Composer backdrop.
|
||||
*/
|
||||
showBackdrop() {
|
||||
this.$backdrop = $('<div/>')
|
||||
.addClass('composer-backdrop')
|
||||
.appendTo('body');
|
||||
this.$backdrop = $('<div/>').addClass('composer-backdrop').appendTo('body');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -346,7 +342,7 @@ class Composer extends Component {
|
||||
// Animate the composer sliding down off the bottom edge of the viewport.
|
||||
// Only when the animation is completed, update the Composer state flag and
|
||||
// other elements on the page.
|
||||
$composer.stop(true).animate({bottom: -$composer.height()}, 'fast', () => {
|
||||
$composer.stop(true).animate({ bottom: -$composer.height() }, 'fast', () => {
|
||||
this.position = Composer.PositionEnum.HIDDEN;
|
||||
this.clear();
|
||||
m.redraw();
|
||||
@ -421,32 +417,44 @@ class Composer extends Component {
|
||||
const items = new ItemList();
|
||||
|
||||
if (this.position === Composer.PositionEnum.FULLSCREEN) {
|
||||
items.add('exitFullScreen', ComposerButton.component({
|
||||
items.add(
|
||||
'exitFullScreen',
|
||||
ComposerButton.component({
|
||||
icon: 'fas fa-compress',
|
||||
title: app.translator.trans('core.forum.composer.exit_full_screen_tooltip'),
|
||||
onclick: this.exitFullScreen.bind(this)
|
||||
}));
|
||||
onclick: this.exitFullScreen.bind(this),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
if (this.position !== Composer.PositionEnum.MINIMIZED) {
|
||||
items.add('minimize', ComposerButton.component({
|
||||
items.add(
|
||||
'minimize',
|
||||
ComposerButton.component({
|
||||
icon: 'fas fa-minus minimize',
|
||||
title: app.translator.trans('core.forum.composer.minimize_tooltip'),
|
||||
onclick: this.minimize.bind(this),
|
||||
itemClassName: 'App-backControl'
|
||||
}));
|
||||
itemClassName: 'App-backControl',
|
||||
})
|
||||
);
|
||||
|
||||
items.add('fullScreen', ComposerButton.component({
|
||||
items.add(
|
||||
'fullScreen',
|
||||
ComposerButton.component({
|
||||
icon: 'fas fa-expand',
|
||||
title: app.translator.trans('core.forum.composer.full_screen_tooltip'),
|
||||
onclick: this.fullScreen.bind(this)
|
||||
}));
|
||||
onclick: this.fullScreen.bind(this),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
items.add('close', ComposerButton.component({
|
||||
items.add(
|
||||
'close',
|
||||
ComposerButton.component({
|
||||
icon: 'fas fa-times',
|
||||
title: app.translator.trans('core.forum.composer.close_tooltip'),
|
||||
onclick: this.close.bind(this)
|
||||
}));
|
||||
onclick: this.close.bind(this),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
@ -524,7 +532,7 @@ Composer.PositionEnum = {
|
||||
HIDDEN: 'hidden',
|
||||
NORMAL: 'normal',
|
||||
MINIMIZED: 'minimized',
|
||||
FULLSCREEN: 'fullScreen'
|
||||
FULLSCREEN: 'fullScreen',
|
||||
};
|
||||
|
||||
export default Composer;
|
||||
|
@ -47,7 +47,7 @@ export default class ComposerBody extends Component {
|
||||
placeholder: this.props.placeholder,
|
||||
onchange: this.content,
|
||||
onsubmit: this.onsubmit.bind(this),
|
||||
value: this.content()
|
||||
value: this.content(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -57,12 +57,12 @@ export default class ComposerBody extends Component {
|
||||
|
||||
return (
|
||||
<div className={'ComposerBody ' + (this.props.className || '')}>
|
||||
{avatar(this.props.user, {className: 'ComposerBody-avatar'})}
|
||||
{avatar(this.props.user, { className: 'ComposerBody-avatar' })}
|
||||
<div className="ComposerBody-content">
|
||||
<ul className="ComposerBody-header">{listItems(this.headerItems().toArray())}</ul>
|
||||
<div className="ComposerBody-editor">{this.editor.render()}</div>
|
||||
</div>
|
||||
{LoadingIndicator.component({className: 'ComposerBody-loading' + (this.loading ? ' active' : '')})}
|
||||
{LoadingIndicator.component({ className: 'ComposerBody-loading' + (this.loading ? ' active' : '') })}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -100,8 +100,7 @@ export default class ComposerBody extends Component {
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
onsubmit() {
|
||||
}
|
||||
onsubmit() {}
|
||||
|
||||
/**
|
||||
* Stop loading.
|
||||
|
@ -39,16 +39,19 @@ export default class DiscussionComposer extends ComposerBody {
|
||||
|
||||
items.add('title', <h3>{app.translator.trans('core.forum.composer_discussion.title')}</h3>, 100);
|
||||
|
||||
items.add('discussionTitle', (
|
||||
items.add(
|
||||
'discussionTitle',
|
||||
<h3>
|
||||
<input className="FormControl"
|
||||
<input
|
||||
className="FormControl"
|
||||
value={this.title()}
|
||||
oninput={m.withAttr('value', this.title)}
|
||||
placeholder={this.props.titlePlaceholder}
|
||||
disabled={!!this.props.disabled}
|
||||
onkeydown={this.onkeydown.bind(this)}/>
|
||||
onkeydown={this.onkeydown.bind(this)}
|
||||
/>
|
||||
</h3>
|
||||
));
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -60,7 +63,8 @@ export default class DiscussionComposer extends ComposerBody {
|
||||
* @param {Event} e
|
||||
*/
|
||||
onkeydown(e) {
|
||||
if (e.which === 13) { // Return
|
||||
if (e.which === 13) {
|
||||
// Return
|
||||
e.preventDefault();
|
||||
this.editor.setSelectionRange(0, 0);
|
||||
}
|
||||
@ -80,7 +84,7 @@ export default class DiscussionComposer extends ComposerBody {
|
||||
data() {
|
||||
return {
|
||||
title: this.title(),
|
||||
content: this.content()
|
||||
content: this.content(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -89,13 +93,13 @@ export default class DiscussionComposer extends ComposerBody {
|
||||
|
||||
const data = this.data();
|
||||
|
||||
app.store.createRecord('discussions').save(data).then(
|
||||
discussion => {
|
||||
app.store
|
||||
.createRecord('discussions')
|
||||
.save(data)
|
||||
.then((discussion) => {
|
||||
app.composer.hide();
|
||||
app.cache.discussionList.refresh();
|
||||
m.route(app.route.discussion(discussion));
|
||||
},
|
||||
this.loaded.bind(this)
|
||||
);
|
||||
}, this.loaded.bind(this));
|
||||
}
|
||||
}
|
||||
|
@ -48,33 +48,27 @@ export default class DiscussionList extends Component {
|
||||
loading = Button.component({
|
||||
children: app.translator.trans('core.forum.discussion_list.load_more_button'),
|
||||
className: 'Button',
|
||||
onclick: this.loadMore.bind(this)
|
||||
onclick: this.loadMore.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.discussions.length === 0 && !this.loading) {
|
||||
const text = app.translator.trans('core.forum.discussion_list.empty_text');
|
||||
return (
|
||||
<div className="DiscussionList">
|
||||
{Placeholder.component({text})}
|
||||
</div>
|
||||
);
|
||||
return <div className="DiscussionList">{Placeholder.component({ text })}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'DiscussionList'+(this.props.params.q ? ' DiscussionList--searchResults' : '')}>
|
||||
<div className={'DiscussionList' + (this.props.params.q ? ' DiscussionList--searchResults' : '')}>
|
||||
<ul className="DiscussionList-discussions">
|
||||
{this.discussions.map(discussion => {
|
||||
{this.discussions.map((discussion) => {
|
||||
return (
|
||||
<li key={discussion.id()} data-id={discussion.id()}>
|
||||
{DiscussionListItem.component({discussion, params})}
|
||||
{DiscussionListItem.component({ discussion, params })}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<div className="DiscussionList-loadMore">
|
||||
{loading}
|
||||
</div>
|
||||
<div className="DiscussionList-loadMore">{loading}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -87,7 +81,7 @@ export default class DiscussionList extends Component {
|
||||
* @api
|
||||
*/
|
||||
requestParams() {
|
||||
const params = {include: ['user', 'lastPostedUser'], filter: {}};
|
||||
const params = { include: ['user', 'lastPostedUser'], filter: {} };
|
||||
|
||||
params.sort = this.sortMap()[this.props.params.sort];
|
||||
|
||||
@ -132,7 +126,7 @@ export default class DiscussionList extends Component {
|
||||
}
|
||||
|
||||
return this.loadResults().then(
|
||||
results => {
|
||||
(results) => {
|
||||
this.discussions = [];
|
||||
this.parseResults(results);
|
||||
},
|
||||
@ -157,7 +151,7 @@ export default class DiscussionList extends Component {
|
||||
}
|
||||
|
||||
const params = this.requestParams();
|
||||
params.page = {offset};
|
||||
params.page = { offset };
|
||||
params.include = params.include.join(',');
|
||||
|
||||
return app.store.find('discussions', params);
|
||||
@ -171,8 +165,7 @@ export default class DiscussionList extends Component {
|
||||
loadMore() {
|
||||
this.loading = true;
|
||||
|
||||
this.loadResults(this.discussions.length)
|
||||
.then(this.parseResults.bind(this));
|
||||
this.loadResults(this.discussions.length).then(this.parseResults.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,8 +47,8 @@ export default class DiscussionListItem extends Component {
|
||||
className: classList([
|
||||
'DiscussionListItem',
|
||||
this.active() ? 'active' : '',
|
||||
this.props.discussion.isHidden() ? 'DiscussionListItem--hidden' : ''
|
||||
])
|
||||
this.props.discussion.isHidden() ? 'DiscussionListItem--hidden' : '',
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
@ -73,50 +73,56 @@ export default class DiscussionListItem extends Component {
|
||||
}
|
||||
|
||||
const phrase = this.props.params.q;
|
||||
this.highlightRegExp = new RegExp(phrase+'|'+phrase.trim().replace(/\s+/g, '|'), 'gi');
|
||||
this.highlightRegExp = new RegExp(phrase + '|' + phrase.trim().replace(/\s+/g, '|'), 'gi');
|
||||
} else {
|
||||
jumpTo = Math.min(discussion.lastPostNumber(), (discussion.lastReadPostNumber() || 0) + 1);
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...attrs}>
|
||||
{controls.length ? Dropdown.component({
|
||||
{controls.length
|
||||
? Dropdown.component({
|
||||
icon: 'fas fa-ellipsis-v',
|
||||
children: controls,
|
||||
className: 'DiscussionListItem-controls',
|
||||
buttonClassName: 'Button Button--icon Button--flat Slidable-underneath Slidable-underneath--right'
|
||||
}) : ''}
|
||||
buttonClassName: 'Button Button--icon Button--flat Slidable-underneath Slidable-underneath--right',
|
||||
})
|
||||
: ''}
|
||||
|
||||
<a className={'Slidable-underneath Slidable-underneath--left Slidable-underneath--elastic' + (isUnread ? '' : ' disabled')}
|
||||
onclick={this.markAsRead.bind(this)}>
|
||||
<a
|
||||
className={'Slidable-underneath Slidable-underneath--left Slidable-underneath--elastic' + (isUnread ? '' : ' disabled')}
|
||||
onclick={this.markAsRead.bind(this)}
|
||||
>
|
||||
{icon('fas fa-check')}
|
||||
</a>
|
||||
|
||||
<div className={'DiscussionListItem-content Slidable-content' + (isUnread ? ' unread' : '') + (isRead ? ' read' : '')}>
|
||||
<a href={user ? app.route.user(user) : '#'}
|
||||
<a
|
||||
href={user ? app.route.user(user) : '#'}
|
||||
className="DiscussionListItem-author"
|
||||
title={extractText(app.translator.trans('core.forum.discussion_list.started_text', {user: user, ago: humanTime(discussion.createdAt())}))}
|
||||
config={function(element) {
|
||||
$(element).tooltip({placement: 'right'});
|
||||
title={extractText(
|
||||
app.translator.trans('core.forum.discussion_list.started_text', { user: user, ago: humanTime(discussion.createdAt()) })
|
||||
)}
|
||||
config={function (element) {
|
||||
$(element).tooltip({ placement: 'right' });
|
||||
m.route.apply(this, arguments);
|
||||
}}>
|
||||
{avatar(user, {title: ''})}
|
||||
}}
|
||||
>
|
||||
{avatar(user, { title: '' })}
|
||||
</a>
|
||||
|
||||
<ul className="DiscussionListItem-badges badges">
|
||||
{listItems(discussion.badges().toArray())}
|
||||
</ul>
|
||||
<ul className="DiscussionListItem-badges badges">{listItems(discussion.badges().toArray())}</ul>
|
||||
|
||||
<a href={app.route.discussion(discussion, jumpTo)}
|
||||
config={m.route}
|
||||
className="DiscussionListItem-main">
|
||||
<a href={app.route.discussion(discussion, jumpTo)} config={m.route} className="DiscussionListItem-main">
|
||||
<h3 className="DiscussionListItem-title">{highlight(discussion.title(), this.highlightRegExp)}</h3>
|
||||
<ul className="DiscussionListItem-info">{listItems(this.infoItems().toArray())}</ul>
|
||||
</a>
|
||||
|
||||
<span className="DiscussionListItem-count"
|
||||
<span
|
||||
className="DiscussionListItem-count"
|
||||
onclick={this.markAsRead.bind(this)}
|
||||
title={showUnread ? app.translator.trans('core.forum.discussion_list.mark_as_read_tooltip') : ''}>
|
||||
title={showUnread ? app.translator.trans('core.forum.discussion_list.mark_as_read_tooltip') : ''}
|
||||
>
|
||||
{abbreviateNumber(discussion[showUnread ? 'unreadCount' : 'replyCount']())}
|
||||
</span>
|
||||
</div>
|
||||
@ -133,8 +139,7 @@ export default class DiscussionListItem extends Component {
|
||||
if ('ontouchstart' in window) {
|
||||
const slidableInstance = slidable(this.$().addClass('Slidable'));
|
||||
|
||||
this.$('.DiscussionListItem-controls')
|
||||
.on('hidden.bs.dropdown', () => slidableInstance.reset());
|
||||
this.$('.DiscussionListItem-controls').on('hidden.bs.dropdown', () => slidableInstance.reset());
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,7 +182,7 @@ export default class DiscussionListItem extends Component {
|
||||
const discussion = this.props.discussion;
|
||||
|
||||
if (discussion.isUnread()) {
|
||||
discussion.save({lastReadPostNumber: discussion.lastPostNumber()});
|
||||
discussion.save({ lastReadPostNumber: discussion.lastPostNumber() });
|
||||
m.redraw();
|
||||
}
|
||||
}
|
||||
@ -199,10 +204,11 @@ export default class DiscussionListItem extends Component {
|
||||
items.add('excerpt', excerpt, -100);
|
||||
}
|
||||
} else {
|
||||
items.add('terminalPost',
|
||||
items.add(
|
||||
'terminalPost',
|
||||
TerminalPost.component({
|
||||
discussion: this.props.discussion,
|
||||
lastPost: !this.showFirstPost()
|
||||
lastPost: !this.showFirstPost(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -90,26 +90,26 @@ export default class DiscussionPage extends Page {
|
||||
|
||||
return (
|
||||
<div className="DiscussionPage">
|
||||
{app.cache.discussionList
|
||||
? <div className="DiscussionPage-list" config={this.configPane.bind(this)}>
|
||||
{app.cache.discussionList ? (
|
||||
<div className="DiscussionPage-list" config={this.configPane.bind(this)}>
|
||||
{!$('.App-navigation').is(':visible') ? app.cache.discussionList.render() : ''}
|
||||
</div>
|
||||
: ''}
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
<div className="DiscussionPage-discussion">
|
||||
{discussion
|
||||
? [
|
||||
DiscussionHero.component({discussion}),
|
||||
DiscussionHero.component({ discussion }),
|
||||
<div className="container">
|
||||
<nav className="DiscussionPage-nav">
|
||||
<ul>{listItems(this.sidebarItems().toArray())}</ul>
|
||||
</nav>
|
||||
<div className="DiscussionPage-stream">
|
||||
{this.stream.render()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="DiscussionPage-stream">{this.stream.render()}</div>
|
||||
</div>,
|
||||
]
|
||||
: LoadingIndicator.component({className: 'LoadingIndicator--block'})}
|
||||
: LoadingIndicator.component({ className: 'LoadingIndicator--block' })}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -140,8 +140,7 @@ export default class DiscussionPage extends Page {
|
||||
} else {
|
||||
const params = this.requestParams();
|
||||
|
||||
app.store.find('discussions', m.route.param('id').split('-')[0], params)
|
||||
.then(this.show.bind(this));
|
||||
app.store.find('discussions', m.route.param('id').split('-')[0], params).then(this.show.bind(this));
|
||||
}
|
||||
|
||||
m.lazyRedraw();
|
||||
@ -155,7 +154,7 @@ export default class DiscussionPage extends Page {
|
||||
*/
|
||||
requestParams() {
|
||||
return {
|
||||
page: {near: this.near}
|
||||
page: { near: this.near },
|
||||
};
|
||||
}
|
||||
|
||||
@ -182,11 +181,14 @@ export default class DiscussionPage extends Page {
|
||||
const discussionId = discussion.id();
|
||||
|
||||
includedPosts = discussion.payload.included
|
||||
.filter(record => record.type === 'posts'
|
||||
&& record.relationships
|
||||
&& record.relationships.discussion
|
||||
&& record.relationships.discussion.data.id === discussionId)
|
||||
.map(record => app.store.getById('posts', record.id))
|
||||
.filter(
|
||||
(record) =>
|
||||
record.type === 'posts' &&
|
||||
record.relationships &&
|
||||
record.relationships.discussion &&
|
||||
record.relationships.discussion.data.id === discussionId
|
||||
)
|
||||
.map((record) => app.store.getById('posts', record.id))
|
||||
.sort((a, b) => a.id() - b.id())
|
||||
.slice(0, 20);
|
||||
}
|
||||
@ -194,7 +196,7 @@ export default class DiscussionPage extends Page {
|
||||
// Set up the post stream for this discussion, along with the first page of
|
||||
// posts we want to display. Tell the stream to scroll down and highlight
|
||||
// the specific post that was routed to.
|
||||
this.stream = new PostStream({discussion, includedPosts});
|
||||
this.stream = new PostStream({ discussion, includedPosts });
|
||||
this.stream.on('positionChanged', this.positionChanged.bind(this));
|
||||
this.stream.goToNumber(m.route.param('near') || (includedPosts[0] && includedPosts[0].number()), true);
|
||||
}
|
||||
@ -219,7 +221,7 @@ export default class DiscussionPage extends Page {
|
||||
const pane = app.pane;
|
||||
$list.hover(pane.show.bind(pane), pane.onmouseleave.bind(pane));
|
||||
|
||||
const hotEdge = e => {
|
||||
const hotEdge = (e) => {
|
||||
if (e.pageX < 10) pane.show();
|
||||
};
|
||||
$(document).on('mousemove', hotEdge);
|
||||
@ -249,19 +251,21 @@ export default class DiscussionPage extends Page {
|
||||
sidebarItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('controls',
|
||||
items.add(
|
||||
'controls',
|
||||
SplitDropdown.component({
|
||||
children: DiscussionControls.controls(this.discussion, this).toArray(),
|
||||
icon: 'fas fa-ellipsis-v',
|
||||
className: 'App-primaryControl',
|
||||
buttonClassName: 'Button--primary'
|
||||
buttonClassName: 'Button--primary',
|
||||
})
|
||||
);
|
||||
|
||||
items.add('scrubber',
|
||||
items.add(
|
||||
'scrubber',
|
||||
PostStreamScrubber.component({
|
||||
stream: this.stream,
|
||||
className: 'App-titleControl'
|
||||
className: 'App-titleControl',
|
||||
}),
|
||||
-100
|
||||
);
|
||||
@ -281,7 +285,7 @@ export default class DiscussionPage extends Page {
|
||||
|
||||
// Construct a URL to this discussion with the updated position, then
|
||||
// replace it into the window's history and our own history stack.
|
||||
const url = app.route.discussion(discussion, this.near = startNumber);
|
||||
const url = app.route.discussion(discussion, (this.near = startNumber));
|
||||
|
||||
m.route(url, true);
|
||||
window.history.replaceState(null, document.title, url);
|
||||
@ -291,7 +295,7 @@ export default class DiscussionPage extends Page {
|
||||
// If the user hasn't read past here before, then we'll update their read
|
||||
// state and redraw.
|
||||
if (app.session.user && endNumber > (discussion.lastReadPostNumber() || 0)) {
|
||||
discussion.save({lastReadPostNumber: endNumber});
|
||||
discussion.save({ lastReadPostNumber: endNumber });
|
||||
m.redraw();
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,6 @@ export default class DiscussionRenamedNotification extends Notification {
|
||||
}
|
||||
|
||||
content() {
|
||||
return app.translator.trans('core.forum.notifications.discussion_renamed_text', {user: this.props.notification.fromUser()});
|
||||
return app.translator.trans('core.forum.notifications.discussion_renamed_text', { user: this.props.notification.fromUser() });
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ export default class DiscussionRenamedPost extends EventPost {
|
||||
const newTitle = post.content()[1];
|
||||
|
||||
return {
|
||||
'old': oldTitle,
|
||||
'new': <strong className="DiscussionRenamedPost-new">{newTitle}</strong>
|
||||
old: oldTitle,
|
||||
new: <strong className="DiscussionRenamedPost-new">{newTitle}</strong>,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,12 @@ export default class DiscussionsSearchSource {
|
||||
this.results[query] = [];
|
||||
|
||||
const params = {
|
||||
filter: {q: query},
|
||||
page: {limit: 3},
|
||||
include: 'mostRelevantPost'
|
||||
filter: { q: query },
|
||||
page: { limit: 3 },
|
||||
include: 'mostRelevantPost',
|
||||
};
|
||||
|
||||
return app.store.find('discussions', params).then(results => this.results[query] = results);
|
||||
return app.store.find('discussions', params).then((results) => (this.results[query] = results));
|
||||
}
|
||||
|
||||
view(query) {
|
||||
@ -36,11 +36,11 @@ export default class DiscussionsSearchSource {
|
||||
<li>
|
||||
{LinkButton.component({
|
||||
icon: 'fas fa-search',
|
||||
children: app.translator.trans('core.forum.search.all_discussions_button', {query}),
|
||||
href: app.route('index', {q: query})
|
||||
children: app.translator.trans('core.forum.search.all_discussions_button', { query }),
|
||||
href: app.route('index', { q: query }),
|
||||
})}
|
||||
</li>,
|
||||
results.map(discussion => {
|
||||
results.map((discussion) => {
|
||||
const mostRelevantPost = discussion.mostRelevantPost();
|
||||
|
||||
return (
|
||||
@ -51,7 +51,7 @@ export default class DiscussionsSearchSource {
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ export default class DiscussionsUserPage extends UserPage {
|
||||
{DiscussionList.component({
|
||||
params: {
|
||||
q: 'author:' + this.user.username(),
|
||||
sort: 'newest'
|
||||
}
|
||||
sort: 'newest',
|
||||
},
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
@ -22,7 +22,7 @@ export default class EditPostComposer extends ComposerBody {
|
||||
init() {
|
||||
super.init();
|
||||
|
||||
this.editor.props.preview = e => {
|
||||
this.editor.props.preview = (e) => {
|
||||
minimizeComposerIfFullScreen(e);
|
||||
|
||||
m.route(app.route.post(this.props.post));
|
||||
@ -44,20 +44,21 @@ export default class EditPostComposer extends ComposerBody {
|
||||
const items = super.headerItems();
|
||||
const post = this.props.post;
|
||||
|
||||
const routeAndMinimize = function(element, isInitialized) {
|
||||
const routeAndMinimize = function (element, isInitialized) {
|
||||
if (isInitialized) return;
|
||||
$(element).on('click', minimizeComposerIfFullScreen);
|
||||
m.route.apply(this, arguments);
|
||||
};
|
||||
|
||||
items.add('title', (
|
||||
items.add(
|
||||
'title',
|
||||
<h3>
|
||||
{icon('fas fa-pencil-alt')} {' '}
|
||||
{icon('fas fa-pencil-alt')}{' '}
|
||||
<a href={app.route.discussion(post.discussion(), post.number())} config={routeAndMinimize}>
|
||||
{app.translator.trans('core.forum.composer_edit.post_link', {number: post.number(), discussion: post.discussion().title()})}
|
||||
{app.translator.trans('core.forum.composer_edit.post_link', { number: post.number(), discussion: post.discussion().title() })}
|
||||
</a>
|
||||
</h3>
|
||||
));
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -69,7 +70,7 @@ export default class EditPostComposer extends ComposerBody {
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
content: this.content()
|
||||
content: this.content(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -78,9 +79,6 @@ export default class EditPostComposer extends ComposerBody {
|
||||
|
||||
const data = this.data();
|
||||
|
||||
this.props.post.save(data).then(
|
||||
() => app.composer.hide(),
|
||||
this.loaded.bind(this)
|
||||
);
|
||||
this.props.post.save(data).then(() => app.composer.hide(), this.loaded.bind(this));
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,10 @@ export default class EditUserModal extends Modal {
|
||||
this.password = m.prop(user.password() || '');
|
||||
this.groups = {};
|
||||
|
||||
app.store.all('groups')
|
||||
.filter(group => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||
.forEach(group => this.groups[group.id()] = m.prop(user.groups().indexOf(group) !== -1));
|
||||
app.store
|
||||
.all('groups')
|
||||
.filter((group) => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
||||
.forEach((group) => (this.groups[group.id()] = m.prop(user.groups().indexOf(group) !== -1)));
|
||||
}
|
||||
|
||||
className() {
|
||||
@ -37,9 +38,7 @@ export default class EditUserModal extends Modal {
|
||||
content() {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<div className="Form">
|
||||
{this.fields().toArray()}
|
||||
</div>
|
||||
<div className="Form">{this.fields().toArray()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -47,19 +46,22 @@ export default class EditUserModal extends Modal {
|
||||
fields() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('username', <div className="Form-group">
|
||||
items.add(
|
||||
'username',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.forum.edit_user.username_heading')}</label>
|
||||
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.username_label'))}
|
||||
bidi={this.username} />
|
||||
</div>, 40);
|
||||
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.username_label'))} bidi={this.username} />
|
||||
</div>,
|
||||
40
|
||||
);
|
||||
|
||||
if (app.session.user !== this.props.user) {
|
||||
items.add('email', <div className="Form-group">
|
||||
items.add(
|
||||
'email',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.forum.edit_user.email_heading')}</label>
|
||||
<div>
|
||||
<input className="FormControl"
|
||||
placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))}
|
||||
bidi={this.email}/>
|
||||
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))} bidi={this.email} />
|
||||
</div>
|
||||
{!this.isEmailConfirmed() ? (
|
||||
<div>
|
||||
@ -67,58 +69,84 @@ export default class EditUserModal extends Modal {
|
||||
className: 'Button Button--block',
|
||||
children: app.translator.trans('core.forum.edit_user.activate_button'),
|
||||
loading: this.loading,
|
||||
onclick: this.activate.bind(this)
|
||||
onclick: this.activate.bind(this),
|
||||
})}
|
||||
</div>
|
||||
) : ''}
|
||||
</div>, 30);
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>,
|
||||
30
|
||||
);
|
||||
|
||||
items.add('password', <div className="Form-group">
|
||||
items.add(
|
||||
'password',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.forum.edit_user.password_heading')}</label>
|
||||
<div>
|
||||
<label className="checkbox">
|
||||
<input type="checkbox" onchange={e => {
|
||||
<input
|
||||
type="checkbox"
|
||||
onchange={(e) => {
|
||||
this.setPassword(e.target.checked);
|
||||
m.redraw(true);
|
||||
if (e.target.checked) this.$('[name=password]').select();
|
||||
m.redraw.strategy('none');
|
||||
}}/>
|
||||
}}
|
||||
/>
|
||||
{app.translator.trans('core.forum.edit_user.set_password_label')}
|
||||
</label>
|
||||
{this.setPassword() ? (
|
||||
<input className="FormControl" type="password" name="password"
|
||||
<input
|
||||
className="FormControl"
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder={extractText(app.translator.trans('core.forum.edit_user.password_label'))}
|
||||
bidi={this.password}/>
|
||||
) : ''}
|
||||
bidi={this.password}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
</div>, 20);
|
||||
|
||||
</div>,
|
||||
20
|
||||
);
|
||||
}
|
||||
|
||||
items.add('groups', <div className="Form-group EditUserModal-groups">
|
||||
items.add(
|
||||
'groups',
|
||||
<div className="Form-group EditUserModal-groups">
|
||||
<label>{app.translator.trans('core.forum.edit_user.groups_heading')}</label>
|
||||
<div>
|
||||
{Object.keys(this.groups)
|
||||
.map(id => app.store.getById('groups', id))
|
||||
.map(group => (
|
||||
.map((id) => app.store.getById('groups', id))
|
||||
.map((group) => (
|
||||
<label className="checkbox">
|
||||
<input type="checkbox"
|
||||
<input
|
||||
type="checkbox"
|
||||
bidi={this.groups[group.id()]}
|
||||
disabled={this.props.user.id() === '1' && group.id() === Group.ADMINISTRATOR_ID} />
|
||||
{GroupBadge.component({group, label: ''})} {group.nameSingular()}
|
||||
disabled={this.props.user.id() === '1' && group.id() === Group.ADMINISTRATOR_ID}
|
||||
/>
|
||||
{GroupBadge.component({ group, label: '' })} {group.nameSingular()}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>, 10);
|
||||
</div>,
|
||||
10
|
||||
);
|
||||
|
||||
items.add('submit', <div className="Form-group">
|
||||
items.add(
|
||||
'submit',
|
||||
<div className="Form-group">
|
||||
{Button.component({
|
||||
className: 'Button Button--primary',
|
||||
type: 'submit',
|
||||
loading: this.loading,
|
||||
children: app.translator.trans('core.forum.edit_user.submit_button')
|
||||
children: app.translator.trans('core.forum.edit_user.submit_button'),
|
||||
})}
|
||||
</div>, -10);
|
||||
</div>,
|
||||
-10
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -129,7 +157,8 @@ export default class EditUserModal extends Modal {
|
||||
username: this.username(),
|
||||
isEmailConfirmed: true,
|
||||
};
|
||||
this.props.user.save(data, {errorHandler: this.onerror.bind(this)})
|
||||
this.props.user
|
||||
.save(data, { errorHandler: this.onerror.bind(this) })
|
||||
.then(() => {
|
||||
this.isEmailConfirmed(true);
|
||||
this.loading = false;
|
||||
@ -143,12 +172,12 @@ export default class EditUserModal extends Modal {
|
||||
|
||||
data() {
|
||||
const groups = Object.keys(this.groups)
|
||||
.filter(id => this.groups[id]())
|
||||
.map(id => app.store.getById('groups', id));
|
||||
.filter((id) => this.groups[id]())
|
||||
.map((id) => app.store.getById('groups', id));
|
||||
|
||||
const data = {
|
||||
username: this.username(),
|
||||
relationships: {groups}
|
||||
relationships: { groups },
|
||||
};
|
||||
|
||||
if (app.session.user !== this.props.user) {
|
||||
@ -167,7 +196,8 @@ export default class EditUserModal extends Modal {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
this.props.user.save(this.data(), {errorHandler: this.onerror.bind(this)})
|
||||
this.props.user
|
||||
.save(this.data(), { errorHandler: this.onerror.bind(this) })
|
||||
.then(this.hide.bind(this))
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
|
@ -28,17 +28,16 @@ export default class EventPost extends Post {
|
||||
const username = usernameHelper(user);
|
||||
const data = Object.assign(this.descriptionData(), {
|
||||
user,
|
||||
username: user
|
||||
? <a className="EventPost-user" href={app.route.user(user)} config={m.route}>{username}</a>
|
||||
: username
|
||||
username: user ? (
|
||||
<a className="EventPost-user" href={app.route.user(user)} config={m.route}>
|
||||
{username}
|
||||
</a>
|
||||
) : (
|
||||
username
|
||||
),
|
||||
});
|
||||
|
||||
return super.content().concat([
|
||||
icon(this.icon(), {className: 'EventPost-icon'}),
|
||||
<div class="EventPost-info">
|
||||
{this.description(data)}
|
||||
</div>
|
||||
]);
|
||||
return super.content().concat([icon(this.icon(), { className: 'EventPost-icon' }), <div class="EventPost-info">{this.description(data)}</div>]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,17 +59,22 @@ export default class ForgotPasswordModal extends Modal {
|
||||
<div className="Form Form--centered">
|
||||
<p className="helpText">{app.translator.trans('core.forum.forgot_password.text')}</p>
|
||||
<div className="Form-group">
|
||||
<input className="FormControl" name="email" type="email" placeholder={extractText(app.translator.trans('core.forum.forgot_password.email_placeholder'))}
|
||||
<input
|
||||
className="FormControl"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder={extractText(app.translator.trans('core.forum.forgot_password.email_placeholder'))}
|
||||
value={this.email()}
|
||||
onchange={m.withAttr('value', this.email)}
|
||||
disabled={this.loading} />
|
||||
disabled={this.loading}
|
||||
/>
|
||||
</div>
|
||||
<div className="Form-group">
|
||||
{Button.component({
|
||||
className: 'Button Button--primary Button--block',
|
||||
type: 'submit',
|
||||
loading: this.loading,
|
||||
children: app.translator.trans('core.forum.forgot_password.submit_button')
|
||||
children: app.translator.trans('core.forum.forgot_password.submit_button'),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
@ -82,11 +87,12 @@ export default class ForgotPasswordModal extends Modal {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
app.request({
|
||||
app
|
||||
.request({
|
||||
method: 'POST',
|
||||
url: app.forum.attribute('apiUrl') + '/forgot',
|
||||
data: {email: this.email()},
|
||||
errorHandler: this.onerror.bind(this)
|
||||
data: { email: this.email() },
|
||||
errorHandler: this.onerror.bind(this),
|
||||
})
|
||||
.then(() => {
|
||||
this.success = true;
|
||||
|
@ -8,11 +8,7 @@ import listItems from '../../common/helpers/listItems';
|
||||
*/
|
||||
export default class HeaderPrimary extends Component {
|
||||
view() {
|
||||
return (
|
||||
<ul className="Header-controls">
|
||||
{listItems(this.items().toArray())}
|
||||
</ul>
|
||||
);
|
||||
return <ul className="Header-controls">{listItems(this.items().toArray())}</ul>;
|
||||
}
|
||||
|
||||
config(isInitialized, context) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user