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:
@ -14,7 +14,7 @@ export default class AdminApplication extends Application {
|
|||||||
backUrl: () => this.forum.attribute('baseUrl'),
|
backUrl: () => this.forum.attribute('baseUrl'),
|
||||||
back: function () {
|
back: function () {
|
||||||
window.location = this.backUrl();
|
window.location = this.backUrl();
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -59,5 +59,5 @@ export default class AdminApplication extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return required;
|
return required;
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,6 @@ export default Object.assign(compat, {
|
|||||||
'components/AdminNav': AdminNav,
|
'components/AdminNav': AdminNav,
|
||||||
'components/EditCustomCssModal': EditCustomCssModal,
|
'components/EditCustomCssModal': EditCustomCssModal,
|
||||||
'components/EditGroupModal': EditGroupModal,
|
'components/EditGroupModal': EditGroupModal,
|
||||||
'routes': routes,
|
routes: routes,
|
||||||
'AdminApplication': AdminApplication
|
AdminApplication: AdminApplication,
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,9 @@ export default class AddExtensionModal extends Modal {
|
|||||||
return (
|
return (
|
||||||
<div className="Modal-body">
|
<div className="Modal-body">
|
||||||
<p>{app.translator.trans('core.admin.add_extension.temporary_text')}</p>
|
<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.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.developer_text', { a: <a href="http://flarum.org/docs/extend" target="_blank" /> })}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,11 +13,7 @@ export default class AdminLinkButton extends LinkButton {
|
|||||||
getButtonContent() {
|
getButtonContent() {
|
||||||
const content = super.getButtonContent();
|
const content = super.getButtonContent();
|
||||||
|
|
||||||
content.push(
|
content.push(<div className="AdminLinkButton-description">{this.props.description}</div>);
|
||||||
<div className="AdminLinkButton-description">
|
|
||||||
{this.props.description}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,7 @@ import ItemList from '../../common/utils/ItemList';
|
|||||||
export default class AdminNav extends Component {
|
export default class AdminNav extends Component {
|
||||||
view() {
|
view() {
|
||||||
return (
|
return (
|
||||||
<SelectDropdown
|
<SelectDropdown className="AdminNav App-titleControl" buttonClassName="Button">
|
||||||
className="AdminNav App-titleControl"
|
|
||||||
buttonClassName="Button">
|
|
||||||
{this.items().toArray()}
|
{this.items().toArray()}
|
||||||
</SelectDropdown>
|
</SelectDropdown>
|
||||||
);
|
);
|
||||||
@ -31,47 +29,65 @@ export default class AdminNav extends Component {
|
|||||||
items() {
|
items() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('dashboard', AdminLinkButton.component({
|
items.add(
|
||||||
|
'dashboard',
|
||||||
|
AdminLinkButton.component({
|
||||||
href: app.route('dashboard'),
|
href: app.route('dashboard'),
|
||||||
icon: 'far fa-chart-bar',
|
icon: 'far fa-chart-bar',
|
||||||
children: app.translator.trans('core.admin.nav.dashboard_button'),
|
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'),
|
href: app.route('basics'),
|
||||||
icon: 'fas fa-pencil-alt',
|
icon: 'fas fa-pencil-alt',
|
||||||
children: app.translator.trans('core.admin.nav.basics_button'),
|
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'),
|
href: app.route('mail'),
|
||||||
icon: 'fas fa-envelope',
|
icon: 'fas fa-envelope',
|
||||||
children: app.translator.trans('core.admin.nav.email_button'),
|
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'),
|
href: app.route('permissions'),
|
||||||
icon: 'fas fa-key',
|
icon: 'fas fa-key',
|
||||||
children: app.translator.trans('core.admin.nav.permissions_button'),
|
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'),
|
href: app.route('appearance'),
|
||||||
icon: 'fas fa-paint-brush',
|
icon: 'fas fa-paint-brush',
|
||||||
children: app.translator.trans('core.admin.nav.appearance_button'),
|
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'),
|
href: app.route('extensions'),
|
||||||
icon: 'fas fa-puzzle-piece',
|
icon: 'fas fa-puzzle-piece',
|
||||||
children: app.translator.trans('core.admin.nav.extensions_button'),
|
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;
|
return items;
|
||||||
}
|
}
|
||||||
|
@ -24,85 +24,85 @@ export default class AppearancePage extends Page {
|
|||||||
<form onsubmit={this.onsubmit.bind(this)}>
|
<form onsubmit={this.onsubmit.bind(this)}>
|
||||||
<fieldset className="AppearancePage-colors">
|
<fieldset className="AppearancePage-colors">
|
||||||
<legend>{app.translator.trans('core.admin.appearance.colors_heading')}</legend>
|
<legend>{app.translator.trans('core.admin.appearance.colors_heading')}</legend>
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.appearance.colors_text')}</div>
|
||||||
{app.translator.trans('core.admin.appearance.colors_text')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="AppearancePage-colors-input">
|
<div className="AppearancePage-colors-input">
|
||||||
<input className="FormControl" type="text" placeholder="#aaaaaa" value={this.primaryColor()} onchange={m.withAttr('value', this.primaryColor)}/>
|
<input
|
||||||
<input className="FormControl" type="text" placeholder="#aaaaaa" value={this.secondaryColor()} onchange={m.withAttr('value', this.secondaryColor)}/>
|
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>
|
</div>
|
||||||
|
|
||||||
{Switch.component({
|
{Switch.component({
|
||||||
state: this.darkMode(),
|
state: this.darkMode(),
|
||||||
children: app.translator.trans('core.admin.appearance.dark_mode_label'),
|
children: app.translator.trans('core.admin.appearance.dark_mode_label'),
|
||||||
onchange: this.darkMode
|
onchange: this.darkMode,
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{Switch.component({
|
{Switch.component({
|
||||||
state: this.coloredHeader(),
|
state: this.coloredHeader(),
|
||||||
children: app.translator.trans('core.admin.appearance.colored_header_label'),
|
children: app.translator.trans('core.admin.appearance.colored_header_label'),
|
||||||
onchange: this.coloredHeader
|
onchange: this.coloredHeader,
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button Button--primary',
|
className: 'Button Button--primary',
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
children: app.translator.trans('core.admin.appearance.submit_button'),
|
children: app.translator.trans('core.admin.appearance.submit_button'),
|
||||||
loading: this.loading
|
loading: this.loading,
|
||||||
})}
|
})}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{app.translator.trans('core.admin.appearance.logo_heading')}</legend>
|
<legend>{app.translator.trans('core.admin.appearance.logo_heading')}</legend>
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.appearance.logo_text')}</div>
|
||||||
{app.translator.trans('core.admin.appearance.logo_text')}
|
|
||||||
</div>
|
|
||||||
<UploadImageButton name="logo" />
|
<UploadImageButton name="logo" />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{app.translator.trans('core.admin.appearance.favicon_heading')}</legend>
|
<legend>{app.translator.trans('core.admin.appearance.favicon_heading')}</legend>
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.appearance.favicon_text')}</div>
|
||||||
{app.translator.trans('core.admin.appearance.favicon_text')}
|
|
||||||
</div>
|
|
||||||
<UploadImageButton name="favicon" />
|
<UploadImageButton name="favicon" />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{app.translator.trans('core.admin.appearance.custom_header_heading')}</legend>
|
<legend>{app.translator.trans('core.admin.appearance.custom_header_heading')}</legend>
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.appearance.custom_header_text')}</div>
|
||||||
{app.translator.trans('core.admin.appearance.custom_header_text')}
|
|
||||||
</div>
|
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button',
|
className: 'Button',
|
||||||
children: app.translator.trans('core.admin.appearance.edit_header_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>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{app.translator.trans('core.admin.appearance.custom_footer_heading')}</legend>
|
<legend>{app.translator.trans('core.admin.appearance.custom_footer_heading')}</legend>
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.appearance.custom_footer_text')}</div>
|
||||||
{app.translator.trans('core.admin.appearance.custom_footer_text')}
|
|
||||||
</div>
|
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button',
|
className: 'Button',
|
||||||
children: app.translator.trans('core.admin.appearance.edit_footer_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>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{app.translator.trans('core.admin.appearance.custom_styles_heading')}</legend>
|
<legend>{app.translator.trans('core.admin.appearance.custom_styles_heading')}</legend>
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.appearance.custom_styles_text')}</div>
|
||||||
{app.translator.trans('core.admin.appearance.custom_styles_text')}
|
|
||||||
</div>
|
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button',
|
className: 'Button',
|
||||||
children: app.translator.trans('core.admin.appearance.edit_css_button'),
|
children: app.translator.trans('core.admin.appearance.edit_css_button'),
|
||||||
onclick: () => app.modal.show(new EditCustomCssModal())
|
onclick: () => app.modal.show(new EditCustomCssModal()),
|
||||||
})}
|
})}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
@ -126,7 +126,7 @@ export default class AppearancePage extends Page {
|
|||||||
theme_primary_color: this.primaryColor(),
|
theme_primary_color: this.primaryColor(),
|
||||||
theme_secondary_color: this.secondaryColor(),
|
theme_secondary_color: this.secondaryColor(),
|
||||||
theme_dark_mode: this.darkMode(),
|
theme_dark_mode: this.darkMode(),
|
||||||
theme_colored_header: this.coloredHeader()
|
theme_colored_header: this.coloredHeader(),
|
||||||
}).then(() => window.location.reload());
|
}).then(() => window.location.reload());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ export default class BasicsPage extends Page {
|
|||||||
'show_language_selector',
|
'show_language_selector',
|
||||||
'default_route',
|
'default_route',
|
||||||
'welcome_title',
|
'welcome_title',
|
||||||
'welcome_message'
|
'welcome_message',
|
||||||
];
|
];
|
||||||
this.values = {};
|
this.values = {};
|
||||||
|
|
||||||
const settings = app.data.settings;
|
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 = {};
|
this.localeOptions = {};
|
||||||
const locales = app.data.locales;
|
const locales = app.data.locales;
|
||||||
@ -33,7 +33,7 @@ export default class BasicsPage extends Page {
|
|||||||
this.localeOptions[i] = `${locales[i]} (${i})`;
|
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() {
|
view() {
|
||||||
@ -43,19 +43,19 @@ export default class BasicsPage extends Page {
|
|||||||
<form onsubmit={this.onsubmit.bind(this)}>
|
<form onsubmit={this.onsubmit.bind(this)}>
|
||||||
{FieldSet.component({
|
{FieldSet.component({
|
||||||
label: app.translator.trans('core.admin.basics.forum_title_heading'),
|
label: app.translator.trans('core.admin.basics.forum_title_heading'),
|
||||||
children: [
|
children: [<input className="FormControl" value={this.values.forum_title()} oninput={m.withAttr('value', this.values.forum_title)} />],
|
||||||
<input className="FormControl" value={this.values.forum_title()} oninput={m.withAttr('value', this.values.forum_title)}/>
|
|
||||||
]
|
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{FieldSet.component({
|
{FieldSet.component({
|
||||||
label: app.translator.trans('core.admin.basics.forum_description_heading'),
|
label: app.translator.trans('core.admin.basics.forum_description_heading'),
|
||||||
children: [
|
children: [
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.basics.forum_description_text')}</div>,
|
||||||
{app.translator.trans('core.admin.basics.forum_description_text')}
|
<textarea
|
||||||
</div>,
|
className="FormControl"
|
||||||
<textarea className="FormControl" value={this.values.forum_description()} oninput={m.withAttr('value', this.values.forum_description)}/>
|
value={this.values.forum_description()}
|
||||||
]
|
oninput={m.withAttr('value', this.values.forum_description)}
|
||||||
|
/>,
|
||||||
|
],
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{Object.keys(this.localeOptions).length > 1
|
{Object.keys(this.localeOptions).length > 1
|
||||||
@ -65,14 +65,14 @@ export default class BasicsPage extends Page {
|
|||||||
Select.component({
|
Select.component({
|
||||||
options: this.localeOptions,
|
options: this.localeOptions,
|
||||||
value: this.values.default_locale(),
|
value: this.values.default_locale(),
|
||||||
onchange: this.values.default_locale
|
onchange: this.values.default_locale,
|
||||||
}),
|
}),
|
||||||
Switch.component({
|
Switch.component({
|
||||||
state: this.values.show_language_selector(),
|
state: this.values.show_language_selector(),
|
||||||
onchange: this.values.show_language_selector,
|
onchange: this.values.show_language_selector,
|
||||||
children: app.translator.trans('core.admin.basics.show_language_selector_label'),
|
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'),
|
label: app.translator.trans('core.admin.basics.home_page_heading'),
|
||||||
className: 'BasicsPage-homePage',
|
className: 'BasicsPage-homePage',
|
||||||
children: [
|
children: [
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.basics.home_page_text')}</div>,
|
||||||
{app.translator.trans('core.admin.basics.home_page_text')}
|
this.homePageItems()
|
||||||
</div>,
|
.toArray()
|
||||||
this.homePageItems().toArray().map(({path, label}) =>
|
.map(({ path, label }) => (
|
||||||
<label className="checkbox">
|
<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}
|
||||||
</label>
|
</label>
|
||||||
)
|
)),
|
||||||
]
|
],
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{FieldSet.component({
|
{FieldSet.component({
|
||||||
label: app.translator.trans('core.admin.basics.welcome_banner_heading'),
|
label: app.translator.trans('core.admin.basics.welcome_banner_heading'),
|
||||||
className: 'BasicsPage-welcomeBanner',
|
className: 'BasicsPage-welcomeBanner',
|
||||||
children: [
|
children: [
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.basics.welcome_banner_text')}</div>,
|
||||||
{app.translator.trans('core.admin.basics.welcome_banner_text')}
|
|
||||||
</div>,
|
|
||||||
<div className="BasicsPage-welcomeBanner-input">
|
<div className="BasicsPage-welcomeBanner-input">
|
||||||
<input className="FormControl" value={this.values.welcome_title()} oninput={m.withAttr('value', this.values.welcome_title)} />
|
<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)}/>
|
<textarea
|
||||||
</div>
|
className="FormControl"
|
||||||
]
|
value={this.values.welcome_message()}
|
||||||
|
oninput={m.withAttr('value', this.values.welcome_message)}
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
],
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{Button.component({
|
{Button.component({
|
||||||
@ -111,7 +119,7 @@ export default class BasicsPage extends Page {
|
|||||||
className: 'Button Button--primary',
|
className: 'Button Button--primary',
|
||||||
children: app.translator.trans('core.admin.basics.submit_button'),
|
children: app.translator.trans('core.admin.basics.submit_button'),
|
||||||
loading: this.loading,
|
loading: this.loading,
|
||||||
disabled: !this.changed()
|
disabled: !this.changed(),
|
||||||
})}
|
})}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -120,7 +128,7 @@ export default class BasicsPage extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changed() {
|
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', {
|
items.add('allDiscussions', {
|
||||||
path: '/all',
|
path: '/all',
|
||||||
label: app.translator.trans('core.admin.basics.all_discussions_label')
|
label: app.translator.trans('core.admin.basics.all_discussions_label'),
|
||||||
});
|
});
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
@ -151,11 +159,11 @@ export default class BasicsPage extends Page {
|
|||||||
|
|
||||||
const settings = {};
|
const settings = {};
|
||||||
|
|
||||||
this.fields.forEach(key => settings[key] = this.values[key]());
|
this.fields.forEach((key) => (settings[key] = this.values[key]()));
|
||||||
|
|
||||||
saveSettings(settings)
|
saveSettings(settings)
|
||||||
.then(() => {
|
.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(() => {})
|
.catch(() => {})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -5,9 +5,7 @@ export default class DashboardPage extends Page {
|
|||||||
view() {
|
view() {
|
||||||
return (
|
return (
|
||||||
<div className="DashboardPage">
|
<div className="DashboardPage">
|
||||||
<div className="container">
|
<div className="container">{this.availableWidgets()}</div>
|
||||||
{this.availableWidgets()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,7 @@ import Component from '../../common/Component';
|
|||||||
|
|
||||||
export default class Widget extends Component {
|
export default class Widget extends Component {
|
||||||
view() {
|
view() {
|
||||||
return (
|
return <div className={'Widget ' + this.className()}>{this.content()}</div>;
|
||||||
<div className={"Widget "+this.className()}>
|
|
||||||
{this.content()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,10 +11,14 @@ export default class EditCustomCssModal extends SettingsModal {
|
|||||||
|
|
||||||
form() {
|
form() {
|
||||||
return [
|
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">
|
<div className="Form-group">
|
||||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_less')} />
|
<textarea className="FormControl" rows="30" bidi={this.setting('custom_less')} />
|
||||||
</div>
|
</div>,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export default class EditCustomFooterModal extends SettingsModal {
|
|||||||
<p>{app.translator.trans('core.admin.edit_footer.customize_text')}</p>,
|
<p>{app.translator.trans('core.admin.edit_footer.customize_text')}</p>,
|
||||||
<div className="Form-group">
|
<div className="Form-group">
|
||||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_footer')} />
|
<textarea className="FormControl" rows="30" bidi={this.setting('custom_footer')} />
|
||||||
</div>
|
</div>,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export default class EditCustomHeaderModal extends SettingsModal {
|
|||||||
<p>{app.translator.trans('core.admin.edit_header.customize_text')}</p>,
|
<p>{app.translator.trans('core.admin.edit_header.customize_text')}</p>,
|
||||||
<div className="Form-group">
|
<div className="Form-group">
|
||||||
<textarea className="FormControl" rows="30" bidi={this.setting('custom_header')} />
|
<textarea className="FormControl" rows="30" bidi={this.setting('custom_header')} />
|
||||||
</div>
|
</div>,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,21 +24,21 @@ export default class EditGroupModal extends Modal {
|
|||||||
|
|
||||||
title() {
|
title() {
|
||||||
return [
|
return [
|
||||||
this.color() || this.icon() ? Badge.component({
|
this.color() || this.icon()
|
||||||
|
? Badge.component({
|
||||||
icon: this.icon(),
|
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() {
|
content() {
|
||||||
return (
|
return (
|
||||||
<div className="Modal-body">
|
<div className="Modal-body">
|
||||||
<div className="Form">
|
<div className="Form">{this.fields().toArray()}</div>
|
||||||
{this.fields().toArray()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -46,40 +46,68 @@ export default class EditGroupModal extends Modal {
|
|||||||
fields() {
|
fields() {
|
||||||
const items = new ItemList();
|
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>
|
<label>{app.translator.trans('core.admin.edit_group.name_label')}</label>
|
||||||
<div className="EditGroupModal-name-input">
|
<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
|
||||||
<input className="FormControl" placeholder={app.translator.trans('core.admin.edit_group.plural_placeholder')} value={this.namePlural()} oninput={m.withAttr('value', this.namePlural)}/>
|
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>
|
||||||
</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>
|
<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)} />
|
<input className="FormControl" placeholder="#aaaaaa" value={this.color()} oninput={m.withAttr('value', this.color)} />
|
||||||
</div>, 20);
|
</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>
|
<label>{app.translator.trans('core.admin.edit_group.icon_label')}</label>
|
||||||
<div className="helpText">
|
<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>
|
</div>
|
||||||
<input className="FormControl" placeholder="fas fa-bolt" value={this.icon()} oninput={m.withAttr('value', this.icon)} />
|
<input className="FormControl" placeholder="fas fa-bolt" value={this.icon()} oninput={m.withAttr('value', this.icon)} />
|
||||||
</div>, 10);
|
</div>,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
items.add('submit', <div className="Form-group">
|
items.add(
|
||||||
|
'submit',
|
||||||
|
<div className="Form-group">
|
||||||
{Button.component({
|
{Button.component({
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
className: 'Button Button--primary EditGroupModal-save',
|
className: 'Button Button--primary EditGroupModal-save',
|
||||||
loading: this.loading,
|
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 ? (
|
{this.group.exists && this.group.id() !== Group.ADMINISTRATOR_ID ? (
|
||||||
<button type="button" className="Button EditGroupModal-delete" onclick={this.deleteGroup.bind(this)}>
|
<button type="button" className="Button EditGroupModal-delete" onclick={this.deleteGroup.bind(this)}>
|
||||||
{app.translator.trans('core.admin.edit_group.delete_button')}
|
{app.translator.trans('core.admin.edit_group.delete_button')}
|
||||||
</button>
|
</button>
|
||||||
) : ''}
|
) : (
|
||||||
</div>, -10);
|
''
|
||||||
|
)}
|
||||||
|
</div>,
|
||||||
|
-10
|
||||||
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -89,7 +117,7 @@ export default class EditGroupModal extends Modal {
|
|||||||
nameSingular: this.nameSingular(),
|
nameSingular: this.nameSingular(),
|
||||||
namePlural: this.namePlural(),
|
namePlural: this.namePlural(),
|
||||||
color: this.color(),
|
color: this.color(),
|
||||||
icon: this.icon()
|
icon: this.icon(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +126,8 @@ export default class EditGroupModal extends Modal {
|
|||||||
|
|
||||||
this.loading = true;
|
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))
|
.then(this.hide.bind(this))
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
@ -19,7 +19,7 @@ export default class ExtensionsPage extends Page {
|
|||||||
children: app.translator.trans('core.admin.extensions.add_button'),
|
children: app.translator.trans('core.admin.extensions.add_button'),
|
||||||
icon: 'fas fa-plus',
|
icon: 'fas fa-plus',
|
||||||
className: 'Button Button--primary',
|
className: 'Button Button--primary',
|
||||||
onclick: () => app.modal.show(new AddExtensionModal())
|
onclick: () => app.modal.show(new AddExtensionModal()),
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -27,12 +27,12 @@ export default class ExtensionsPage extends Page {
|
|||||||
<div className="ExtensionsPage-list">
|
<div className="ExtensionsPage-list">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<ul className="ExtensionList">
|
<ul className="ExtensionList">
|
||||||
{Object.keys(app.data.extensions)
|
{Object.keys(app.data.extensions).map((id) => {
|
||||||
.map(id => {
|
|
||||||
const extension = app.data.extensions[id];
|
const extension = app.data.extensions[id];
|
||||||
const controls = this.controlItems(extension.id).toArray();
|
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">
|
<div className="ExtensionListItem-content">
|
||||||
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
|
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
|
||||||
{extension.icon ? icon(extension.icon.name) : ''}
|
{extension.icon ? icon(extension.icon.name) : ''}
|
||||||
@ -42,10 +42,13 @@ export default class ExtensionsPage extends Page {
|
|||||||
className="ExtensionListItem-controls"
|
className="ExtensionListItem-controls"
|
||||||
buttonClassName="Button Button--icon Button--flat"
|
buttonClassName="Button Button--icon Button--flat"
|
||||||
menuClassName="Dropdown-menu--right"
|
menuClassName="Dropdown-menu--right"
|
||||||
icon="fas fa-ellipsis-h">
|
icon="fas fa-ellipsis-h"
|
||||||
|
>
|
||||||
{controls}
|
{controls}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
) : ''}
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
<div className="ExtensionListItem-main">
|
<div className="ExtensionListItem-main">
|
||||||
<label className="ExtensionListItem-title">
|
<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)} />{' '}
|
||||||
@ -55,7 +58,8 @@ export default class ExtensionsPage extends Page {
|
|||||||
<div className="ExtensionListItem-description">{extension.description}</div>
|
<div className="ExtensionListItem-description">{extension.description}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>;
|
</li>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -69,26 +73,34 @@ export default class ExtensionsPage extends Page {
|
|||||||
const enabled = this.isEnabled(name);
|
const enabled = this.isEnabled(name);
|
||||||
|
|
||||||
if (app.extensionSettings[name]) {
|
if (app.extensionSettings[name]) {
|
||||||
items.add('settings', Button.component({
|
items.add(
|
||||||
|
'settings',
|
||||||
|
Button.component({
|
||||||
icon: 'fas fa-cog',
|
icon: 'fas fa-cog',
|
||||||
children: app.translator.trans('core.admin.extensions.settings_button'),
|
children: app.translator.trans('core.admin.extensions.settings_button'),
|
||||||
onclick: app.extensionSettings[name]
|
onclick: app.extensionSettings[name],
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
items.add('uninstall', Button.component({
|
items.add(
|
||||||
|
'uninstall',
|
||||||
|
Button.component({
|
||||||
icon: 'far fa-trash-alt',
|
icon: 'far fa-trash-alt',
|
||||||
children: app.translator.trans('core.admin.extensions.uninstall_button'),
|
children: app.translator.trans('core.admin.extensions.uninstall_button'),
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
url: app.forum.attribute('apiUrl') + '/extensions/' + name,
|
url: app.forum.attribute('apiUrl') + '/extensions/' + name,
|
||||||
method: 'DELETE'
|
method: 'DELETE',
|
||||||
}).then(() => window.location.reload());
|
})
|
||||||
|
.then(() => window.location.reload());
|
||||||
|
|
||||||
app.modal.show(new LoadingModal());
|
app.modal.show(new LoadingModal());
|
||||||
}
|
},
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
@ -103,11 +115,13 @@ export default class ExtensionsPage extends Page {
|
|||||||
toggle(id) {
|
toggle(id) {
|
||||||
const enabled = this.isEnabled(id);
|
const enabled = this.isEnabled(id);
|
||||||
|
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
url: app.forum.attribute('apiUrl') + '/extensions/' + id,
|
url: app.forum.attribute('apiUrl') + '/extensions/' + id,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
data: {enabled: !enabled}
|
data: { enabled: !enabled },
|
||||||
}).then(() => {
|
})
|
||||||
|
.then(() => {
|
||||||
if (!enabled) localStorage.setItem('enabledExtension', id);
|
if (!enabled) localStorage.setItem('enabledExtension', id);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
|
@ -8,11 +8,7 @@ import listItems from '../../common/helpers/listItems';
|
|||||||
*/
|
*/
|
||||||
export default class HeaderPrimary extends Component {
|
export default class HeaderPrimary extends Component {
|
||||||
view() {
|
view() {
|
||||||
return (
|
return <ul className="Header-controls">{listItems(this.items().toArray())}</ul>;
|
||||||
<ul className="Header-controls">
|
|
||||||
{listItems(this.items().toArray())}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config(isInitialized, context) {
|
config(isInitialized, context) {
|
||||||
|
@ -8,11 +8,7 @@ import listItems from '../../common/helpers/listItems';
|
|||||||
*/
|
*/
|
||||||
export default class HeaderSecondary extends Component {
|
export default class HeaderSecondary extends Component {
|
||||||
view() {
|
view() {
|
||||||
return (
|
return <ul className="Header-controls">{listItems(this.items().toArray())}</ul>;
|
||||||
<ul className="Header-controls">
|
|
||||||
{listItems(this.items().toArray())}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config(isInitialized, context) {
|
config(isInitialized, context) {
|
||||||
|
@ -23,12 +23,14 @@ export default class MailPage extends Page {
|
|||||||
this.status = { sending: false, errors: {} };
|
this.status = { sending: false, errors: {} };
|
||||||
|
|
||||||
const settings = app.data.settings;
|
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',
|
method: 'GET',
|
||||||
url: app.forum.attribute('apiUrl') + '/mail-settings'
|
url: app.forum.attribute('apiUrl') + '/mail-settings',
|
||||||
}).then(response => {
|
})
|
||||||
|
.then((response) => {
|
||||||
this.driverFields = response['data']['attributes']['fields'];
|
this.driverFields = response['data']['attributes']['fields'];
|
||||||
this.status.sending = response['data']['attributes']['sending'];
|
this.status.sending = response['data']['attributes']['sending'];
|
||||||
this.status.errors = response['data']['attributes']['errors'];
|
this.status.errors = response['data']['attributes']['errors'];
|
||||||
@ -64,9 +66,7 @@ export default class MailPage extends Page {
|
|||||||
<div className="container">
|
<div className="container">
|
||||||
<form onsubmit={this.onsubmit.bind(this)}>
|
<form onsubmit={this.onsubmit.bind(this)}>
|
||||||
<h2>{app.translator.trans('core.admin.email.heading')}</h2>
|
<h2>{app.translator.trans('core.admin.email.heading')}</h2>
|
||||||
<div className="helpText">
|
<div className="helpText">{app.translator.trans('core.admin.email.text')}</div>
|
||||||
{app.translator.trans('core.admin.email.text')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{FieldSet.component({
|
{FieldSet.component({
|
||||||
label: app.translator.trans('core.admin.email.addresses_heading'),
|
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')}
|
{app.translator.trans('core.admin.email.from_label')}
|
||||||
<input className="FormControl" value={this.values.mail_from() || ''} oninput={m.withAttr('value', this.values.mail_from)} />
|
<input className="FormControl" value={this.values.mail_from() || ''} oninput={m.withAttr('value', this.values.mail_from)} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>,
|
||||||
]
|
],
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{FieldSet.component({
|
{FieldSet.component({
|
||||||
@ -88,38 +88,44 @@ export default class MailPage extends Page {
|
|||||||
<div className="MailPage-MailSettings-input">
|
<div className="MailPage-MailSettings-input">
|
||||||
<label>
|
<label>
|
||||||
{app.translator.trans('core.admin.email.driver_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>
|
</label>
|
||||||
</div>
|
</div>,
|
||||||
]
|
],
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{this.status.sending || Alert.component({
|
{this.status.sending ||
|
||||||
|
Alert.component({
|
||||||
children: app.translator.trans('core.admin.email.not_sending_message'),
|
children: app.translator.trans('core.admin.email.not_sending_message'),
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{fieldKeys.length > 0 && FieldSet.component({
|
{fieldKeys.length > 0 &&
|
||||||
|
FieldSet.component({
|
||||||
label: app.translator.trans(`core.admin.email.${this.values.mail_driver()}_heading`),
|
label: app.translator.trans(`core.admin.email.${this.values.mail_driver()}_heading`),
|
||||||
className: 'MailPage-MailSettings',
|
className: 'MailPage-MailSettings',
|
||||||
children: [
|
children: [
|
||||||
<div className="MailPage-MailSettings-input">
|
<div className="MailPage-MailSettings-input">
|
||||||
{fieldKeys.map(field => [
|
{fieldKeys.map((field) => [
|
||||||
<label>
|
<label>
|
||||||
{app.translator.trans(`core.admin.email.${field}_label`)}
|
{app.translator.trans(`core.admin.email.${field}_label`)}
|
||||||
{this.renderField(field)}
|
{this.renderField(field)}
|
||||||
</label>,
|
</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({
|
{Button.component({
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
className: 'Button Button--primary',
|
className: 'Button Button--primary',
|
||||||
children: app.translator.trans('core.admin.email.submit_button'),
|
children: app.translator.trans('core.admin.email.submit_button'),
|
||||||
disabled: !this.changed()
|
disabled: !this.changed(),
|
||||||
})}
|
})}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -140,7 +146,7 @@ export default class MailPage extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changed() {
|
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) {
|
onsubmit(e) {
|
||||||
@ -153,11 +159,11 @@ export default class MailPage extends Page {
|
|||||||
|
|
||||||
const settings = {};
|
const settings = {};
|
||||||
|
|
||||||
this.fields.forEach(key => settings[key] = this.values[key]());
|
this.fields.forEach((key) => (settings[key] = this.values[key]()));
|
||||||
|
|
||||||
saveSettings(settings)
|
saveSettings(settings)
|
||||||
.then(() => {
|
.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(() => {})
|
.catch(() => {})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -12,18 +12,17 @@ function badgeForId(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function filterByRequiredPermissions(groupIds, permission) {
|
function filterByRequiredPermissions(groupIds, permission) {
|
||||||
app.getRequiredPermissions(permission)
|
app.getRequiredPermissions(permission).forEach((required) => {
|
||||||
.forEach(required => {
|
|
||||||
const restrictToGroupIds = app.data.permissions[required] || [];
|
const restrictToGroupIds = app.data.permissions[required] || [];
|
||||||
|
|
||||||
if (restrictToGroupIds.indexOf(Group.GUEST_ID) !== -1) {
|
if (restrictToGroupIds.indexOf(Group.GUEST_ID) !== -1) {
|
||||||
// do nothing
|
// do nothing
|
||||||
} else if (restrictToGroupIds.indexOf(Group.MEMBER_ID) !== -1) {
|
} 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) {
|
} else if (groupIds.indexOf(Group.MEMBER_ID) !== -1) {
|
||||||
groupIds = restrictToGroupIds;
|
groupIds = restrictToGroupIds;
|
||||||
} else {
|
} else {
|
||||||
groupIds = restrictToGroupIds.filter(id => groupIds.indexOf(id) !== -1);
|
groupIds = restrictToGroupIds.filter((id) => groupIds.indexOf(id) !== -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
groupIds = filterByRequiredPermissions(groupIds, required);
|
groupIds = filterByRequiredPermissions(groupIds, required);
|
||||||
@ -56,10 +55,7 @@ export default class PermissionDropdown extends Dropdown {
|
|||||||
} else if (members) {
|
} else if (members) {
|
||||||
this.props.label = Badge.component({ icon: 'fas fa-user' });
|
this.props.label = Badge.component({ icon: 'fas fa-user' });
|
||||||
} else {
|
} else {
|
||||||
this.props.label = [
|
this.props.label = [badgeForId(Group.ADMINISTRATOR_ID), groupIds.map(badgeForId)];
|
||||||
badgeForId(Group.ADMINISTRATOR_ID),
|
|
||||||
groupIds.map(badgeForId)
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.showing) {
|
if (this.showing) {
|
||||||
@ -69,7 +65,7 @@ export default class PermissionDropdown extends Dropdown {
|
|||||||
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,
|
icon: everyone ? 'fas fa-check' : true,
|
||||||
onclick: () => this.save([Group.GUEST_ID]),
|
onclick: () => this.save([Group.GUEST_ID]),
|
||||||
disabled: this.isGroupDisabled(Group.GUEST_ID)
|
disabled: this.isGroupDisabled(Group.GUEST_ID),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -79,7 +75,7 @@ export default class PermissionDropdown extends Dropdown {
|
|||||||
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,
|
icon: members ? 'fas fa-check' : true,
|
||||||
onclick: () => this.save([Group.MEMBER_ID]),
|
onclick: () => this.save([Group.MEMBER_ID]),
|
||||||
disabled: this.isGroupDisabled(Group.MEMBER_ID)
|
disabled: this.isGroupDisabled(Group.MEMBER_ID),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Separator.component(),
|
Separator.component(),
|
||||||
@ -88,26 +84,29 @@ export default class PermissionDropdown extends Dropdown {
|
|||||||
children: [badgeForId(adminGroup.id()), ' ', adminGroup.namePlural()],
|
children: [badgeForId(adminGroup.id()), ' ', adminGroup.namePlural()],
|
||||||
icon: !everyone && !members ? 'fas fa-check' : true,
|
icon: !everyone && !members ? 'fas fa-check' : true,
|
||||||
disabled: !everyone && !members,
|
disabled: !everyone && !members,
|
||||||
onclick: e => {
|
onclick: (e) => {
|
||||||
if (e.shiftKey) e.stopPropagation();
|
if (e.shiftKey) e.stopPropagation();
|
||||||
this.save([]);
|
this.save([]);
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
[].push.apply(
|
[].push.apply(
|
||||||
this.props.children,
|
this.props.children,
|
||||||
app.store.all('groups')
|
app.store
|
||||||
.filter(group => [Group.ADMINISTRATOR_ID, Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
.all('groups')
|
||||||
.map(group => Button.component({
|
.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()],
|
children: [badgeForId(group.id()), ' ', group.namePlural()],
|
||||||
icon: groupIds.indexOf(group.id()) !== -1 ? 'fas fa-check' : true,
|
icon: groupIds.indexOf(group.id()) !== -1 ? 'fas fa-check' : true,
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
if (e.shiftKey) e.stopPropagation();
|
if (e.shiftKey) e.stopPropagation();
|
||||||
this.toggle(group.id());
|
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({
|
app.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: app.forum.attribute('apiUrl') + '/permission',
|
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);
|
groupIds.splice(index, 1);
|
||||||
} else {
|
} else {
|
||||||
groupIds.push(groupId);
|
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);
|
this.save(groupIds);
|
||||||
|
@ -13,12 +13,8 @@ export default class PermissionGrid extends Component {
|
|||||||
view() {
|
view() {
|
||||||
const scopes = this.scopeItems().toArray();
|
const scopes = this.scopeItems().toArray();
|
||||||
|
|
||||||
const permissionCells = permission => {
|
const permissionCells = (permission) => {
|
||||||
return scopes.map(scope => (
|
return scopes.map((scope) => <td>{scope.render(permission)}</td>);
|
||||||
<td>
|
|
||||||
{scope.render(permission)}
|
|
||||||
</td>
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -26,25 +22,30 @@ export default class PermissionGrid extends Component {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
{scopes.map(scope => (
|
{scopes.map((scope) => (
|
||||||
<th>
|
<th>
|
||||||
{scope.label}{' '}
|
{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>
|
||||||
))}
|
))}
|
||||||
<th>{this.scopeControlItems().toArray()}</th>
|
<th>{this.scopeControlItems().toArray()}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{this.permissions.map(section => (
|
{this.permissions.map((section) => (
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr className="PermissionGrid-section">
|
<tr className="PermissionGrid-section">
|
||||||
<th>{section.label}</th>
|
<th>{section.label}</th>
|
||||||
{permissionCells(section)}
|
{permissionCells(section)}
|
||||||
<td />
|
<td />
|
||||||
</tr>
|
</tr>
|
||||||
{section.children.map(child => (
|
{section.children.map((child) => (
|
||||||
<tr className="PermissionGrid-child">
|
<tr className="PermissionGrid-child">
|
||||||
<th>{icon(child.icon)}{child.label}</th>
|
<th>
|
||||||
|
{icon(child.icon)}
|
||||||
|
{child.label}
|
||||||
|
</th>
|
||||||
{permissionCells(child)}
|
{permissionCells(child)}
|
||||||
<td />
|
<td />
|
||||||
</tr>
|
</tr>
|
||||||
@ -58,25 +59,41 @@ export default class PermissionGrid extends Component {
|
|||||||
permissionItems() {
|
permissionItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('view', {
|
items.add(
|
||||||
|
'view',
|
||||||
|
{
|
||||||
label: app.translator.trans('core.admin.permissions.read_heading'),
|
label: app.translator.trans('core.admin.permissions.read_heading'),
|
||||||
children: this.viewItems().toArray()
|
children: this.viewItems().toArray(),
|
||||||
}, 100);
|
},
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
items.add('start', {
|
items.add(
|
||||||
|
'start',
|
||||||
|
{
|
||||||
label: app.translator.trans('core.admin.permissions.create_heading'),
|
label: app.translator.trans('core.admin.permissions.create_heading'),
|
||||||
children: this.startItems().toArray()
|
children: this.startItems().toArray(),
|
||||||
}, 90);
|
},
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
items.add('reply', {
|
items.add(
|
||||||
|
'reply',
|
||||||
|
{
|
||||||
label: app.translator.trans('core.admin.permissions.participate_heading'),
|
label: app.translator.trans('core.admin.permissions.participate_heading'),
|
||||||
children: this.replyItems().toArray()
|
children: this.replyItems().toArray(),
|
||||||
}, 80);
|
},
|
||||||
|
80
|
||||||
|
);
|
||||||
|
|
||||||
items.add('moderate', {
|
items.add(
|
||||||
|
'moderate',
|
||||||
|
{
|
||||||
label: app.translator.trans('core.admin.permissions.moderate_heading'),
|
label: app.translator.trans('core.admin.permissions.moderate_heading'),
|
||||||
children: this.moderateItems().toArray()
|
children: this.moderateItems().toArray(),
|
||||||
}, 70);
|
},
|
||||||
|
70
|
||||||
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -84,31 +101,44 @@ export default class PermissionGrid extends Component {
|
|||||||
viewItems() {
|
viewItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('viewDiscussions', {
|
items.add(
|
||||||
|
'viewDiscussions',
|
||||||
|
{
|
||||||
icon: 'fas fa-eye',
|
icon: 'fas fa-eye',
|
||||||
label: app.translator.trans('core.admin.permissions.view_discussions_label'),
|
label: app.translator.trans('core.admin.permissions.view_discussions_label'),
|
||||||
permission: 'viewDiscussions',
|
permission: 'viewDiscussions',
|
||||||
allowGuest: true
|
allowGuest: true,
|
||||||
}, 100);
|
},
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
items.add('viewUserList', {
|
items.add(
|
||||||
|
'viewUserList',
|
||||||
|
{
|
||||||
icon: 'fas fa-users',
|
icon: 'fas fa-users',
|
||||||
label: app.translator.trans('core.admin.permissions.view_user_list_label'),
|
label: app.translator.trans('core.admin.permissions.view_user_list_label'),
|
||||||
permission: 'viewUserList',
|
permission: 'viewUserList',
|
||||||
allowGuest: true
|
allowGuest: true,
|
||||||
}, 100);
|
},
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
items.add('signUp', {
|
items.add(
|
||||||
|
'signUp',
|
||||||
|
{
|
||||||
icon: 'fas fa-user-plus',
|
icon: 'fas fa-user-plus',
|
||||||
label: app.translator.trans('core.admin.permissions.sign_up_label'),
|
label: app.translator.trans('core.admin.permissions.sign_up_label'),
|
||||||
setting: () => SettingDropdown.component({
|
setting: () =>
|
||||||
|
SettingDropdown.component({
|
||||||
key: 'allow_sign_up',
|
key: 'allow_sign_up',
|
||||||
options: [
|
options: [
|
||||||
{ value: '1', label: app.translator.trans('core.admin.permissions_controls.signup_open_button') },
|
{ 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')}
|
{ value: '0', label: app.translator.trans('core.admin.permissions_controls.signup_closed_button') },
|
||||||
]
|
],
|
||||||
})
|
}),
|
||||||
}, 90);
|
},
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
items.add('viewLastSeenAt', {
|
items.add('viewLastSeenAt', {
|
||||||
icon: 'far fa-clock',
|
icon: 'far fa-clock',
|
||||||
@ -122,13 +152,19 @@ export default class PermissionGrid extends Component {
|
|||||||
startItems() {
|
startItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('start', {
|
items.add(
|
||||||
|
'start',
|
||||||
|
{
|
||||||
icon: 'fas fa-edit',
|
icon: 'fas fa-edit',
|
||||||
label: app.translator.trans('core.admin.permissions.start_discussions_label'),
|
label: app.translator.trans('core.admin.permissions.start_discussions_label'),
|
||||||
permission: 'startDiscussion'
|
permission: 'startDiscussion',
|
||||||
}, 100);
|
},
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
items.add('allowRenaming', {
|
items.add(
|
||||||
|
'allowRenaming',
|
||||||
|
{
|
||||||
icon: 'fas fa-i-cursor',
|
icon: 'fas fa-i-cursor',
|
||||||
label: app.translator.trans('core.admin.permissions.allow_renaming_label'),
|
label: app.translator.trans('core.admin.permissions.allow_renaming_label'),
|
||||||
setting: () => {
|
setting: () => {
|
||||||
@ -142,11 +178,13 @@ export default class PermissionGrid extends Component {
|
|||||||
options: [
|
options: [
|
||||||
{ value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_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: '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: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button') },
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}, 90);
|
},
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -154,13 +192,19 @@ export default class PermissionGrid extends Component {
|
|||||||
replyItems() {
|
replyItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('reply', {
|
items.add(
|
||||||
|
'reply',
|
||||||
|
{
|
||||||
icon: 'fas fa-reply',
|
icon: 'fas fa-reply',
|
||||||
label: app.translator.trans('core.admin.permissions.reply_to_discussions_label'),
|
label: app.translator.trans('core.admin.permissions.reply_to_discussions_label'),
|
||||||
permission: 'discussion.reply'
|
permission: 'discussion.reply',
|
||||||
}, 100);
|
},
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
items.add('allowPostEditing', {
|
items.add(
|
||||||
|
'allowPostEditing',
|
||||||
|
{
|
||||||
icon: 'fas fa-pencil-alt',
|
icon: 'fas fa-pencil-alt',
|
||||||
label: app.translator.trans('core.admin.permissions.allow_post_editing_label'),
|
label: app.translator.trans('core.admin.permissions.allow_post_editing_label'),
|
||||||
setting: () => {
|
setting: () => {
|
||||||
@ -174,11 +218,13 @@ export default class PermissionGrid extends Component {
|
|||||||
options: [
|
options: [
|
||||||
{ value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_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: '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: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button') },
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}, 90);
|
},
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -186,59 +232,95 @@ export default class PermissionGrid extends Component {
|
|||||||
moderateItems() {
|
moderateItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('viewIpsPosts', {
|
items.add(
|
||||||
|
'viewIpsPosts',
|
||||||
|
{
|
||||||
icon: 'fas fa-bullseye',
|
icon: 'fas fa-bullseye',
|
||||||
label: app.translator.trans('core.admin.permissions.view_post_ips_label'),
|
label: app.translator.trans('core.admin.permissions.view_post_ips_label'),
|
||||||
permission: 'discussion.viewIpsPosts'
|
permission: 'discussion.viewIpsPosts',
|
||||||
}, 110);
|
},
|
||||||
|
110
|
||||||
|
);
|
||||||
|
|
||||||
items.add('renameDiscussions', {
|
items.add(
|
||||||
|
'renameDiscussions',
|
||||||
|
{
|
||||||
icon: 'fas fa-i-cursor',
|
icon: 'fas fa-i-cursor',
|
||||||
label: app.translator.trans('core.admin.permissions.rename_discussions_label'),
|
label: app.translator.trans('core.admin.permissions.rename_discussions_label'),
|
||||||
permission: 'discussion.rename'
|
permission: 'discussion.rename',
|
||||||
}, 100);
|
},
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
items.add('hideDiscussions', {
|
items.add(
|
||||||
|
'hideDiscussions',
|
||||||
|
{
|
||||||
icon: 'far fa-trash-alt',
|
icon: 'far fa-trash-alt',
|
||||||
label: app.translator.trans('core.admin.permissions.delete_discussions_label'),
|
label: app.translator.trans('core.admin.permissions.delete_discussions_label'),
|
||||||
permission: 'discussion.hide'
|
permission: 'discussion.hide',
|
||||||
}, 90);
|
},
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
items.add('deleteDiscussions', {
|
items.add(
|
||||||
|
'deleteDiscussions',
|
||||||
|
{
|
||||||
icon: 'fas fa-times',
|
icon: 'fas fa-times',
|
||||||
label: app.translator.trans('core.admin.permissions.delete_discussions_forever_label'),
|
label: app.translator.trans('core.admin.permissions.delete_discussions_forever_label'),
|
||||||
permission: 'discussion.delete'
|
permission: 'discussion.delete',
|
||||||
}, 80);
|
},
|
||||||
|
80
|
||||||
|
);
|
||||||
|
|
||||||
items.add('postWithoutThrottle', {
|
items.add(
|
||||||
|
'postWithoutThrottle',
|
||||||
|
{
|
||||||
icon: 'fas fa-swimmer',
|
icon: 'fas fa-swimmer',
|
||||||
label: app.translator.trans('core.admin.permissions.post_without_throttle_label'),
|
label: app.translator.trans('core.admin.permissions.post_without_throttle_label'),
|
||||||
permission: 'postWithoutThrottle'
|
permission: 'postWithoutThrottle',
|
||||||
}, 70);
|
},
|
||||||
|
70
|
||||||
|
);
|
||||||
|
|
||||||
items.add('editPosts', {
|
items.add(
|
||||||
|
'editPosts',
|
||||||
|
{
|
||||||
icon: 'fas fa-pencil-alt',
|
icon: 'fas fa-pencil-alt',
|
||||||
label: app.translator.trans('core.admin.permissions.edit_posts_label'),
|
label: app.translator.trans('core.admin.permissions.edit_posts_label'),
|
||||||
permission: 'discussion.editPosts'
|
permission: 'discussion.editPosts',
|
||||||
}, 70);
|
},
|
||||||
|
70
|
||||||
|
);
|
||||||
|
|
||||||
items.add('hidePosts', {
|
items.add(
|
||||||
|
'hidePosts',
|
||||||
|
{
|
||||||
icon: 'far fa-trash-alt',
|
icon: 'far fa-trash-alt',
|
||||||
label: app.translator.trans('core.admin.permissions.delete_posts_label'),
|
label: app.translator.trans('core.admin.permissions.delete_posts_label'),
|
||||||
permission: 'discussion.hidePosts'
|
permission: 'discussion.hidePosts',
|
||||||
}, 60);
|
},
|
||||||
|
60
|
||||||
|
);
|
||||||
|
|
||||||
items.add('deletePosts', {
|
items.add(
|
||||||
|
'deletePosts',
|
||||||
|
{
|
||||||
icon: 'fas fa-times',
|
icon: 'fas fa-times',
|
||||||
label: app.translator.trans('core.admin.permissions.delete_posts_forever_label'),
|
label: app.translator.trans('core.admin.permissions.delete_posts_forever_label'),
|
||||||
permission: 'discussion.deletePosts'
|
permission: 'discussion.deletePosts',
|
||||||
}, 60);
|
},
|
||||||
|
60
|
||||||
|
);
|
||||||
|
|
||||||
items.add('userEdit', {
|
items.add(
|
||||||
|
'userEdit',
|
||||||
|
{
|
||||||
icon: 'fas fa-user-cog',
|
icon: 'fas fa-user-cog',
|
||||||
label: app.translator.trans('core.admin.permissions.edit_users_label'),
|
label: app.translator.trans('core.admin.permissions.edit_users_label'),
|
||||||
permission: 'user.edit'
|
permission: 'user.edit',
|
||||||
}, 60);
|
},
|
||||||
|
60
|
||||||
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -246,21 +328,25 @@ export default class PermissionGrid extends Component {
|
|||||||
scopeItems() {
|
scopeItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('global', {
|
items.add(
|
||||||
|
'global',
|
||||||
|
{
|
||||||
label: app.translator.trans('core.admin.permissions.global_heading'),
|
label: app.translator.trans('core.admin.permissions.global_heading'),
|
||||||
render: item => {
|
render: (item) => {
|
||||||
if (item.setting) {
|
if (item.setting) {
|
||||||
return item.setting();
|
return item.setting();
|
||||||
} else if (item.permission) {
|
} else if (item.permission) {
|
||||||
return PermissionDropdown.component({
|
return PermissionDropdown.component({
|
||||||
permission: item.permission,
|
permission: item.permission,
|
||||||
allowGuest: item.allowGuest
|
allowGuest: item.allowGuest,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
},
|
||||||
}, 100);
|
},
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,15 @@ export default class PermissionsPage extends Page {
|
|||||||
<div className="PermissionsPage">
|
<div className="PermissionsPage">
|
||||||
<div className="PermissionsPage-groups">
|
<div className="PermissionsPage-groups">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
{app.store.all('groups')
|
{app.store
|
||||||
.filter(group => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
.all('groups')
|
||||||
.map(group => (
|
.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 }))}>
|
<button className="Button Group" onclick={() => app.modal.show(new EditGroupModal({ group }))}>
|
||||||
{GroupBadge.component({
|
{GroupBadge.component({
|
||||||
group,
|
group,
|
||||||
className: 'Group-icon',
|
className: 'Group-icon',
|
||||||
label: null
|
label: null,
|
||||||
})}
|
})}
|
||||||
<span className="Group-name">{group.namePlural()}</span>
|
<span className="Group-name">{group.namePlural()}</span>
|
||||||
</button>
|
</button>
|
||||||
@ -31,9 +32,7 @@ export default class PermissionsPage extends Page {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="PermissionsPage-permissions">
|
<div className="PermissionsPage-permissions">
|
||||||
<div className="container">
|
<div className="container">{PermissionGrid.component()}</div>
|
||||||
{PermissionGrid.component()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -26,10 +26,7 @@ export default class SessionDropdown extends Dropdown {
|
|||||||
getButtonContent() {
|
getButtonContent() {
|
||||||
const user = app.session.user;
|
const user = app.session.user;
|
||||||
|
|
||||||
return [
|
return [avatar(user), ' ', <span className="Button-label">{username(user)}</span>];
|
||||||
avatar(user), ' ',
|
|
||||||
<span className="Button-label">{username(user)}</span>
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,11 +37,12 @@ export default class SessionDropdown extends Dropdown {
|
|||||||
items() {
|
items() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('logOut',
|
items.add(
|
||||||
|
'logOut',
|
||||||
Button.component({
|
Button.component({
|
||||||
icon: 'fas fa-sign-out-alt',
|
icon: 'fas fa-sign-out-alt',
|
||||||
children: app.translator.trans('core.admin.header.log_out_button'),
|
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
|
-100
|
||||||
);
|
);
|
||||||
|
@ -18,7 +18,7 @@ export default class SettingDropdown extends SelectDropdown {
|
|||||||
children: label,
|
children: label,
|
||||||
icon: active ? 'fas fa-check' : true,
|
icon: active ? 'fas fa-check' : true,
|
||||||
onclick: saveSettings.bind(this, { [props.key]: value }),
|
onclick: saveSettings.bind(this, { [props.key]: value }),
|
||||||
active
|
active,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,7 @@ export default class SettingsModal extends Modal {
|
|||||||
<div className="Form">
|
<div className="Form">
|
||||||
{this.form()}
|
{this.form()}
|
||||||
|
|
||||||
<div className="Form-group">
|
<div className="Form-group">{this.submitButton()}</div>
|
||||||
{this.submitButton()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -28,11 +26,7 @@ export default class SettingsModal extends Modal {
|
|||||||
|
|
||||||
submitButton() {
|
submitButton() {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button type="submit" className="Button Button--primary" loading={this.loading} disabled={!this.changed()}>
|
||||||
type="submit"
|
|
||||||
className="Button Button--primary"
|
|
||||||
loading={this.loading}
|
|
||||||
disabled={!this.changed()}>
|
|
||||||
{app.translator.trans('core.admin.settings.submit_button')}
|
{app.translator.trans('core.admin.settings.submit_button')}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@ -47,7 +41,7 @@ export default class SettingsModal extends Modal {
|
|||||||
dirty() {
|
dirty() {
|
||||||
const dirty = {};
|
const dirty = {};
|
||||||
|
|
||||||
Object.keys(this.settings).forEach(key => {
|
Object.keys(this.settings).forEach((key) => {
|
||||||
const value = this.settings[key]();
|
const value = this.settings[key]();
|
||||||
|
|
||||||
if (value !== app.data.settings[key]) {
|
if (value !== app.data.settings[key]) {
|
||||||
@ -67,10 +61,7 @@ export default class SettingsModal extends Modal {
|
|||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
saveSettings(this.dirty()).then(
|
saveSettings(this.dirty()).then(this.onsaved.bind(this), this.loaded.bind(this));
|
||||||
this.onsaved.bind(this),
|
|
||||||
this.loaded.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onsaved() {
|
onsaved() {
|
||||||
|
@ -20,25 +20,23 @@ export default class StatusWidget extends DashboardWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
content() {
|
content() {
|
||||||
return (
|
return <ul>{listItems(this.items().toArray())}</ul>;
|
||||||
<ul>{listItems(this.items().toArray())}</ul>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
items() {
|
items() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('tools', (
|
items.add(
|
||||||
|
'tools',
|
||||||
<Dropdown
|
<Dropdown
|
||||||
label={app.translator.trans('core.admin.dashboard.tools_button')}
|
label={app.translator.trans('core.admin.dashboard.tools_button')}
|
||||||
icon="fas fa-cog"
|
icon="fas fa-cog"
|
||||||
buttonClassName="Button"
|
buttonClassName="Button"
|
||||||
menuClassName="Dropdown-menu--right">
|
menuClassName="Dropdown-menu--right"
|
||||||
<Button onclick={this.handleClearCache.bind(this)}>
|
>
|
||||||
{app.translator.trans('core.admin.dashboard.clear_cache_button')}
|
<Button onclick={this.handleClearCache.bind(this)}>{app.translator.trans('core.admin.dashboard.clear_cache_button')}</Button>
|
||||||
</Button>
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
));
|
);
|
||||||
|
|
||||||
items.add('version-flarum', [<strong>Flarum</strong>, <br />, app.forum.attribute('version')]);
|
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-php', [<strong>PHP</strong>, <br />, app.data.phpVersion]);
|
||||||
@ -50,9 +48,11 @@ export default class StatusWidget extends DashboardWidget {
|
|||||||
handleClearCache(e) {
|
handleClearCache(e) {
|
||||||
app.modal.show(new LoadingModal());
|
app.modal.show(new LoadingModal());
|
||||||
|
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: app.forum.attribute('apiUrl') + '/cache'
|
url: app.forum.attribute('apiUrl') + '/cache',
|
||||||
}).then(() => window.location.reload());
|
})
|
||||||
|
.then(() => window.location.reload());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,9 @@ export default class UploadImageButton extends Button {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<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>
|
<p>{super.view()}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -35,22 +37,25 @@ export default class UploadImageButton extends Button {
|
|||||||
|
|
||||||
const $input = $('<input type="file">');
|
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();
|
const data = new FormData();
|
||||||
data.append(this.props.name, $(e.target)[0].files[0]);
|
data.append(this.props.name, $(e.target)[0].files[0]);
|
||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
m.redraw();
|
m.redraw();
|
||||||
|
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: this.resourceUrl(),
|
url: this.resourceUrl(),
|
||||||
serialize: raw => raw,
|
serialize: (raw) => raw,
|
||||||
data
|
data,
|
||||||
}).then(
|
})
|
||||||
this.success.bind(this),
|
.then(this.success.bind(this), this.failure.bind(this));
|
||||||
this.failure.bind(this)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,13 +66,12 @@ export default class UploadImageButton extends Button {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
m.redraw();
|
m.redraw();
|
||||||
|
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: this.resourceUrl()
|
url: this.resourceUrl(),
|
||||||
}).then(
|
})
|
||||||
this.success.bind(this),
|
.then(this.success.bind(this), this.failure.bind(this));
|
||||||
this.failure.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceUrl() {
|
resourceUrl() {
|
||||||
|
@ -11,11 +11,7 @@ import Component from '../../common/Component';
|
|||||||
|
|
||||||
export default class DashboardWidget extends Component {
|
export default class DashboardWidget extends Component {
|
||||||
view() {
|
view() {
|
||||||
return (
|
return <div className={'DashboardWidget ' + this.className()}>{this.content()}</div>;
|
||||||
<div className={"DashboardWidget "+this.className()}>
|
|
||||||
{this.content()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +9,6 @@ export { app };
|
|||||||
|
|
||||||
// Export public API
|
// Export public API
|
||||||
|
|
||||||
|
|
||||||
// Export compat API
|
// Export compat API
|
||||||
import compat from './compat';
|
import compat from './compat';
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@ import MailPage from './components/MailPage';
|
|||||||
*/
|
*/
|
||||||
export default function (app) {
|
export default function (app) {
|
||||||
app.routes = {
|
app.routes = {
|
||||||
'dashboard': {path: '/', component: DashboardPage.component()},
|
dashboard: { path: '/', component: DashboardPage.component() },
|
||||||
'basics': {path: '/basics', component: BasicsPage.component()},
|
basics: { path: '/basics', component: BasicsPage.component() },
|
||||||
'permissions': {path: '/permissions', component: PermissionsPage.component()},
|
permissions: { path: '/permissions', component: PermissionsPage.component() },
|
||||||
'appearance': {path: '/appearance', component: AppearancePage.component()},
|
appearance: { path: '/appearance', component: AppearancePage.component() },
|
||||||
'extensions': {path: '/extensions', component: ExtensionsPage.component()},
|
extensions: { path: '/extensions', component: ExtensionsPage.component() },
|
||||||
'mail': {path: '/mail', component: MailPage.component()}
|
mail: { path: '/mail', component: MailPage.component() },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,13 @@ export default function saveSettings(settings) {
|
|||||||
|
|
||||||
Object.assign(app.data.settings, settings);
|
Object.assign(app.data.settings, settings);
|
||||||
|
|
||||||
return app.request({
|
return app
|
||||||
|
.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: app.forum.attribute('apiUrl') + '/settings',
|
url: app.forum.attribute('apiUrl') + '/settings',
|
||||||
data: settings
|
data: settings,
|
||||||
}).catch(error => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
app.data.settings = oldSettings;
|
app.data.settings = oldSettings;
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
@ -86,7 +86,7 @@ export default class Application {
|
|||||||
discussions: Discussion,
|
discussions: Discussion,
|
||||||
posts: Post,
|
posts: Post,
|
||||||
groups: Group,
|
groups: Group,
|
||||||
notifications: Notification
|
notifications: Notification,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,22 +126,19 @@ export default class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boot() {
|
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.forum = this.store.getById('forums', 1);
|
||||||
|
|
||||||
this.session = new Session(
|
this.session = new Session(this.store.getById('users', this.data.session.userId), this.data.session.csrfToken);
|
||||||
this.store.getById('users', this.data.session.userId),
|
|
||||||
this.data.session.csrfToken
|
|
||||||
);
|
|
||||||
|
|
||||||
this.mount();
|
this.mount();
|
||||||
}
|
}
|
||||||
|
|
||||||
bootExtensions(extensions) {
|
bootExtensions(extensions) {
|
||||||
Object.keys(extensions).forEach(name => {
|
Object.keys(extensions).forEach((name) => {
|
||||||
const extension = extensions[name];
|
const extension = extensions[name];
|
||||||
|
|
||||||
const extenders = flattenDeep(extension.extend);
|
const extenders = flattenDeep(extension.extend);
|
||||||
@ -158,21 +155,15 @@ export default class Application {
|
|||||||
|
|
||||||
this.drawer = new Drawer();
|
this.drawer = new Drawer();
|
||||||
|
|
||||||
m.route(
|
m.route(document.getElementById('content'), basePath + '/', mapRoutes(this.routes, basePath));
|
||||||
document.getElementById('content'),
|
|
||||||
basePath + '/',
|
|
||||||
mapRoutes(this.routes, basePath)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add a class to the body which indicates that the page has been scrolled
|
// Add a class to the body which indicates that the page has been scrolled
|
||||||
// down.
|
// down.
|
||||||
new ScrollListener(top => {
|
new ScrollListener((top) => {
|
||||||
const $app = $('#app');
|
const $app = $('#app');
|
||||||
const offset = $app.offset().top;
|
const offset = $app.offset().top;
|
||||||
|
|
||||||
$app
|
$app.toggleClass('affix', top >= offset).toggleClass('scrolled', top > offset);
|
||||||
.toggleClass('affix', top >= offset)
|
|
||||||
.toggleClass('scrolled', top > offset);
|
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
@ -220,9 +211,7 @@ export default class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTitle() {
|
updateTitle() {
|
||||||
document.title = (this.titleCount ? `(${this.titleCount}) ` : '') +
|
document.title = (this.titleCount ? `(${this.titleCount}) ` : '') + (this.title ? this.title + ' - ' : '') + this.forum.attribute('title');
|
||||||
(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
|
// 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
|
// a dud response, we don't want the application to crash. We'll show an
|
||||||
// error message to the user instead.
|
// 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;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -266,7 +257,7 @@ export default class Application {
|
|||||||
// response code and show an error message to the user if something's gone
|
// response code and show an error message to the user if something's gone
|
||||||
// awry.
|
// awry.
|
||||||
const original = options.extract;
|
const original = options.extract;
|
||||||
options.extract = xhr => {
|
options.extract = (xhr) => {
|
||||||
let responseText;
|
let responseText;
|
||||||
|
|
||||||
if (original) {
|
if (original) {
|
||||||
@ -299,7 +290,9 @@ export default class Application {
|
|||||||
// returned and show an alert containing its contents.
|
// returned and show an alert containing its contents.
|
||||||
const deferred = m.deferred();
|
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;
|
this.requestError = error;
|
||||||
|
|
||||||
let children;
|
let children;
|
||||||
@ -307,7 +300,7 @@ export default class Application {
|
|||||||
switch (error.status) {
|
switch (error.status) {
|
||||||
case 422:
|
case 422:
|
||||||
children = error.response.errors
|
children = error.response.errors
|
||||||
.map(error => [error.detail, <br/>])
|
.map((error) => [error.detail, <br />])
|
||||||
.reduce((a, b) => a.concat(b), [])
|
.reduce((a, b) => a.concat(b), [])
|
||||||
.slice(0, -1);
|
.slice(0, -1);
|
||||||
break;
|
break;
|
||||||
@ -336,8 +329,10 @@ export default class Application {
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
children,
|
children,
|
||||||
controls: isDebug && [
|
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 {
|
try {
|
||||||
@ -347,7 +342,8 @@ export default class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,7 @@ export default class Component {
|
|||||||
*
|
*
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
init() {
|
init() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the component is destroyed, i.e. after a redraw where it is no
|
* 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
|
* @param {Object} e
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
onunload() {
|
onunload() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the renderable virtual DOM that represents the component's view.
|
* Get the renderable virtual DOM that represents the component's view.
|
||||||
@ -148,8 +146,7 @@ export default class Component {
|
|||||||
* @param {Object} vdom
|
* @param {Object} vdom
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
config() {
|
config() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the virtual DOM that represents the component's view.
|
* Get the virtual DOM that represents the component's view.
|
||||||
@ -201,7 +198,7 @@ export default class Component {
|
|||||||
controller: this.bind(undefined, componentProps),
|
controller: this.bind(undefined, componentProps),
|
||||||
view: view,
|
view: view,
|
||||||
props: componentProps,
|
props: componentProps,
|
||||||
component: this
|
component: this,
|
||||||
};
|
};
|
||||||
|
|
||||||
// If a `key` prop was set, then we'll assume that we want that to actually
|
// If a `key` prop was set, then we'll assume that we want that to actually
|
||||||
@ -220,6 +217,5 @@ export default class Component {
|
|||||||
* @param {Object} props
|
* @param {Object} props
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
static initProps(props) {
|
static initProps(props) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ export default class Model {
|
|||||||
const data = {
|
const data = {
|
||||||
type: this.data.type,
|
type: this.data.type,
|
||||||
id: this.data.id,
|
id: this.data.id,
|
||||||
attributes
|
attributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
// If a 'relationships' key exists, extract it from the attributes hash and
|
// 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];
|
const model = attributes.relationships[key];
|
||||||
|
|
||||||
data.relationships[key] = {
|
data.relationships[key] = {
|
||||||
data: model instanceof Array
|
data: model instanceof Array ? model.map(Model.getIdentifier) : Model.getIdentifier(model),
|
||||||
? model.map(Model.getIdentifier)
|
|
||||||
: Model.getIdentifier(model)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,15 +155,22 @@ export default class Model {
|
|||||||
const request = { data };
|
const request = { data };
|
||||||
if (options.meta) request.meta = options.meta;
|
if (options.meta) request.meta = options.meta;
|
||||||
|
|
||||||
return app.request(Object.assign({
|
return app
|
||||||
|
.request(
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
method: this.exists ? 'PATCH' : 'POST',
|
method: this.exists ? 'PATCH' : 'POST',
|
||||||
url: app.forum.attribute('apiUrl') + this.apiEndpoint(),
|
url: app.forum.attribute('apiUrl') + this.apiEndpoint(),
|
||||||
data: request
|
data: request,
|
||||||
}, options)).then(
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(
|
||||||
// If everything went well, we'll make sure the store knows that this
|
// 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
|
// model exists now (if it didn't already), and we'll push the data that
|
||||||
// the API returned into the store.
|
// 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] = this.store.data[payload.data.type] || {};
|
||||||
this.store.data[payload.data.type][payload.data.id] = this;
|
this.store.data[payload.data.type][payload.data.id] = this;
|
||||||
return this.store.pushPayload(payload);
|
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
|
// 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.
|
// old data! We'll revert to that and let others handle the error.
|
||||||
response => {
|
(response) => {
|
||||||
this.pushData(oldData);
|
this.pushData(oldData);
|
||||||
m.lazyRedraw();
|
m.lazyRedraw();
|
||||||
throw response;
|
throw response;
|
||||||
@ -192,11 +197,18 @@ export default class Model {
|
|||||||
delete(data, options = {}) {
|
delete(data, options = {}) {
|
||||||
if (!this.exists) return m.deferred().resolve().promise;
|
if (!this.exists) return m.deferred().resolve().promise;
|
||||||
|
|
||||||
return app.request(Object.assign({
|
return app
|
||||||
|
.request(
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: app.forum.attribute('apiUrl') + this.apiEndpoint(),
|
url: app.forum.attribute('apiUrl') + this.apiEndpoint(),
|
||||||
data
|
data,
|
||||||
}, options)).then(() => {
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
this.exists = false;
|
this.exists = false;
|
||||||
this.store.remove(this);
|
this.store.remove(this);
|
||||||
});
|
});
|
||||||
@ -272,7 +284,7 @@ export default class Model {
|
|||||||
const relationship = this.data.relationships[name];
|
const relationship = this.data.relationships[name];
|
||||||
|
|
||||||
if (relationship) {
|
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) {
|
static getIdentifier(model) {
|
||||||
return {
|
return {
|
||||||
type: model.data.type,
|
type: model.data.type,
|
||||||
id: model.data.id
|
id: model.data.id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,11 +31,16 @@ export default class Session {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
login(data, options = {}) {
|
login(data, options = {}) {
|
||||||
return app.request(Object.assign({
|
return app.request(
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: app.forum.attribute('baseUrl') + '/login',
|
url: app.forum.attribute('baseUrl') + '/login',
|
||||||
data
|
data,
|
||||||
}, options));
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,9 +34,7 @@ export default class Store {
|
|||||||
pushPayload(payload) {
|
pushPayload(payload) {
|
||||||
if (payload.included) payload.included.map(this.pushObject.bind(this));
|
if (payload.included) payload.included.map(this.pushObject.bind(this));
|
||||||
|
|
||||||
const result = payload.data instanceof Array
|
const result = payload.data instanceof Array ? payload.data.map(this.pushObject.bind(this)) : this.pushObject(payload.data);
|
||||||
? payload.data.map(this.pushObject.bind(this))
|
|
||||||
: this.pushObject(payload.data);
|
|
||||||
|
|
||||||
// Attach the original payload to the model that we give back. This is
|
// Attach the original payload to the model that we give back. This is
|
||||||
// useful to consumers as it allows them to access meta information
|
// useful to consumers as it allows them to access meta information
|
||||||
@ -58,7 +56,7 @@ export default class Store {
|
|||||||
pushObject(data) {
|
pushObject(data) {
|
||||||
if (!this.models[data.type]) return null;
|
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]) {
|
if (type[data.id]) {
|
||||||
type[data.id].pushData(data);
|
type[data.id].pushData(data);
|
||||||
@ -95,11 +93,18 @@ export default class Store {
|
|||||||
url += '/' + id;
|
url += '/' + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.request(Object.assign({
|
return app
|
||||||
|
.request(
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url,
|
url,
|
||||||
data
|
data,
|
||||||
}, options)).then(this.pushPayload.bind(this));
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(this.pushPayload.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,7 +129,7 @@ export default class Store {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getBy(type, key, value) {
|
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) {
|
all(type) {
|
||||||
const records = this.data[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 = {}) {
|
createRecord(type, data = {}) {
|
||||||
data.type = data.type || type;
|
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 hydrated = [];
|
||||||
const open = [hydrated];
|
const open = [hydrated];
|
||||||
|
|
||||||
translation.forEach(part => {
|
translation.forEach((part) => {
|
||||||
const match = part.match(new RegExp('{([a-z0-9_]+)}|<(/?)([a-z0-9_]+)>', 'i'));
|
const match = part.match(new RegExp('{([a-z0-9_]+)}|<(/?)([a-z0-9_]+)>', 'i'));
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
@ -87,7 +87,7 @@ export default class Translator {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return hydrated.filter(part => part);
|
return hydrated.filter((part) => part);
|
||||||
}
|
}
|
||||||
|
|
||||||
pluralize(translation, number) {
|
pluralize(translation, number) {
|
||||||
@ -97,7 +97,7 @@ export default class Translator {
|
|||||||
standardRules = [],
|
standardRules = [],
|
||||||
explicitRules = [];
|
explicitRules = [];
|
||||||
|
|
||||||
translation.split('|').forEach(part => {
|
translation.split('|').forEach((part) => {
|
||||||
if (cPluralRegex.test(part)) {
|
if (cPluralRegex.test(part)) {
|
||||||
const matches = part.match(cPluralRegex);
|
const matches = part.match(cPluralRegex);
|
||||||
explicitRules[matches[0]] = matches[matches.length - 1];
|
explicitRules[matches[0]] = matches[matches.length - 1];
|
||||||
@ -125,8 +125,10 @@ export default class Translator {
|
|||||||
var leftNumber = this.convertNumber(matches[4]);
|
var leftNumber = this.convertNumber(matches[4]);
|
||||||
var rightNumber = this.convertNumber(matches[5]);
|
var rightNumber = this.convertNumber(matches[5]);
|
||||||
|
|
||||||
if (('[' === matches[3] ? number >= leftNumber : number > leftNumber) &&
|
if (
|
||||||
(']' === matches[6] ? number <= rightNumber : number < rightNumber)) {
|
('[' === matches[3] ? number >= leftNumber : number > leftNumber) &&
|
||||||
|
(']' === matches[6] ? number <= rightNumber : number < rightNumber)
|
||||||
|
) {
|
||||||
return explicitRules[e];
|
return explicitRules[e];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,7 +225,7 @@ export default class Translator {
|
|||||||
case 'tr':
|
case 'tr':
|
||||||
case 'ur':
|
case 'ur':
|
||||||
case 'zu':
|
case 'zu':
|
||||||
return (number == 1) ? 0 : 1;
|
return number == 1 ? 0 : 1;
|
||||||
|
|
||||||
case 'am':
|
case 'am':
|
||||||
case 'bh':
|
case 'bh':
|
||||||
@ -237,7 +239,7 @@ export default class Translator {
|
|||||||
case 'xbr':
|
case 'xbr':
|
||||||
case 'ti':
|
case 'ti':
|
||||||
case 'wa':
|
case 'wa':
|
||||||
return ((number === 0) || (number == 1)) ? 0 : 1;
|
return number === 0 || number == 1 ? 0 : 1;
|
||||||
|
|
||||||
case 'be':
|
case 'be':
|
||||||
case 'bs':
|
case 'bs':
|
||||||
@ -245,41 +247,41 @@ export default class Translator {
|
|||||||
case 'ru':
|
case 'ru':
|
||||||
case 'sr':
|
case 'sr':
|
||||||
case 'uk':
|
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 'cs':
|
||||||
case 'sk':
|
case 'sk':
|
||||||
return (number == 1) ? 0 : (((number >= 2) && (number <= 4)) ? 1 : 2);
|
return number == 1 ? 0 : number >= 2 && number <= 4 ? 1 : 2;
|
||||||
|
|
||||||
case 'ga':
|
case 'ga':
|
||||||
return (number == 1) ? 0 : ((number == 2) ? 1 : 2);
|
return number == 1 ? 0 : number == 2 ? 1 : 2;
|
||||||
|
|
||||||
case 'lt':
|
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':
|
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':
|
case 'mk':
|
||||||
return (number % 10 == 1) ? 0 : 1;
|
return number % 10 == 1 ? 0 : 1;
|
||||||
|
|
||||||
case 'mt':
|
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':
|
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':
|
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':
|
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':
|
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':
|
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:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -62,9 +62,9 @@ import userOnline from './helpers/userOnline';
|
|||||||
import listItems from './helpers/listItems';
|
import listItems from './helpers/listItems';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
'extend': extend,
|
extend: extend,
|
||||||
'Session': Session,
|
Session: Session,
|
||||||
'Store': Store,
|
Store: Store,
|
||||||
'utils/evented': evented,
|
'utils/evented': evented,
|
||||||
'utils/liveHumanTimes': liveHumanTimes,
|
'utils/liveHumanTimes': liveHumanTimes,
|
||||||
'utils/ItemList': ItemList,
|
'utils/ItemList': ItemList,
|
||||||
@ -91,8 +91,8 @@ export default {
|
|||||||
'models/Discussion': Discussion,
|
'models/Discussion': Discussion,
|
||||||
'models/Group': Group,
|
'models/Group': Group,
|
||||||
'models/Forum': Forum,
|
'models/Forum': Forum,
|
||||||
'Component': Component,
|
Component: Component,
|
||||||
'Translator': Translator,
|
Translator: Translator,
|
||||||
'components/AlertManager': AlertManager,
|
'components/AlertManager': AlertManager,
|
||||||
'components/Switch': Switch,
|
'components/Switch': Switch,
|
||||||
'components/Badge': Badge,
|
'components/Badge': Badge,
|
||||||
@ -113,8 +113,8 @@ export default {
|
|||||||
'components/Button': Button,
|
'components/Button': Button,
|
||||||
'components/Modal': Modal,
|
'components/Modal': Modal,
|
||||||
'components/GroupBadge': GroupBadge,
|
'components/GroupBadge': GroupBadge,
|
||||||
'Model': Model,
|
Model: Model,
|
||||||
'Application': Application,
|
Application: Application,
|
||||||
'helpers/fullTime': fullTime,
|
'helpers/fullTime': fullTime,
|
||||||
'helpers/avatar': avatar,
|
'helpers/avatar': avatar,
|
||||||
'helpers/icon': icon,
|
'helpers/icon': icon,
|
||||||
@ -123,5 +123,5 @@ export default {
|
|||||||
'helpers/highlight': highlight,
|
'helpers/highlight': highlight,
|
||||||
'helpers/username': username,
|
'helpers/username': username,
|
||||||
'helpers/userOnline': userOnline,
|
'helpers/userOnline': userOnline,
|
||||||
'helpers/listItems': listItems
|
'helpers/listItems': listItems,
|
||||||
};
|
};
|
||||||
|
@ -35,22 +35,13 @@ export default class Alert extends Component {
|
|||||||
const dismissControl = [];
|
const dismissControl = [];
|
||||||
|
|
||||||
if (dismissible || dismissible === undefined) {
|
if (dismissible || dismissible === undefined) {
|
||||||
dismissControl.push(
|
dismissControl.push(<Button icon="fas fa-times" className="Button Button--link Button--icon Alert-dismiss" onclick={ondismiss} />);
|
||||||
<Button
|
|
||||||
icon="fas fa-times"
|
|
||||||
className="Button Button--link Button--icon Alert-dismiss"
|
|
||||||
onclick={ondismiss}/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...attrs}>
|
<div {...attrs}>
|
||||||
<span className="Alert-body">
|
<span className="Alert-body">{children}</span>
|
||||||
{children}
|
<ul className="Alert-controls">{listItems(controls.concat(dismissControl))}</ul>
|
||||||
</span>
|
|
||||||
<ul className="Alert-controls">
|
|
||||||
{listItems(controls.concat(dismissControl))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,9 @@ export default class AlertManager extends Component {
|
|||||||
view() {
|
view() {
|
||||||
return (
|
return (
|
||||||
<div className="AlertManager">
|
<div className="AlertManager">
|
||||||
{this.components.map(component => <div className="AlertManager-alert">{component}</div>)}
|
{this.components.map((component) => (
|
||||||
|
<div className="AlertManager-alert">{component}</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,7 @@ export default class Badge extends Component {
|
|||||||
attrs.className = 'Badge ' + (type ? 'Badge--' + type : '') + ' ' + (attrs.className || '');
|
attrs.className = 'Badge ' + (type ? 'Badge--' + type : '') + ' ' + (attrs.className || '');
|
||||||
attrs.title = extract(attrs, 'label') || '';
|
attrs.title = extract(attrs, 'label') || '';
|
||||||
|
|
||||||
return (
|
return <span {...attrs}>{iconName ? icon(iconName, { className: 'Badge-icon' }) : m.trust(' ')}</span>;
|
||||||
<span {...attrs}>
|
|
||||||
{iconName ? icon(iconName, {className: 'Badge-icon'}) : m.trust(' ')}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config(isInitialized) {
|
config(isInitialized) {
|
||||||
|
@ -64,7 +64,7 @@ export default class Button extends Component {
|
|||||||
return [
|
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.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 (
|
return (
|
||||||
<label className={className}>
|
<label className={className}>
|
||||||
<input type="checkbox"
|
<input type="checkbox" checked={this.props.state} disabled={this.props.disabled} onchange={m.withAttr('checked', this.onchange.bind(this))} />
|
||||||
checked={this.props.state}
|
<div className="Checkbox-display">{this.getDisplay()}</div>
|
||||||
disabled={this.props.disabled}
|
|
||||||
onchange={m.withAttr('checked', this.onchange.bind(this))}/>
|
|
||||||
<div className="Checkbox-display">
|
|
||||||
{this.getDisplay()}
|
|
||||||
</div>
|
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
@ -50,9 +45,7 @@ export default class Checkbox extends Component {
|
|||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
getDisplay() {
|
getDisplay() {
|
||||||
return this.loading
|
return this.loading ? LoadingIndicator.component({ size: 'tiny' }) : icon(this.props.state ? 'fas fa-check' : 'fas fa-times');
|
||||||
? 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.removeClass('Dropdown-menu--top Dropdown-menu--right');
|
||||||
|
|
||||||
$menu.toggleClass(
|
$menu.toggleClass('Dropdown-menu--top', $menu.offset().top + $menu.height() > $(window).scrollTop() + $(window).height());
|
||||||
'Dropdown-menu--top',
|
|
||||||
$menu.offset().top + $menu.height() > $(window).scrollTop() + $(window).height()
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($menu.offset().top < 0) {
|
if ($menu.offset().top < 0) {
|
||||||
$menu.removeClass('Dropdown-menu--top');
|
$menu.removeClass('Dropdown-menu--top');
|
||||||
}
|
}
|
||||||
|
|
||||||
$menu.toggleClass(
|
$menu.toggleClass('Dropdown-menu--right', isRight || $menu.offset().left + $menu.width() > $(window).scrollLeft() + $(window).width());
|
||||||
'Dropdown-menu--right',
|
|
||||||
isRight || $menu.offset().left + $menu.width() > $(window).scrollLeft() + $(window).width()
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$().on('hidden.bs.dropdown', () => {
|
this.$().on('hidden.bs.dropdown', () => {
|
||||||
@ -98,10 +92,7 @@ export default class Dropdown extends Component {
|
|||||||
*/
|
*/
|
||||||
getButton() {
|
getButton() {
|
||||||
return (
|
return (
|
||||||
<button
|
<button className={'Dropdown-toggle ' + this.props.buttonClassName} data-toggle="dropdown" onclick={this.props.onclick}>
|
||||||
className={'Dropdown-toggle ' + this.props.buttonClassName}
|
|
||||||
data-toggle="dropdown"
|
|
||||||
onclick={this.props.onclick}>
|
|
||||||
{this.getButtonContent()}
|
{this.getButtonContent()}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
@ -117,15 +108,11 @@ export default class Dropdown extends Component {
|
|||||||
return [
|
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>,
|
<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) {
|
getMenu(items) {
|
||||||
return (
|
return <ul className={'Dropdown-menu dropdown-menu ' + this.props.menuClassName}>{items}</ul>;
|
||||||
<ul className={'Dropdown-menu dropdown-menu ' + this.props.menuClassName}>
|
|
||||||
{items}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,6 @@ export default class LinkButton extends Button {
|
|||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
static isActive(props) {
|
static isActive(props) {
|
||||||
return typeof props.active !== 'undefined'
|
return typeof props.active !== 'undefined' ? props.active : m.route() === props.href;
|
||||||
? props.active
|
|
||||||
: m.route() === props.href;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,12 @@ export default class Modal extends Component {
|
|||||||
{Button.component({
|
{Button.component({
|
||||||
icon: 'fas fa-times',
|
icon: 'fas fa-times',
|
||||||
onclick: this.hide.bind(this),
|
onclick: this.hide.bind(this),
|
||||||
className: 'Button Button--icon Button--link'
|
className: 'Button Button--icon Button--link',
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : ''}
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
<form onsubmit={this.onsubmit.bind(this)}>
|
<form onsubmit={this.onsubmit.bind(this)}>
|
||||||
<div className="Modal-header">
|
<div className="Modal-header">
|
||||||
@ -65,8 +67,7 @@ export default class Modal extends Component {
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
className() {
|
className() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the title of the modal dialog.
|
* Get the title of the modal dialog.
|
||||||
@ -74,8 +75,7 @@ export default class Modal extends Component {
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
title() {
|
title() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the content of the modal.
|
* Get the content of the modal.
|
||||||
@ -83,16 +83,14 @@ export default class Modal extends Component {
|
|||||||
* @return {VirtualElement}
|
* @return {VirtualElement}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
content() {
|
content() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the modal form's submit event.
|
* Handle the modal form's submit event.
|
||||||
*
|
*
|
||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
*/
|
*/
|
||||||
onsubmit() {
|
onsubmit() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Focus on the first input when the modal is ready to be used.
|
* 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();
|
this.$('form').find('input, select, textarea').first().focus().select();
|
||||||
}
|
}
|
||||||
|
|
||||||
onhide() {
|
onhide() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the modal.
|
* Hide the modal.
|
||||||
|
@ -13,11 +13,7 @@ export default class ModalManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
view() {
|
view() {
|
||||||
return (
|
return <div className="ModalManager modal fade">{this.component && this.component.render()}</div>;
|
||||||
<div className="ModalManager modal fade">
|
|
||||||
{this.component && this.component.render()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config(isInitialized, context) {
|
config(isInitialized, context) {
|
||||||
@ -28,9 +24,7 @@ export default class ModalManager extends Component {
|
|||||||
// to be retained across route changes.
|
// to be retained across route changes.
|
||||||
context.retain = true;
|
context.retain = true;
|
||||||
|
|
||||||
this.$()
|
this.$().on('hidden.bs.modal', this.clear.bind(this)).on('shown.bs.modal', this.onready.bind(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);
|
m.redraw(true);
|
||||||
|
|
||||||
const dismissible = !!this.component.isDismissible();
|
const dismissible = !!this.component.isDismissible();
|
||||||
this.$().modal({
|
this.$()
|
||||||
|
.modal({
|
||||||
backdrop: dismissible || 'static',
|
backdrop: dismissible || 'static',
|
||||||
keyboard: dismissible
|
keyboard: dismissible,
|
||||||
}).modal('show');
|
})
|
||||||
|
.modal('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,12 +22,12 @@ export default class Navigation extends Component {
|
|||||||
const { history, pane } = app;
|
const { history, pane } = app;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'Navigation ButtonGroup ' + (this.props.className || '')}
|
<div
|
||||||
|
className={'Navigation ButtonGroup ' + (this.props.className || '')}
|
||||||
onmouseenter={pane && pane.show.bind(pane)}
|
onmouseenter={pane && pane.show.bind(pane)}
|
||||||
onmouseleave={pane && pane.onmouseleave.bind(pane)}>
|
onmouseleave={pane && pane.onmouseleave.bind(pane)}
|
||||||
{history.canGoBack()
|
>
|
||||||
? [this.getBackButton(), this.getPaneButton()]
|
{history.canGoBack() ? [this.getBackButton(), this.getPaneButton()] : this.getDrawerButton()}
|
||||||
: this.getDrawerButton()}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -55,11 +55,11 @@ export default class Navigation extends Component {
|
|||||||
icon: 'fas fa-chevron-left',
|
icon: 'fas fa-chevron-left',
|
||||||
title: previous.title,
|
title: previous.title,
|
||||||
config: () => {},
|
config: () => {},
|
||||||
onclick: e => {
|
onclick: (e) => {
|
||||||
if (e.shiftKey || e.ctrlKey || e.metaKey || e.which === 2) return;
|
if (e.shiftKey || e.ctrlKey || e.metaKey || e.which === 2) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
history.back();
|
history.back();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ export default class Navigation extends Component {
|
|||||||
return Button.component({
|
return Button.component({
|
||||||
className: 'Button Button--icon Navigation-pin' + (pane.pinned ? ' active' : ''),
|
className: 'Button Button--icon Navigation-pin' + (pane.pinned ? ' active' : ''),
|
||||||
onclick: pane.togglePinned.bind(pane),
|
onclick: pane.togglePinned.bind(pane),
|
||||||
icon: 'fas fa-thumbtack'
|
icon: 'fas fa-thumbtack',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,13 +94,12 @@ export default class Navigation extends Component {
|
|||||||
const user = app.session.user;
|
const user = app.session.user;
|
||||||
|
|
||||||
return Button.component({
|
return Button.component({
|
||||||
className: 'Button Button--icon Navigation-drawer' +
|
className: 'Button Button--icon Navigation-drawer' + (user && user.newNotificationCount() ? ' new' : ''),
|
||||||
(user && user.newNotificationCount() ? ' new' : ''),
|
onclick: (e) => {
|
||||||
onclick: e => {
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
drawer.show();
|
drawer.show();
|
||||||
},
|
},
|
||||||
icon: 'fas fa-bars'
|
icon: 'fas fa-bars',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,7 @@ export default class RequestErrorModal extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
title() {
|
title() {
|
||||||
return this.props.error.xhr
|
return this.props.error.xhr ? this.props.error.xhr.status + ' ' + this.props.error.xhr.statusText : '';
|
||||||
? this.props.error.xhr.status+' '+this.props.error.xhr.statusText
|
|
||||||
: '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
content() {
|
content() {
|
||||||
@ -20,11 +18,15 @@ export default class RequestErrorModal extends Modal {
|
|||||||
responseText = this.props.error.responseText;
|
responseText = this.props.error.responseText;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="Modal-body">
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
<pre>
|
<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}
|
{responseText}
|
||||||
</pre>
|
</pre>
|
||||||
</div>;
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,15 @@ export default class Select extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="Select">
|
<span className="Select">
|
||||||
<select className="Select-input FormControl" onchange={onchange ? m.withAttr('value', onchange.bind(this)) : undefined} value={value} disabled={disabled}>
|
<select
|
||||||
{Object.keys(options).map(key => <option value={key}>{options[key]}</option>)}
|
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>
|
</select>
|
||||||
{icon('fas fa-sort', { className: 'Select-caret' })}
|
{icon('fas fa-sort', { className: 'Select-caret' })}
|
||||||
</span>
|
</span>
|
||||||
|
@ -21,14 +21,11 @@ export default class SelectDropdown extends Dropdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getButtonContent() {
|
getButtonContent() {
|
||||||
const activeChild = this.props.children.filter(child => child.props.active)[0];
|
const activeChild = this.props.children.filter((child) => child.props.active)[0];
|
||||||
let label = activeChild && activeChild.props.children || this.props.defaultLabel;
|
let label = (activeChild && activeChild.props.children) || this.props.defaultLabel;
|
||||||
|
|
||||||
if (label instanceof Array) label = label[0];
|
if (label instanceof Array) label = label[0];
|
||||||
|
|
||||||
return [
|
return [<span className="Button-label">{label}</span>, icon(this.props.caretIcon, { className: 'Button-caret' })];
|
||||||
<span className="Button-label">{label}</span>,
|
|
||||||
icon(this.props.caretIcon, {className: 'Button-caret'})
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,10 @@ export default class SplitDropdown extends Dropdown {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
Button.component(buttonProps),
|
Button.component(buttonProps),
|
||||||
<button
|
<button className={'Dropdown-toggle Button Button--icon ' + this.props.buttonClassName} data-toggle="dropdown">
|
||||||
className={'Dropdown-toggle Button Button--icon ' + this.props.buttonClassName}
|
|
||||||
data-toggle="dropdown">
|
|
||||||
{icon(this.props.icon, { className: 'Button-icon' })}
|
{icon(this.props.icon, { className: 'Button-icon' })}
|
||||||
{icon('fas fa-caret-down', { className: 'Button-caret' })}
|
{icon('fas fa-caret-down', { className: 'Button-caret' })}
|
||||||
</button>
|
</button>,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ export default class Routes {
|
|||||||
|
|
||||||
const model = app.store.models[this.type];
|
const model = app.store.models[this.type];
|
||||||
|
|
||||||
this.attributes.forEach(name => model.prototype[name] = model.attribute(name));
|
this.attributes.forEach((name) => (model.prototype[name] = model.attribute(name)));
|
||||||
this.hasOnes.forEach(name => model.prototype[name] = model.hasOne(name));
|
this.hasOnes.forEach((name) => (model.prototype[name] = model.hasOne(name)));
|
||||||
this.hasManys.forEach(name => model.prototype[name] = model.hasMany(name));
|
this.hasManys.forEach((name) => (model.prototype[name] = model.hasMany(name)));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,5 +11,9 @@ export default function fullTime(time) {
|
|||||||
const datetime = mo.format();
|
const datetime = mo.format();
|
||||||
const full = mo.format('LLLL');
|
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 full = mo.format('LLLL');
|
||||||
const ago = humanTimeUtil(time);
|
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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ function withoutUnnecessarySeparators(items) {
|
|||||||
export default function listItems(items) {
|
export default function listItems(items) {
|
||||||
if (!(items instanceof Array)) items = [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 isListItem = item.component && item.component.isListItem;
|
||||||
const active = item.component && item.component.isActive && item.component.isActive(item.props);
|
const active = item.component && item.component.isActive && item.component.isActive(item.props);
|
||||||
const className = item.props ? item.props.itemClassName : item.itemClassName;
|
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;
|
item.attrs.key = item.attrs.key || item.itemName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isListItem
|
return isListItem ? (
|
||||||
? item
|
item
|
||||||
: <li className={classList([
|
) : (
|
||||||
(item.itemName ? 'item-' + item.itemName : ''),
|
<li className={classList([item.itemName ? 'item-' + item.itemName : '', className, active ? 'active' : ''])} key={item.itemName}>
|
||||||
className,
|
|
||||||
(active ? 'active' : '')
|
|
||||||
])}
|
|
||||||
key={item.itemName}>
|
|
||||||
{item}
|
{item}
|
||||||
</li>;
|
</li>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ export default function punctuateSeries(items) {
|
|||||||
if (items.length === 2) {
|
if (items.length === 2) {
|
||||||
return app.translator.trans('core.lib.series.two_text', {
|
return app.translator.trans('core.lib.series.two_text', {
|
||||||
first: items[0],
|
first: items[0],
|
||||||
second: items[1]
|
second: items[1],
|
||||||
});
|
});
|
||||||
} else if (items.length >= 3) {
|
} else if (items.length >= 3) {
|
||||||
// If there are three or more items, we will join all but the first and
|
// 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', {
|
return app.translator.trans('core.lib.series.three_text', {
|
||||||
first: items[0],
|
first: items[0],
|
||||||
second,
|
second,
|
||||||
third: items[items.length - 1]
|
third: items[items.length - 1],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,18 +19,18 @@ Object.assign(Discussion.prototype, {
|
|||||||
lastPostNumber: Model.attribute('lastPostNumber'),
|
lastPostNumber: Model.attribute('lastPostNumber'),
|
||||||
|
|
||||||
commentCount: Model.attribute('commentCount'),
|
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'),
|
posts: Model.hasMany('posts'),
|
||||||
mostRelevantPost: Model.hasOne('mostRelevantPost'),
|
mostRelevantPost: Model.hasOne('mostRelevantPost'),
|
||||||
|
|
||||||
lastReadAt: Model.attribute('lastReadAt', Model.transformDate),
|
lastReadAt: Model.attribute('lastReadAt', Model.transformDate),
|
||||||
lastReadPostNumber: Model.attribute('lastReadPostNumber'),
|
lastReadPostNumber: Model.attribute('lastReadPostNumber'),
|
||||||
isUnread: computed('unreadCount', unreadCount => !!unreadCount),
|
isUnread: computed('unreadCount', (unreadCount) => !!unreadCount),
|
||||||
isRead: computed('unreadCount', unreadCount => app.session.user && !unreadCount),
|
isRead: computed('unreadCount', (unreadCount) => app.session.user && !unreadCount),
|
||||||
|
|
||||||
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
||||||
hiddenUser: Model.hasOne('hiddenUser'),
|
hiddenUser: Model.hasOne('hiddenUser'),
|
||||||
isHidden: computed('hiddenAt', hiddenAt => !!hiddenAt),
|
isHidden: computed('hiddenAt', (hiddenAt) => !!hiddenAt),
|
||||||
|
|
||||||
canReply: Model.attribute('canReply'),
|
canReply: Model.attribute('canReply'),
|
||||||
canRename: Model.attribute('canRename'),
|
canRename: Model.attribute('canRename'),
|
||||||
@ -99,6 +99,6 @@ Object.assign(Discussion.prototype, {
|
|||||||
postIds() {
|
postIds() {
|
||||||
const posts = this.data.relationships.posts;
|
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'),
|
nameSingular: Model.attribute('nameSingular'),
|
||||||
namePlural: Model.attribute('namePlural'),
|
namePlural: Model.attribute('namePlural'),
|
||||||
color: Model.attribute('color'),
|
color: Model.attribute('color'),
|
||||||
icon: Model.attribute('icon')
|
icon: Model.attribute('icon'),
|
||||||
});
|
});
|
||||||
|
|
||||||
Group.ADMINISTRATOR_ID = '1';
|
Group.ADMINISTRATOR_ID = '1';
|
||||||
|
@ -11,5 +11,5 @@ Object.assign(Notification.prototype, {
|
|||||||
|
|
||||||
user: Model.hasOne('user'),
|
user: Model.hasOne('user'),
|
||||||
fromUser: Model.hasOne('fromUser'),
|
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),
|
editedAt: Model.attribute('editedAt', Model.transformDate),
|
||||||
editedUser: Model.hasOne('editedUser'),
|
editedUser: Model.hasOne('editedUser'),
|
||||||
isEdited: computed('editedAt', editedAt => !!editedAt),
|
isEdited: computed('editedAt', (editedAt) => !!editedAt),
|
||||||
|
|
||||||
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
||||||
hiddenUser: Model.hasOne('hiddenUser'),
|
hiddenUser: Model.hasOne('hiddenUser'),
|
||||||
isHidden: computed('hiddenAt', hiddenAt => !!hiddenAt),
|
isHidden: computed('hiddenAt', (hiddenAt) => !!hiddenAt),
|
||||||
|
|
||||||
canEdit: Model.attribute('canEdit'),
|
canEdit: Model.attribute('canEdit'),
|
||||||
canHide: Model.attribute('canHide'),
|
canHide: Model.attribute('canHide'),
|
||||||
canDelete: Model.attribute('canDelete')
|
canDelete: Model.attribute('canDelete'),
|
||||||
});
|
});
|
||||||
|
@ -67,7 +67,7 @@ Object.assign(User.prototype, {
|
|||||||
const groups = this.groups();
|
const groups = this.groups();
|
||||||
|
|
||||||
if (groups) {
|
if (groups) {
|
||||||
groups.forEach(group => {
|
groups.forEach((group) => {
|
||||||
items.add('group' + group.id(), GroupBadge.component({ group }));
|
items.add('group' + group.id(), GroupBadge.component({ group }));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -107,5 +107,5 @@ Object.assign(User.prototype, {
|
|||||||
Object.assign(preferences, newPreferences);
|
Object.assign(preferences, newPreferences);
|
||||||
|
|
||||||
return this.save({ preferences });
|
return this.save({ preferences });
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,7 @@ export default class Drawer {
|
|||||||
constructor() {
|
constructor() {
|
||||||
// Set up an event handler so that whenever the content area is tapped,
|
// Set up an event handler so that whenever the content area is tapped,
|
||||||
// the drawer will close.
|
// the drawer will close.
|
||||||
$('#content').click(e => {
|
$('#content').click((e) => {
|
||||||
if (this.isOpen()) {
|
if (this.isOpen()) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.hide();
|
this.hide();
|
||||||
|
@ -147,14 +147,15 @@ export default class ItemList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return items.sort((a, b) => {
|
return items
|
||||||
|
.sort((a, b) => {
|
||||||
if (a.priority === b.priority) {
|
if (a.priority === b.priority) {
|
||||||
return a.key - b.key;
|
return a.key - b.key;
|
||||||
} else if (a.priority > b.priority) {
|
} else if (a.priority > b.priority) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
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.webkitRequestAnimationFrame ||
|
||||||
window.mozRequestAnimationFrame ||
|
window.mozRequestAnimationFrame ||
|
||||||
window.msRequestAnimationFrame ||
|
window.msRequestAnimationFrame ||
|
||||||
window.oRequestAnimationFrame ||
|
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
|
* The `ScrollListener` class sets up a listener that handles window scroll
|
||||||
@ -57,10 +58,7 @@ export default class ScrollListener {
|
|||||||
*/
|
*/
|
||||||
start() {
|
start() {
|
||||||
if (!this.active) {
|
if (!this.active) {
|
||||||
window.addEventListener(
|
window.addEventListener('scroll', (this.active = this.loop.bind(this)));
|
||||||
'scroll',
|
|
||||||
this.active = this.loop.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ export default function classList(classes) {
|
|||||||
let classNames;
|
let classNames;
|
||||||
|
|
||||||
if (classes instanceof Array) {
|
if (classes instanceof Array) {
|
||||||
classNames = classes.filter(name => name);
|
classNames = classes.filter((name) => name);
|
||||||
} else {
|
} else {
|
||||||
classNames = [];
|
classNames = [];
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ export default function computed(...dependentKeys) {
|
|||||||
|
|
||||||
// Read all of the dependent values. If any of them have changed since last
|
// Read all of the dependent values. If any of them have changed since last
|
||||||
// time, then we'll want to recompute our output.
|
// 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];
|
const value = typeof this[key] === 'function' ? this[key]() : this[key];
|
||||||
|
|
||||||
if (dependentValues[key] !== value) {
|
if (dependentValues[key] !== value) {
|
||||||
@ -29,7 +29,10 @@ export default function computed(...dependentKeys) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (recompute) {
|
if (recompute) {
|
||||||
computedValue = compute.apply(this, keys.map(key => dependentValues[key]));
|
computedValue = compute.apply(
|
||||||
|
this,
|
||||||
|
keys.map((key) => dependentValues[key])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return computedValue;
|
return computedValue;
|
||||||
|
@ -34,7 +34,7 @@ export default {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
trigger(event, ...args) {
|
trigger(event, ...args) {
|
||||||
this.getHandlers(event).forEach(handler => handler.apply(this, args));
|
this.getHandlers(event).forEach((handler) => handler.apply(this, args));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,5 +77,5 @@ export default {
|
|||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
handlers.splice(index, 1);
|
handlers.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
export default function extractText(vdom) {
|
export default function extractText(vdom) {
|
||||||
if (vdom instanceof Array) {
|
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) {
|
} else if (typeof vdom === 'object' && vdom !== null) {
|
||||||
return extractText(vdom.children);
|
return extractText(vdom.children);
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,4 +33,4 @@ export default function humanTime(time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ago;
|
return ago;
|
||||||
};
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
export default function mixin(Parent, ...mixins) {
|
export default function mixin(Parent, ...mixins) {
|
||||||
class Mixed extends Parent {}
|
class Mixed extends Parent {}
|
||||||
|
|
||||||
mixins.forEach(object => {
|
mixins.forEach((object) => {
|
||||||
Object.assign(Mixed.prototype, object);
|
Object.assign(Mixed.prototype, object);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ export default function patchMithril(global) {
|
|||||||
if (comp.prototype && comp.prototype instanceof Component) {
|
if (comp.prototype && comp.prototype instanceof Component) {
|
||||||
let children = args.slice(1);
|
let children = args.slice(1);
|
||||||
if (children.length === 1 && Array.isArray(children[0])) {
|
if (children.length === 1 && Array.isArray(children[0])) {
|
||||||
children = children[0]
|
children = children[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return comp.component(args[0], children);
|
return comp.component(args[0], children);
|
||||||
@ -29,7 +29,7 @@ export default function patchMithril(global) {
|
|||||||
return node;
|
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).
|
* Redraw only if not in the middle of a computation (e.g. a route change).
|
||||||
|
@ -7,9 +7,7 @@
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
export function truncate(string, length, start = 0) {
|
export function truncate(string, length, start = 0) {
|
||||||
return (start > 0 ? '...' : '') +
|
return (start > 0 ? '...' : '') + string.substring(start, start + length) + (string.length > start + length ? '...' : '');
|
||||||
string.substring(start, start + length) +
|
|
||||||
(string.length > start + length ? '...' : '');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,7 +22,8 @@ export function truncate(string, length, start = 0) {
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
export function slug(string) {
|
export function slug(string) {
|
||||||
return string.toLowerCase()
|
return string
|
||||||
|
.toLowerCase()
|
||||||
.replace(/[^a-z0-9]/gi, '-')
|
.replace(/[^a-z0-9]/gi, '-')
|
||||||
.replace(/-+/g, '-')
|
.replace(/-+/g, '-')
|
||||||
.replace(/-$|^-/g, '');
|
.replace(/-$|^-/g, '');
|
||||||
@ -38,9 +37,7 @@ export function slug(string) {
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
export function getPlainContent(string) {
|
export function getPlainContent(string) {
|
||||||
const html = string
|
const html = string.replace(/(<\/p>|<br>)/g, '$1 ').replace(/<img\b[^>]*>/gi, ' ');
|
||||||
.replace(/(<\/p>|<br>)/g, '$1 ')
|
|
||||||
.replace(/<img\b[^>]*>/ig, ' ');
|
|
||||||
|
|
||||||
const dom = $('<div/>').html(html);
|
const dom = $('<div/>').html(html);
|
||||||
|
|
||||||
|
@ -10,18 +10,42 @@ function hsvToRgb(h, s, v) {
|
|||||||
const t = v * (1 - (1 - f) * s);
|
const t = v * (1 - (1 - f) * s);
|
||||||
|
|
||||||
switch (i % 6) {
|
switch (i % 6) {
|
||||||
case 0: r = v; g = t; b = p; break;
|
case 0:
|
||||||
case 1: r = q; g = v; b = p; break;
|
r = v;
|
||||||
case 2: r = p; g = v; b = t; break;
|
g = t;
|
||||||
case 3: r = p; g = q; b = v; break;
|
b = p;
|
||||||
case 4: r = t; g = p; b = v; break;
|
break;
|
||||||
case 5: r = v; g = p; b = q; 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 {
|
return {
|
||||||
r: Math.floor(r * 255),
|
r: Math.floor(r * 255),
|
||||||
g: Math.floor(g * 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}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
notificationComponents = {
|
notificationComponents = {
|
||||||
discussionRenamed: DiscussionRenamedNotification
|
discussionRenamed: DiscussionRenamedNotification,
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* A map of post types to their components.
|
* A map of post types to their components.
|
||||||
@ -31,7 +31,7 @@ export default class ForumApplication extends Application {
|
|||||||
*/
|
*/
|
||||||
postComponents = {
|
postComponents = {
|
||||||
comment: CommentPost,
|
comment: CommentPost,
|
||||||
discussionRenamed: DiscussionRenamedPost
|
discussionRenamed: DiscussionRenamedPost,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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
|
// 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.
|
// 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;
|
if (e.ctrlKey || e.metaKey || e.which === 2) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
app.history.home();
|
app.history.home();
|
||||||
@ -123,9 +123,11 @@ export default class ForumApplication extends Application {
|
|||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
composingReplyTo(discussion) {
|
composingReplyTo(discussion) {
|
||||||
return this.composer.component instanceof ReplyComposer &&
|
return (
|
||||||
|
this.composer.component instanceof ReplyComposer &&
|
||||||
this.composer.component.props.discussion === discussion &&
|
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}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
viewingDiscussion(discussion) {
|
viewingDiscussion(discussion) {
|
||||||
return this.current instanceof DiscussionPage &&
|
return this.current instanceof DiscussionPage && this.current.discussion === discussion;
|
||||||
this.current.discussion === discussion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,6 +134,6 @@ export default Object.assign(compat, {
|
|||||||
'components/DiscussionListItem': DiscussionListItem,
|
'components/DiscussionListItem': DiscussionListItem,
|
||||||
'components/LoadingPost': LoadingPost,
|
'components/LoadingPost': LoadingPost,
|
||||||
'components/PostsUserPage': PostsUserPage,
|
'components/PostsUserPage': PostsUserPage,
|
||||||
'routes': routes,
|
routes: routes,
|
||||||
'ForumApplication': ForumApplication
|
ForumApplication: ForumApplication,
|
||||||
});
|
});
|
||||||
|
@ -44,7 +44,8 @@ export default class AvatarEditor extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className={'AvatarEditor Dropdown ' + this.props.className + (this.loading ? ' loading' : '') + (this.isDraggedOver ? ' dragover' : '')}>
|
<div className={'AvatarEditor Dropdown ' + this.props.className + (this.loading ? ' loading' : '') + (this.isDraggedOver ? ' dragover' : '')}>
|
||||||
{avatar(user)}
|
{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')}
|
title={app.translator.trans('core.forum.user.avatar_upload_tooltip')}
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
onclick={this.quickUpload.bind(this)}
|
onclick={this.quickUpload.bind(this)}
|
||||||
@ -52,12 +53,11 @@ export default class AvatarEditor extends Component {
|
|||||||
ondragenter={this.enableDragover.bind(this)}
|
ondragenter={this.enableDragover.bind(this)}
|
||||||
ondragleave={this.disableDragover.bind(this)}
|
ondragleave={this.disableDragover.bind(this)}
|
||||||
ondragend={this.disableDragover.bind(this)}
|
ondragend={this.disableDragover.bind(this)}
|
||||||
ondrop={this.dropUpload.bind(this)}>
|
ondrop={this.dropUpload.bind(this)}
|
||||||
{this.loading ? LoadingIndicator.component() : (user.avatarUrl() ? icon('fas fa-pencil-alt') : icon('fas fa-plus-circle'))}
|
>
|
||||||
|
{this.loading ? LoadingIndicator.component() : user.avatarUrl() ? icon('fas fa-pencil-alt') : icon('fas fa-plus-circle')}
|
||||||
</a>
|
</a>
|
||||||
<ul className="Dropdown-menu Menu">
|
<ul className="Dropdown-menu Menu">{listItems(this.controlItems().toArray())}</ul>
|
||||||
{listItems(this.controlItems().toArray())}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -70,19 +70,21 @@ export default class AvatarEditor extends Component {
|
|||||||
controlItems() {
|
controlItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('upload',
|
items.add(
|
||||||
|
'upload',
|
||||||
Button.component({
|
Button.component({
|
||||||
icon: 'fas fa-upload',
|
icon: 'fas fa-upload',
|
||||||
children: app.translator.trans('core.forum.user.avatar_upload_button'),
|
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({
|
Button.component({
|
||||||
icon: 'fas fa-times',
|
icon: 'fas fa-times',
|
||||||
children: app.translator.trans('core.forum.user.avatar_remove_button'),
|
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 user = this.props.user;
|
||||||
const $input = $('<input type="file">');
|
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]);
|
this.upload($(e.target)[0].files[0]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -170,15 +176,14 @@ export default class AvatarEditor extends Component {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
m.redraw();
|
m.redraw();
|
||||||
|
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar',
|
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar',
|
||||||
serialize: raw => raw,
|
serialize: (raw) => raw,
|
||||||
data
|
data,
|
||||||
}).then(
|
})
|
||||||
this.success.bind(this),
|
.then(this.success.bind(this), this.failure.bind(this));
|
||||||
this.failure.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,13 +195,12 @@ export default class AvatarEditor extends Component {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
m.redraw();
|
m.redraw();
|
||||||
|
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar'
|
url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar',
|
||||||
}).then(
|
})
|
||||||
this.success.bind(this),
|
.then(this.success.bind(this), this.failure.bind(this));
|
||||||
this.failure.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +44,9 @@ export default class ChangeEmailModal extends Modal {
|
|||||||
return (
|
return (
|
||||||
<div className="Modal-body">
|
<div className="Modal-body">
|
||||||
<div className="Form Form--centered">
|
<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">
|
<div className="Form-group">
|
||||||
<Button className="Button Button--primary Button--block" onclick={this.hide.bind(this)}>
|
<Button className="Button Button--primary Button--block" onclick={this.hide.bind(this)}>
|
||||||
{app.translator.trans('core.forum.change_email.dismiss_button')}
|
{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="Modal-body">
|
||||||
<div className="Form Form--centered">
|
<div className="Form Form--centered">
|
||||||
<div className="Form-group">
|
<div className="Form-group">
|
||||||
<input type="email" name="email" className="FormControl"
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
className="FormControl"
|
||||||
placeholder={app.session.user.email()}
|
placeholder={app.session.user.email()}
|
||||||
bidi={this.email}
|
bidi={this.email}
|
||||||
disabled={this.loading}/>
|
disabled={this.loading}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="Form-group">
|
<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')}
|
placeholder={app.translator.trans('core.forum.change_email.confirm_password_placeholder')}
|
||||||
bidi={this.password}
|
bidi={this.password}
|
||||||
disabled={this.loading}/>
|
disabled={this.loading}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="Form-group">
|
<div className="Form-group">
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button Button--primary Button--block',
|
className: 'Button Button--primary Button--block',
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
loading: this.loading,
|
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>
|
||||||
</div>
|
</div>
|
||||||
@ -97,11 +107,15 @@ export default class ChangeEmailModal extends Modal {
|
|||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
app.session.user.save({email: this.email()}, {
|
app.session.user
|
||||||
|
.save(
|
||||||
|
{ email: this.email() },
|
||||||
|
{
|
||||||
errorHandler: this.onerror.bind(this),
|
errorHandler: this.onerror.bind(this),
|
||||||
meta: {password: this.password()}
|
meta: { password: this.password() },
|
||||||
})
|
}
|
||||||
.then(() => this.success = true)
|
)
|
||||||
|
.then(() => (this.success = true))
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.then(this.loaded.bind(this));
|
.then(this.loaded.bind(this));
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ export default class ChangePasswordModal extends Modal {
|
|||||||
className: 'Button Button--primary Button--block',
|
className: 'Button Button--primary Button--block',
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
loading: this.loading,
|
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>
|
||||||
</div>
|
</div>
|
||||||
@ -37,13 +37,12 @@ export default class ChangePasswordModal extends Modal {
|
|||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: app.forum.attribute('apiUrl') + '/forgot',
|
url: app.forum.attribute('apiUrl') + '/forgot',
|
||||||
data: {email: app.session.user.email()}
|
data: { email: app.session.user.email() },
|
||||||
}).then(
|
})
|
||||||
this.hide.bind(this),
|
.then(this.hide.bind(this), this.loaded.bind(this));
|
||||||
this.loaded.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,13 +44,13 @@ export default class CommentPost extends Post {
|
|||||||
// Note: we avoid using JSX for the <ul> below because it results in some
|
// 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
|
// weirdness in Mithril.js 0.1.x (see flarum/core#975). This workaround can
|
||||||
// be reverted when we upgrade to Mithril 1.0.
|
// 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>,
|
<header className="Post-header">{m('ul', listItems(this.headerItems().toArray()))}</header>,
|
||||||
<div className="Post-body">
|
<div className="Post-body">
|
||||||
{this.isEditing()
|
{this.isEditing() ? <div className="Post-preview" config={this.configPreview.bind(this)} /> : m.trust(this.props.post.contentHtml())}
|
||||||
? <div className="Post-preview" config={this.configPreview.bind(this)}/>
|
</div>,
|
||||||
: m.trust(this.props.post.contentHtml())}
|
|
||||||
</div>
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,20 +72,22 @@ export default class CommentPost extends Post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isEditing() {
|
isEditing() {
|
||||||
return app.composer.component instanceof EditPostComposer &&
|
return app.composer.component instanceof EditPostComposer && app.composer.component.props.post === this.props.post;
|
||||||
app.composer.component.props.post === this.props.post;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs() {
|
attrs() {
|
||||||
const post = this.props.post;
|
const post = this.props.post;
|
||||||
const attrs = super.attrs();
|
const attrs = super.attrs();
|
||||||
|
|
||||||
attrs.className = (attrs.className || '') + ' ' + classList({
|
attrs.className =
|
||||||
'CommentPost': true,
|
(attrs.className || '') +
|
||||||
|
' ' +
|
||||||
|
classList({
|
||||||
|
CommentPost: true,
|
||||||
'Post--hidden': post.isHidden(),
|
'Post--hidden': post.isHidden(),
|
||||||
'Post--edited': post.isEdited(),
|
'Post--edited': post.isEdited(),
|
||||||
'revealContent': this.revealContent,
|
revealContent: this.revealContent,
|
||||||
'editing': this.isEditing()
|
editing: this.isEditing(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return attrs;
|
return attrs;
|
||||||
@ -139,13 +141,14 @@ export default class CommentPost extends Post {
|
|||||||
// If the post is hidden, add a button that allows toggling the visibility
|
// If the post is hidden, add a button that allows toggling the visibility
|
||||||
// of the post's content.
|
// of the post's content.
|
||||||
if (post.isHidden()) {
|
if (post.isHidden()) {
|
||||||
items.add('toggle', (
|
items.add(
|
||||||
|
'toggle',
|
||||||
Button.component({
|
Button.component({
|
||||||
className: 'Button Button--default Button--more',
|
className: 'Button Button--default Button--more',
|
||||||
icon: 'fas fa-ellipsis-h',
|
icon: 'fas fa-ellipsis-h',
|
||||||
onclick: this.toggleContent.bind(this)
|
onclick: this.toggleContent.bind(this),
|
||||||
})
|
})
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
@ -36,10 +36,10 @@ class Composer extends Component {
|
|||||||
|
|
||||||
view() {
|
view() {
|
||||||
const classes = {
|
const classes = {
|
||||||
'normal': this.position === Composer.PositionEnum.NORMAL,
|
normal: this.position === Composer.PositionEnum.NORMAL,
|
||||||
'minimized': this.position === Composer.PositionEnum.MINIMIZED,
|
minimized: this.position === Composer.PositionEnum.MINIMIZED,
|
||||||
'fullScreen': this.position === Composer.PositionEnum.FULLSCREEN,
|
fullScreen: this.position === Composer.PositionEnum.FULLSCREEN,
|
||||||
'active': this.active
|
active: this.active,
|
||||||
};
|
};
|
||||||
classes.visible = classes.normal || classes.minimized || classes.fullScreen;
|
classes.visible = classes.normal || classes.minimized || classes.fullScreen;
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ class Composer extends Component {
|
|||||||
|
|
||||||
// Whenever any of the inputs inside the composer are have focus, we want to
|
// 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.
|
// 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';
|
this.active = e.type === 'focusin';
|
||||||
m.redraw();
|
m.redraw();
|
||||||
});
|
});
|
||||||
@ -94,18 +94,18 @@ class Composer extends Component {
|
|||||||
|
|
||||||
const handlers = {};
|
const handlers = {};
|
||||||
|
|
||||||
$(window).on('resize', handlers.onresize = this.updateHeight.bind(this)).resize();
|
$(window)
|
||||||
|
.on('resize', (handlers.onresize = this.updateHeight.bind(this)))
|
||||||
|
.resize();
|
||||||
|
|
||||||
$(document)
|
$(document)
|
||||||
.on('mousemove', handlers.onmousemove = this.onmousemove.bind(this))
|
.on('mousemove', (handlers.onmousemove = this.onmousemove.bind(this)))
|
||||||
.on('mouseup', handlers.onmouseup = this.onmouseup.bind(this));
|
.on('mouseup', (handlers.onmouseup = this.onmouseup.bind(this)));
|
||||||
|
|
||||||
context.onunload = () => {
|
context.onunload = () => {
|
||||||
$(window).off('resize', handlers.onresize);
|
$(window).off('resize', handlers.onresize);
|
||||||
|
|
||||||
$(document)
|
$(document).off('mousemove', handlers.onmousemove).off('mouseup', handlers.onmouseup);
|
||||||
.off('mousemove', handlers.onmousemove)
|
|
||||||
.off('mouseup', handlers.onmouseup);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +121,9 @@ class Composer extends Component {
|
|||||||
|
|
||||||
const composer = this;
|
const composer = this;
|
||||||
|
|
||||||
$(element).css('cursor', 'row-resize')
|
$(element)
|
||||||
.bind('dragstart mousedown', e => e.preventDefault())
|
.css('cursor', 'row-resize')
|
||||||
|
.bind('dragstart mousedown', (e) => e.preventDefault())
|
||||||
.mousedown(function (e) {
|
.mousedown(function (e) {
|
||||||
composer.mouseStart = e.clientY;
|
composer.mouseStart = e.clientY;
|
||||||
composer.heightStart = composer.$().height();
|
composer.heightStart = composer.$().height();
|
||||||
@ -191,13 +192,10 @@ class Composer extends Component {
|
|||||||
* scrolled right to the bottom.
|
* scrolled right to the bottom.
|
||||||
*/
|
*/
|
||||||
updateBodyPadding() {
|
updateBodyPadding() {
|
||||||
const visible = this.position !== Composer.PositionEnum.HIDDEN &&
|
const visible =
|
||||||
this.position !== Composer.PositionEnum.MINIMIZED &&
|
this.position !== Composer.PositionEnum.HIDDEN && this.position !== Composer.PositionEnum.MINIMIZED && this.$().css('position') !== 'absolute';
|
||||||
this.$().css('position') !== 'absolute';
|
|
||||||
|
|
||||||
const paddingBottom = visible
|
const paddingBottom = visible ? this.computedHeight() - parseInt($('#app').css('padding-bottom'), 10) : 0;
|
||||||
? this.computedHeight() - parseInt($('#app').css('padding-bottom'), 10)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
$('#content').css({ paddingBottom });
|
$('#content').css({ paddingBottom });
|
||||||
}
|
}
|
||||||
@ -304,9 +302,7 @@ class Composer extends Component {
|
|||||||
* Show the Composer backdrop.
|
* Show the Composer backdrop.
|
||||||
*/
|
*/
|
||||||
showBackdrop() {
|
showBackdrop() {
|
||||||
this.$backdrop = $('<div/>')
|
this.$backdrop = $('<div/>').addClass('composer-backdrop').appendTo('body');
|
||||||
.addClass('composer-backdrop')
|
|
||||||
.appendTo('body');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -421,32 +417,44 @@ class Composer extends Component {
|
|||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
if (this.position === Composer.PositionEnum.FULLSCREEN) {
|
if (this.position === Composer.PositionEnum.FULLSCREEN) {
|
||||||
items.add('exitFullScreen', ComposerButton.component({
|
items.add(
|
||||||
|
'exitFullScreen',
|
||||||
|
ComposerButton.component({
|
||||||
icon: 'fas fa-compress',
|
icon: 'fas fa-compress',
|
||||||
title: app.translator.trans('core.forum.composer.exit_full_screen_tooltip'),
|
title: app.translator.trans('core.forum.composer.exit_full_screen_tooltip'),
|
||||||
onclick: this.exitFullScreen.bind(this)
|
onclick: this.exitFullScreen.bind(this),
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
if (this.position !== Composer.PositionEnum.MINIMIZED) {
|
if (this.position !== Composer.PositionEnum.MINIMIZED) {
|
||||||
items.add('minimize', ComposerButton.component({
|
items.add(
|
||||||
|
'minimize',
|
||||||
|
ComposerButton.component({
|
||||||
icon: 'fas fa-minus minimize',
|
icon: 'fas fa-minus minimize',
|
||||||
title: app.translator.trans('core.forum.composer.minimize_tooltip'),
|
title: app.translator.trans('core.forum.composer.minimize_tooltip'),
|
||||||
onclick: this.minimize.bind(this),
|
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',
|
icon: 'fas fa-expand',
|
||||||
title: app.translator.trans('core.forum.composer.full_screen_tooltip'),
|
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',
|
icon: 'fas fa-times',
|
||||||
title: app.translator.trans('core.forum.composer.close_tooltip'),
|
title: app.translator.trans('core.forum.composer.close_tooltip'),
|
||||||
onclick: this.close.bind(this)
|
onclick: this.close.bind(this),
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
@ -524,7 +532,7 @@ Composer.PositionEnum = {
|
|||||||
HIDDEN: 'hidden',
|
HIDDEN: 'hidden',
|
||||||
NORMAL: 'normal',
|
NORMAL: 'normal',
|
||||||
MINIMIZED: 'minimized',
|
MINIMIZED: 'minimized',
|
||||||
FULLSCREEN: 'fullScreen'
|
FULLSCREEN: 'fullScreen',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Composer;
|
export default Composer;
|
||||||
|
@ -47,7 +47,7 @@ export default class ComposerBody extends Component {
|
|||||||
placeholder: this.props.placeholder,
|
placeholder: this.props.placeholder,
|
||||||
onchange: this.content,
|
onchange: this.content,
|
||||||
onsubmit: this.onsubmit.bind(this),
|
onsubmit: this.onsubmit.bind(this),
|
||||||
value: this.content()
|
value: this.content(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +100,7 @@ export default class ComposerBody extends Component {
|
|||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
onsubmit() {
|
onsubmit() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop loading.
|
* 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('title', <h3>{app.translator.trans('core.forum.composer_discussion.title')}</h3>, 100);
|
||||||
|
|
||||||
items.add('discussionTitle', (
|
items.add(
|
||||||
|
'discussionTitle',
|
||||||
<h3>
|
<h3>
|
||||||
<input className="FormControl"
|
<input
|
||||||
|
className="FormControl"
|
||||||
value={this.title()}
|
value={this.title()}
|
||||||
oninput={m.withAttr('value', this.title)}
|
oninput={m.withAttr('value', this.title)}
|
||||||
placeholder={this.props.titlePlaceholder}
|
placeholder={this.props.titlePlaceholder}
|
||||||
disabled={!!this.props.disabled}
|
disabled={!!this.props.disabled}
|
||||||
onkeydown={this.onkeydown.bind(this)}/>
|
onkeydown={this.onkeydown.bind(this)}
|
||||||
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
));
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -60,7 +63,8 @@ export default class DiscussionComposer extends ComposerBody {
|
|||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
*/
|
*/
|
||||||
onkeydown(e) {
|
onkeydown(e) {
|
||||||
if (e.which === 13) { // Return
|
if (e.which === 13) {
|
||||||
|
// Return
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.editor.setSelectionRange(0, 0);
|
this.editor.setSelectionRange(0, 0);
|
||||||
}
|
}
|
||||||
@ -80,7 +84,7 @@ export default class DiscussionComposer extends ComposerBody {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
title: this.title(),
|
title: this.title(),
|
||||||
content: this.content()
|
content: this.content(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,13 +93,13 @@ export default class DiscussionComposer extends ComposerBody {
|
|||||||
|
|
||||||
const data = this.data();
|
const data = this.data();
|
||||||
|
|
||||||
app.store.createRecord('discussions').save(data).then(
|
app.store
|
||||||
discussion => {
|
.createRecord('discussions')
|
||||||
|
.save(data)
|
||||||
|
.then((discussion) => {
|
||||||
app.composer.hide();
|
app.composer.hide();
|
||||||
app.cache.discussionList.refresh();
|
app.cache.discussionList.refresh();
|
||||||
m.route(app.route.discussion(discussion));
|
m.route(app.route.discussion(discussion));
|
||||||
},
|
}, this.loaded.bind(this));
|
||||||
this.loaded.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,23 +48,19 @@ export default class DiscussionList extends Component {
|
|||||||
loading = Button.component({
|
loading = Button.component({
|
||||||
children: app.translator.trans('core.forum.discussion_list.load_more_button'),
|
children: app.translator.trans('core.forum.discussion_list.load_more_button'),
|
||||||
className: 'Button',
|
className: 'Button',
|
||||||
onclick: this.loadMore.bind(this)
|
onclick: this.loadMore.bind(this),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.discussions.length === 0 && !this.loading) {
|
if (this.discussions.length === 0 && !this.loading) {
|
||||||
const text = app.translator.trans('core.forum.discussion_list.empty_text');
|
const text = app.translator.trans('core.forum.discussion_list.empty_text');
|
||||||
return (
|
return <div className="DiscussionList">{Placeholder.component({ text })}</div>;
|
||||||
<div className="DiscussionList">
|
|
||||||
{Placeholder.component({text})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'DiscussionList' + (this.props.params.q ? ' DiscussionList--searchResults' : '')}>
|
<div className={'DiscussionList' + (this.props.params.q ? ' DiscussionList--searchResults' : '')}>
|
||||||
<ul className="DiscussionList-discussions">
|
<ul className="DiscussionList-discussions">
|
||||||
{this.discussions.map(discussion => {
|
{this.discussions.map((discussion) => {
|
||||||
return (
|
return (
|
||||||
<li key={discussion.id()} data-id={discussion.id()}>
|
<li key={discussion.id()} data-id={discussion.id()}>
|
||||||
{DiscussionListItem.component({ discussion, params })}
|
{DiscussionListItem.component({ discussion, params })}
|
||||||
@ -72,9 +68,7 @@ export default class DiscussionList extends Component {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
<div className="DiscussionList-loadMore">
|
<div className="DiscussionList-loadMore">{loading}</div>
|
||||||
{loading}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -132,7 +126,7 @@ export default class DiscussionList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this.loadResults().then(
|
return this.loadResults().then(
|
||||||
results => {
|
(results) => {
|
||||||
this.discussions = [];
|
this.discussions = [];
|
||||||
this.parseResults(results);
|
this.parseResults(results);
|
||||||
},
|
},
|
||||||
@ -171,8 +165,7 @@ export default class DiscussionList extends Component {
|
|||||||
loadMore() {
|
loadMore() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
this.loadResults(this.discussions.length)
|
this.loadResults(this.discussions.length).then(this.parseResults.bind(this));
|
||||||
.then(this.parseResults.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,8 +47,8 @@ export default class DiscussionListItem extends Component {
|
|||||||
className: classList([
|
className: classList([
|
||||||
'DiscussionListItem',
|
'DiscussionListItem',
|
||||||
this.active() ? 'active' : '',
|
this.active() ? 'active' : '',
|
||||||
this.props.discussion.isHidden() ? 'DiscussionListItem--hidden' : ''
|
this.props.discussion.isHidden() ? 'DiscussionListItem--hidden' : '',
|
||||||
])
|
]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,43 +80,49 @@ export default class DiscussionListItem extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...attrs}>
|
<div {...attrs}>
|
||||||
{controls.length ? Dropdown.component({
|
{controls.length
|
||||||
|
? Dropdown.component({
|
||||||
icon: 'fas fa-ellipsis-v',
|
icon: 'fas fa-ellipsis-v',
|
||||||
children: controls,
|
children: controls,
|
||||||
className: 'DiscussionListItem-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')}
|
<a
|
||||||
onclick={this.markAsRead.bind(this)}>
|
className={'Slidable-underneath Slidable-underneath--left Slidable-underneath--elastic' + (isUnread ? '' : ' disabled')}
|
||||||
|
onclick={this.markAsRead.bind(this)}
|
||||||
|
>
|
||||||
{icon('fas fa-check')}
|
{icon('fas fa-check')}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div className={'DiscussionListItem-content Slidable-content' + (isUnread ? ' unread' : '') + (isRead ? ' read' : '')}>
|
<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"
|
className="DiscussionListItem-author"
|
||||||
title={extractText(app.translator.trans('core.forum.discussion_list.started_text', {user: user, ago: humanTime(discussion.createdAt())}))}
|
title={extractText(
|
||||||
|
app.translator.trans('core.forum.discussion_list.started_text', { user: user, ago: humanTime(discussion.createdAt()) })
|
||||||
|
)}
|
||||||
config={function (element) {
|
config={function (element) {
|
||||||
$(element).tooltip({ placement: 'right' });
|
$(element).tooltip({ placement: 'right' });
|
||||||
m.route.apply(this, arguments);
|
m.route.apply(this, arguments);
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{avatar(user, { title: '' })}
|
{avatar(user, { title: '' })}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ul className="DiscussionListItem-badges badges">
|
<ul className="DiscussionListItem-badges badges">{listItems(discussion.badges().toArray())}</ul>
|
||||||
{listItems(discussion.badges().toArray())}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<a href={app.route.discussion(discussion, jumpTo)}
|
<a href={app.route.discussion(discussion, jumpTo)} config={m.route} className="DiscussionListItem-main">
|
||||||
config={m.route}
|
|
||||||
className="DiscussionListItem-main">
|
|
||||||
<h3 className="DiscussionListItem-title">{highlight(discussion.title(), this.highlightRegExp)}</h3>
|
<h3 className="DiscussionListItem-title">{highlight(discussion.title(), this.highlightRegExp)}</h3>
|
||||||
<ul className="DiscussionListItem-info">{listItems(this.infoItems().toArray())}</ul>
|
<ul className="DiscussionListItem-info">{listItems(this.infoItems().toArray())}</ul>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<span className="DiscussionListItem-count"
|
<span
|
||||||
|
className="DiscussionListItem-count"
|
||||||
onclick={this.markAsRead.bind(this)}
|
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']())}
|
{abbreviateNumber(discussion[showUnread ? 'unreadCount' : 'replyCount']())}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -133,8 +139,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
if ('ontouchstart' in window) {
|
if ('ontouchstart' in window) {
|
||||||
const slidableInstance = slidable(this.$().addClass('Slidable'));
|
const slidableInstance = slidable(this.$().addClass('Slidable'));
|
||||||
|
|
||||||
this.$('.DiscussionListItem-controls')
|
this.$('.DiscussionListItem-controls').on('hidden.bs.dropdown', () => slidableInstance.reset());
|
||||||
.on('hidden.bs.dropdown', () => slidableInstance.reset());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,10 +204,11 @@ export default class DiscussionListItem extends Component {
|
|||||||
items.add('excerpt', excerpt, -100);
|
items.add('excerpt', excerpt, -100);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
items.add('terminalPost',
|
items.add(
|
||||||
|
'terminalPost',
|
||||||
TerminalPost.component({
|
TerminalPost.component({
|
||||||
discussion: this.props.discussion,
|
discussion: this.props.discussion,
|
||||||
lastPost: !this.showFirstPost()
|
lastPost: !this.showFirstPost(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -90,11 +90,13 @@ export default class DiscussionPage extends Page {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="DiscussionPage">
|
<div className="DiscussionPage">
|
||||||
{app.cache.discussionList
|
{app.cache.discussionList ? (
|
||||||
? <div className="DiscussionPage-list" config={this.configPane.bind(this)}>
|
<div className="DiscussionPage-list" config={this.configPane.bind(this)}>
|
||||||
{!$('.App-navigation').is(':visible') ? app.cache.discussionList.render() : ''}
|
{!$('.App-navigation').is(':visible') ? app.cache.discussionList.render() : ''}
|
||||||
</div>
|
</div>
|
||||||
: ''}
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="DiscussionPage-discussion">
|
<div className="DiscussionPage-discussion">
|
||||||
{discussion
|
{discussion
|
||||||
@ -104,10 +106,8 @@ export default class DiscussionPage extends Page {
|
|||||||
<nav className="DiscussionPage-nav">
|
<nav className="DiscussionPage-nav">
|
||||||
<ul>{listItems(this.sidebarItems().toArray())}</ul>
|
<ul>{listItems(this.sidebarItems().toArray())}</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<div className="DiscussionPage-stream">
|
<div className="DiscussionPage-stream">{this.stream.render()}</div>
|
||||||
{this.stream.render()}
|
</div>,
|
||||||
</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 {
|
} else {
|
||||||
const params = this.requestParams();
|
const params = this.requestParams();
|
||||||
|
|
||||||
app.store.find('discussions', m.route.param('id').split('-')[0], params)
|
app.store.find('discussions', m.route.param('id').split('-')[0], params).then(this.show.bind(this));
|
||||||
.then(this.show.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.lazyRedraw();
|
m.lazyRedraw();
|
||||||
@ -155,7 +154,7 @@ export default class DiscussionPage extends Page {
|
|||||||
*/
|
*/
|
||||||
requestParams() {
|
requestParams() {
|
||||||
return {
|
return {
|
||||||
page: {near: this.near}
|
page: { near: this.near },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,11 +181,14 @@ export default class DiscussionPage extends Page {
|
|||||||
const discussionId = discussion.id();
|
const discussionId = discussion.id();
|
||||||
|
|
||||||
includedPosts = discussion.payload.included
|
includedPosts = discussion.payload.included
|
||||||
.filter(record => record.type === 'posts'
|
.filter(
|
||||||
&& record.relationships
|
(record) =>
|
||||||
&& record.relationships.discussion
|
record.type === 'posts' &&
|
||||||
&& record.relationships.discussion.data.id === discussionId)
|
record.relationships &&
|
||||||
.map(record => app.store.getById('posts', record.id))
|
record.relationships.discussion &&
|
||||||
|
record.relationships.discussion.data.id === discussionId
|
||||||
|
)
|
||||||
|
.map((record) => app.store.getById('posts', record.id))
|
||||||
.sort((a, b) => a.id() - b.id())
|
.sort((a, b) => a.id() - b.id())
|
||||||
.slice(0, 20);
|
.slice(0, 20);
|
||||||
}
|
}
|
||||||
@ -219,7 +221,7 @@ export default class DiscussionPage extends Page {
|
|||||||
const pane = app.pane;
|
const pane = app.pane;
|
||||||
$list.hover(pane.show.bind(pane), pane.onmouseleave.bind(pane));
|
$list.hover(pane.show.bind(pane), pane.onmouseleave.bind(pane));
|
||||||
|
|
||||||
const hotEdge = e => {
|
const hotEdge = (e) => {
|
||||||
if (e.pageX < 10) pane.show();
|
if (e.pageX < 10) pane.show();
|
||||||
};
|
};
|
||||||
$(document).on('mousemove', hotEdge);
|
$(document).on('mousemove', hotEdge);
|
||||||
@ -249,19 +251,21 @@ export default class DiscussionPage extends Page {
|
|||||||
sidebarItems() {
|
sidebarItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('controls',
|
items.add(
|
||||||
|
'controls',
|
||||||
SplitDropdown.component({
|
SplitDropdown.component({
|
||||||
children: DiscussionControls.controls(this.discussion, this).toArray(),
|
children: DiscussionControls.controls(this.discussion, this).toArray(),
|
||||||
icon: 'fas fa-ellipsis-v',
|
icon: 'fas fa-ellipsis-v',
|
||||||
className: 'App-primaryControl',
|
className: 'App-primaryControl',
|
||||||
buttonClassName: 'Button--primary'
|
buttonClassName: 'Button--primary',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
items.add('scrubber',
|
items.add(
|
||||||
|
'scrubber',
|
||||||
PostStreamScrubber.component({
|
PostStreamScrubber.component({
|
||||||
stream: this.stream,
|
stream: this.stream,
|
||||||
className: 'App-titleControl'
|
className: 'App-titleControl',
|
||||||
}),
|
}),
|
||||||
-100
|
-100
|
||||||
);
|
);
|
||||||
@ -281,7 +285,7 @@ export default class DiscussionPage extends Page {
|
|||||||
|
|
||||||
// Construct a URL to this discussion with the updated position, then
|
// Construct a URL to this discussion with the updated position, then
|
||||||
// replace it into the window's history and our own history stack.
|
// 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);
|
m.route(url, true);
|
||||||
window.history.replaceState(null, document.title, url);
|
window.history.replaceState(null, document.title, url);
|
||||||
|
@ -27,8 +27,8 @@ export default class DiscussionRenamedPost extends EventPost {
|
|||||||
const newTitle = post.content()[1];
|
const newTitle = post.content()[1];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'old': oldTitle,
|
old: oldTitle,
|
||||||
'new': <strong className="DiscussionRenamedPost-new">{newTitle}</strong>
|
new: <strong className="DiscussionRenamedPost-new">{newTitle}</strong>,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,10 @@ export default class DiscussionsSearchSource {
|
|||||||
const params = {
|
const params = {
|
||||||
filter: { q: query },
|
filter: { q: query },
|
||||||
page: { limit: 3 },
|
page: { limit: 3 },
|
||||||
include: 'mostRelevantPost'
|
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) {
|
view(query) {
|
||||||
@ -37,10 +37,10 @@ export default class DiscussionsSearchSource {
|
|||||||
{LinkButton.component({
|
{LinkButton.component({
|
||||||
icon: 'fas fa-search',
|
icon: 'fas fa-search',
|
||||||
children: app.translator.trans('core.forum.search.all_discussions_button', { query }),
|
children: app.translator.trans('core.forum.search.all_discussions_button', { query }),
|
||||||
href: app.route('index', {q: query})
|
href: app.route('index', { q: query }),
|
||||||
})}
|
})}
|
||||||
</li>,
|
</li>,
|
||||||
results.map(discussion => {
|
results.map((discussion) => {
|
||||||
const mostRelevantPost = discussion.mostRelevantPost();
|
const mostRelevantPost = discussion.mostRelevantPost();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -51,7 +51,7 @@ export default class DiscussionsSearchSource {
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ export default class DiscussionsUserPage extends UserPage {
|
|||||||
{DiscussionList.component({
|
{DiscussionList.component({
|
||||||
params: {
|
params: {
|
||||||
q: 'author:' + this.user.username(),
|
q: 'author:' + this.user.username(),
|
||||||
sort: 'newest'
|
sort: 'newest',
|
||||||
}
|
},
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -22,7 +22,7 @@ export default class EditPostComposer extends ComposerBody {
|
|||||||
init() {
|
init() {
|
||||||
super.init();
|
super.init();
|
||||||
|
|
||||||
this.editor.props.preview = e => {
|
this.editor.props.preview = (e) => {
|
||||||
minimizeComposerIfFullScreen(e);
|
minimizeComposerIfFullScreen(e);
|
||||||
|
|
||||||
m.route(app.route.post(this.props.post));
|
m.route(app.route.post(this.props.post));
|
||||||
@ -50,14 +50,15 @@ export default class EditPostComposer extends ComposerBody {
|
|||||||
m.route.apply(this, arguments);
|
m.route.apply(this, arguments);
|
||||||
};
|
};
|
||||||
|
|
||||||
items.add('title', (
|
items.add(
|
||||||
|
'title',
|
||||||
<h3>
|
<h3>
|
||||||
{icon('fas fa-pencil-alt')}{' '}
|
{icon('fas fa-pencil-alt')}{' '}
|
||||||
<a href={app.route.discussion(post.discussion(), post.number())} config={routeAndMinimize}>
|
<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>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
));
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -69,7 +70,7 @@ export default class EditPostComposer extends ComposerBody {
|
|||||||
*/
|
*/
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
content: this.content()
|
content: this.content(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,9 +79,6 @@ export default class EditPostComposer extends ComposerBody {
|
|||||||
|
|
||||||
const data = this.data();
|
const data = this.data();
|
||||||
|
|
||||||
this.props.post.save(data).then(
|
this.props.post.save(data).then(() => app.composer.hide(), this.loaded.bind(this));
|
||||||
() => app.composer.hide(),
|
|
||||||
this.loaded.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,10 @@ export default class EditUserModal extends Modal {
|
|||||||
this.password = m.prop(user.password() || '');
|
this.password = m.prop(user.password() || '');
|
||||||
this.groups = {};
|
this.groups = {};
|
||||||
|
|
||||||
app.store.all('groups')
|
app.store
|
||||||
.filter(group => [Group.GUEST_ID, Group.MEMBER_ID].indexOf(group.id()) === -1)
|
.all('groups')
|
||||||
.forEach(group => this.groups[group.id()] = m.prop(user.groups().indexOf(group) !== -1));
|
.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() {
|
className() {
|
||||||
@ -37,9 +38,7 @@ export default class EditUserModal extends Modal {
|
|||||||
content() {
|
content() {
|
||||||
return (
|
return (
|
||||||
<div className="Modal-body">
|
<div className="Modal-body">
|
||||||
<div className="Form">
|
<div className="Form">{this.fields().toArray()}</div>
|
||||||
{this.fields().toArray()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -47,19 +46,22 @@ export default class EditUserModal extends Modal {
|
|||||||
fields() {
|
fields() {
|
||||||
const items = new ItemList();
|
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>
|
<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'))}
|
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.username_label'))} bidi={this.username} />
|
||||||
bidi={this.username} />
|
</div>,
|
||||||
</div>, 40);
|
40
|
||||||
|
);
|
||||||
|
|
||||||
if (app.session.user !== this.props.user) {
|
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>
|
<label>{app.translator.trans('core.forum.edit_user.email_heading')}</label>
|
||||||
<div>
|
<div>
|
||||||
<input className="FormControl"
|
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))} bidi={this.email} />
|
||||||
placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))}
|
|
||||||
bidi={this.email}/>
|
|
||||||
</div>
|
</div>
|
||||||
{!this.isEmailConfirmed() ? (
|
{!this.isEmailConfirmed() ? (
|
||||||
<div>
|
<div>
|
||||||
@ -67,58 +69,84 @@ export default class EditUserModal extends Modal {
|
|||||||
className: 'Button Button--block',
|
className: 'Button Button--block',
|
||||||
children: app.translator.trans('core.forum.edit_user.activate_button'),
|
children: app.translator.trans('core.forum.edit_user.activate_button'),
|
||||||
loading: this.loading,
|
loading: this.loading,
|
||||||
onclick: this.activate.bind(this)
|
onclick: this.activate.bind(this),
|
||||||
})}
|
})}
|
||||||
</div>
|
</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>
|
<label>{app.translator.trans('core.forum.edit_user.password_heading')}</label>
|
||||||
<div>
|
<div>
|
||||||
<label className="checkbox">
|
<label className="checkbox">
|
||||||
<input type="checkbox" onchange={e => {
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
onchange={(e) => {
|
||||||
this.setPassword(e.target.checked);
|
this.setPassword(e.target.checked);
|
||||||
m.redraw(true);
|
m.redraw(true);
|
||||||
if (e.target.checked) this.$('[name=password]').select();
|
if (e.target.checked) this.$('[name=password]').select();
|
||||||
m.redraw.strategy('none');
|
m.redraw.strategy('none');
|
||||||
}}/>
|
}}
|
||||||
|
/>
|
||||||
{app.translator.trans('core.forum.edit_user.set_password_label')}
|
{app.translator.trans('core.forum.edit_user.set_password_label')}
|
||||||
</label>
|
</label>
|
||||||
{this.setPassword() ? (
|
{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'))}
|
placeholder={extractText(app.translator.trans('core.forum.edit_user.password_label'))}
|
||||||
bidi={this.password}/>
|
bidi={this.password}
|
||||||
) : ''}
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
</div>
|
</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>
|
<label>{app.translator.trans('core.forum.edit_user.groups_heading')}</label>
|
||||||
<div>
|
<div>
|
||||||
{Object.keys(this.groups)
|
{Object.keys(this.groups)
|
||||||
.map(id => app.store.getById('groups', id))
|
.map((id) => app.store.getById('groups', id))
|
||||||
.map(group => (
|
.map((group) => (
|
||||||
<label className="checkbox">
|
<label className="checkbox">
|
||||||
<input type="checkbox"
|
<input
|
||||||
|
type="checkbox"
|
||||||
bidi={this.groups[group.id()]}
|
bidi={this.groups[group.id()]}
|
||||||
disabled={this.props.user.id() === '1' && group.id() === Group.ADMINISTRATOR_ID} />
|
disabled={this.props.user.id() === '1' && group.id() === Group.ADMINISTRATOR_ID}
|
||||||
|
/>
|
||||||
{GroupBadge.component({ group, label: '' })} {group.nameSingular()}
|
{GroupBadge.component({ group, label: '' })} {group.nameSingular()}
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>, 10);
|
</div>,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
items.add('submit', <div className="Form-group">
|
items.add(
|
||||||
|
'submit',
|
||||||
|
<div className="Form-group">
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button Button--primary',
|
className: 'Button Button--primary',
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
loading: this.loading,
|
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;
|
return items;
|
||||||
}
|
}
|
||||||
@ -129,7 +157,8 @@ export default class EditUserModal extends Modal {
|
|||||||
username: this.username(),
|
username: this.username(),
|
||||||
isEmailConfirmed: true,
|
isEmailConfirmed: true,
|
||||||
};
|
};
|
||||||
this.props.user.save(data, {errorHandler: this.onerror.bind(this)})
|
this.props.user
|
||||||
|
.save(data, { errorHandler: this.onerror.bind(this) })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.isEmailConfirmed(true);
|
this.isEmailConfirmed(true);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@ -143,12 +172,12 @@ export default class EditUserModal extends Modal {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
const groups = Object.keys(this.groups)
|
const groups = Object.keys(this.groups)
|
||||||
.filter(id => this.groups[id]())
|
.filter((id) => this.groups[id]())
|
||||||
.map(id => app.store.getById('groups', id));
|
.map((id) => app.store.getById('groups', id));
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
username: this.username(),
|
username: this.username(),
|
||||||
relationships: {groups}
|
relationships: { groups },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (app.session.user !== this.props.user) {
|
if (app.session.user !== this.props.user) {
|
||||||
@ -167,7 +196,8 @@ export default class EditUserModal extends Modal {
|
|||||||
|
|
||||||
this.loading = true;
|
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))
|
.then(this.hide.bind(this))
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
@ -28,17 +28,16 @@ export default class EventPost extends Post {
|
|||||||
const username = usernameHelper(user);
|
const username = usernameHelper(user);
|
||||||
const data = Object.assign(this.descriptionData(), {
|
const data = Object.assign(this.descriptionData(), {
|
||||||
user,
|
user,
|
||||||
username: user
|
username: user ? (
|
||||||
? <a className="EventPost-user" href={app.route.user(user)} config={m.route}>{username}</a>
|
<a className="EventPost-user" href={app.route.user(user)} config={m.route}>
|
||||||
: username
|
{username}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
username
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
return super.content().concat([
|
return super.content().concat([icon(this.icon(), { className: 'EventPost-icon' }), <div class="EventPost-info">{this.description(data)}</div>]);
|
||||||
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">
|
<div className="Form Form--centered">
|
||||||
<p className="helpText">{app.translator.trans('core.forum.forgot_password.text')}</p>
|
<p className="helpText">{app.translator.trans('core.forum.forgot_password.text')}</p>
|
||||||
<div className="Form-group">
|
<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()}
|
value={this.email()}
|
||||||
onchange={m.withAttr('value', this.email)}
|
onchange={m.withAttr('value', this.email)}
|
||||||
disabled={this.loading} />
|
disabled={this.loading}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="Form-group">
|
<div className="Form-group">
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button Button--primary Button--block',
|
className: 'Button Button--primary Button--block',
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
loading: this.loading,
|
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>
|
||||||
</div>
|
</div>
|
||||||
@ -82,11 +87,12 @@ export default class ForgotPasswordModal extends Modal {
|
|||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
app.request({
|
app
|
||||||
|
.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: app.forum.attribute('apiUrl') + '/forgot',
|
url: app.forum.attribute('apiUrl') + '/forgot',
|
||||||
data: { email: this.email() },
|
data: { email: this.email() },
|
||||||
errorHandler: this.onerror.bind(this)
|
errorHandler: this.onerror.bind(this),
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.success = true;
|
this.success = true;
|
||||||
|
@ -8,11 +8,7 @@ import listItems from '../../common/helpers/listItems';
|
|||||||
*/
|
*/
|
||||||
export default class HeaderPrimary extends Component {
|
export default class HeaderPrimary extends Component {
|
||||||
view() {
|
view() {
|
||||||
return (
|
return <ul className="Header-controls">{listItems(this.items().toArray())}</ul>;
|
||||||
<ul className="Header-controls">
|
|
||||||
{listItems(this.items().toArray())}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config(isInitialized, context) {
|
config(isInitialized, context) {
|
||||||
|
@ -15,11 +15,7 @@ import listItems from '../../common/helpers/listItems';
|
|||||||
*/
|
*/
|
||||||
export default class HeaderSecondary extends Component {
|
export default class HeaderSecondary extends Component {
|
||||||
view() {
|
view() {
|
||||||
return (
|
return <ul className="Header-controls">{listItems(this.items().toArray())}</ul>;
|
||||||
<ul className="Header-controls">
|
|
||||||
{listItems(this.items().toArray())}
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config(isInitialized, context) {
|
config(isInitialized, context) {
|
||||||
@ -39,11 +35,12 @@ export default class HeaderSecondary extends Component {
|
|||||||
|
|
||||||
items.add('search', app.search.render(), 30);
|
items.add('search', app.search.render(), 30);
|
||||||
|
|
||||||
if (app.forum.attribute("showLanguageSelector") && Object.keys(app.data.locales).length > 1) {
|
if (app.forum.attribute('showLanguageSelector') && Object.keys(app.data.locales).length > 1) {
|
||||||
const locales = [];
|
const locales = [];
|
||||||
|
|
||||||
for (const locale in app.data.locales) {
|
for (const locale in app.data.locales) {
|
||||||
locales.push(Button.component({
|
locales.push(
|
||||||
|
Button.component({
|
||||||
active: app.data.locale === locale,
|
active: app.data.locale === locale,
|
||||||
children: app.data.locales[locale],
|
children: app.data.locales[locale],
|
||||||
icon: app.data.locale === locale ? 'fas fa-check' : true,
|
icon: app.data.locale === locale ? 'fas fa-check' : true,
|
||||||
@ -54,14 +51,19 @@ export default class HeaderSecondary extends Component {
|
|||||||
document.cookie = `locale=${locale}; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT`;
|
document.cookie = `locale=${locale}; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT`;
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
items.add('locale', SelectDropdown.component({
|
items.add(
|
||||||
|
'locale',
|
||||||
|
SelectDropdown.component({
|
||||||
children: locales,
|
children: locales,
|
||||||
buttonClassName: 'Button Button--link'
|
buttonClassName: 'Button Button--link',
|
||||||
}), 20);
|
}),
|
||||||
|
20
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.session.user) {
|
if (app.session.user) {
|
||||||
@ -69,21 +71,25 @@ export default class HeaderSecondary extends Component {
|
|||||||
items.add('session', SessionDropdown.component(), 0);
|
items.add('session', SessionDropdown.component(), 0);
|
||||||
} else {
|
} else {
|
||||||
if (app.forum.attribute('allowSignUp')) {
|
if (app.forum.attribute('allowSignUp')) {
|
||||||
items.add('signUp',
|
items.add(
|
||||||
|
'signUp',
|
||||||
Button.component({
|
Button.component({
|
||||||
children: app.translator.trans('core.forum.header.sign_up_link'),
|
children: app.translator.trans('core.forum.header.sign_up_link'),
|
||||||
className: 'Button Button--link',
|
className: 'Button Button--link',
|
||||||
onclick: () => app.modal.show(new SignUpModal())
|
onclick: () => app.modal.show(new SignUpModal()),
|
||||||
}), 10
|
}),
|
||||||
|
10
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
items.add('logIn',
|
items.add(
|
||||||
|
'logIn',
|
||||||
Button.component({
|
Button.component({
|
||||||
children: app.translator.trans('core.forum.header.log_in_link'),
|
children: app.translator.trans('core.forum.header.log_in_link'),
|
||||||
className: 'Button Button--link',
|
className: 'Button Button--link',
|
||||||
onclick: () => app.modal.show(new LogInModal())
|
onclick: () => app.modal.show(new LogInModal()),
|
||||||
}), 0
|
}),
|
||||||
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ export default class IndexPage extends Page {
|
|||||||
// are currently present in the cached discussion list. If they differ, we
|
// are currently present in the cached discussion list. If they differ, we
|
||||||
// will clear the cache and set up a new discussion list component with
|
// will clear the cache and set up a new discussion list component with
|
||||||
// the new parameters.
|
// the new parameters.
|
||||||
Object.keys(params).some(key => {
|
Object.keys(params).some((key) => {
|
||||||
if (app.cache.discussionList.props.params[key] !== params[key]) {
|
if (app.cache.discussionList.props.params[key] !== params[key]) {
|
||||||
app.cache.discussionList = null;
|
app.cache.discussionList = null;
|
||||||
return true;
|
return true;
|
||||||
@ -102,7 +102,7 @@ export default class IndexPage extends Page {
|
|||||||
// previous hero. Maintain the same scroll position relative to the bottom
|
// previous hero. Maintain the same scroll position relative to the bottom
|
||||||
// of the hero so that the sidebar doesn't jump around.
|
// of the hero so that the sidebar doesn't jump around.
|
||||||
const oldHeroHeight = app.cache.heroHeight;
|
const oldHeroHeight = app.cache.heroHeight;
|
||||||
const heroHeight = app.cache.heroHeight = this.$('.Hero').outerHeight() || 0;
|
const heroHeight = (app.cache.heroHeight = this.$('.Hero').outerHeight() || 0);
|
||||||
const scrollTop = app.cache.scrollTop;
|
const scrollTop = app.cache.scrollTop;
|
||||||
|
|
||||||
$('#app').css('min-height', $(window).height() + heroHeight);
|
$('#app').css('min-height', $(window).height() + heroHeight);
|
||||||
@ -153,22 +153,26 @@ export default class IndexPage extends Page {
|
|||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
const canStartDiscussion = app.forum.attribute('canStartDiscussion') || !app.session.user;
|
const canStartDiscussion = app.forum.attribute('canStartDiscussion') || !app.session.user;
|
||||||
|
|
||||||
items.add('newDiscussion',
|
items.add(
|
||||||
|
'newDiscussion',
|
||||||
Button.component({
|
Button.component({
|
||||||
children: app.translator.trans(canStartDiscussion ? 'core.forum.index.start_discussion_button' : 'core.forum.index.cannot_start_discussion_button'),
|
children: app.translator.trans(
|
||||||
|
canStartDiscussion ? 'core.forum.index.start_discussion_button' : 'core.forum.index.cannot_start_discussion_button'
|
||||||
|
),
|
||||||
icon: 'fas fa-edit',
|
icon: 'fas fa-edit',
|
||||||
className: 'Button Button--primary IndexPage-newDiscussion',
|
className: 'Button Button--primary IndexPage-newDiscussion',
|
||||||
itemClassName: 'App-primaryControl',
|
itemClassName: 'App-primaryControl',
|
||||||
onclick: this.newDiscussionAction.bind(this),
|
onclick: this.newDiscussionAction.bind(this),
|
||||||
disabled: !canStartDiscussion
|
disabled: !canStartDiscussion,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
items.add('nav',
|
items.add(
|
||||||
|
'nav',
|
||||||
SelectDropdown.component({
|
SelectDropdown.component({
|
||||||
children: this.navItems(this).toArray(),
|
children: this.navItems(this).toArray(),
|
||||||
buttonClassName: 'Button',
|
buttonClassName: 'Button',
|
||||||
className: 'App-titleControl'
|
className: 'App-titleControl',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -185,11 +189,12 @@ export default class IndexPage extends Page {
|
|||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
const params = this.stickyParams();
|
const params = this.stickyParams();
|
||||||
|
|
||||||
items.add('allDiscussions',
|
items.add(
|
||||||
|
'allDiscussions',
|
||||||
LinkButton.component({
|
LinkButton.component({
|
||||||
href: app.route('index', params),
|
href: app.route('index', params),
|
||||||
children: app.translator.trans('core.forum.index.all_discussions_link'),
|
children: app.translator.trans('core.forum.index.all_discussions_link'),
|
||||||
icon: 'far fa-comments'
|
icon: 'far fa-comments',
|
||||||
}),
|
}),
|
||||||
100
|
100
|
||||||
);
|
);
|
||||||
@ -213,11 +218,12 @@ export default class IndexPage extends Page {
|
|||||||
sortOptions[i] = app.translator.trans('core.forum.index_sort.' + i + '_button');
|
sortOptions[i] = app.translator.trans('core.forum.index_sort.' + i + '_button');
|
||||||
}
|
}
|
||||||
|
|
||||||
items.add('sort',
|
items.add(
|
||||||
|
'sort',
|
||||||
Dropdown.component({
|
Dropdown.component({
|
||||||
buttonClassName: 'Button',
|
buttonClassName: 'Button',
|
||||||
label: sortOptions[this.params().sort] || Object.keys(sortMap).map(key => sortOptions[key])[0],
|
label: sortOptions[this.params().sort] || Object.keys(sortMap).map((key) => sortOptions[key])[0],
|
||||||
children: Object.keys(sortOptions).map(value => {
|
children: Object.keys(sortOptions).map((value) => {
|
||||||
const label = sortOptions[value];
|
const label = sortOptions[value];
|
||||||
const active = (this.params().sort || Object.keys(sortMap)[0]) === value;
|
const active = (this.params().sort || Object.keys(sortMap)[0]) === value;
|
||||||
|
|
||||||
@ -226,7 +232,7 @@ export default class IndexPage extends Page {
|
|||||||
icon: active ? 'fas fa-check' : true,
|
icon: active ? 'fas fa-check' : true,
|
||||||
onclick: this.changeSort.bind(this, value),
|
onclick: this.changeSort.bind(this, value),
|
||||||
active: active,
|
active: active,
|
||||||
})
|
});
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -243,7 +249,8 @@ export default class IndexPage extends Page {
|
|||||||
actionItems() {
|
actionItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('refresh',
|
items.add(
|
||||||
|
'refresh',
|
||||||
Button.component({
|
Button.component({
|
||||||
title: app.translator.trans('core.forum.index.refresh_tooltip'),
|
title: app.translator.trans('core.forum.index.refresh_tooltip'),
|
||||||
icon: 'fas fa-sync',
|
icon: 'fas fa-sync',
|
||||||
@ -254,17 +261,18 @@ export default class IndexPage extends Page {
|
|||||||
app.store.find('users', app.session.user.id());
|
app.store.find('users', app.session.user.id());
|
||||||
m.redraw();
|
m.redraw();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (app.session.user) {
|
if (app.session.user) {
|
||||||
items.add('markAllAsRead',
|
items.add(
|
||||||
|
'markAllAsRead',
|
||||||
Button.component({
|
Button.component({
|
||||||
title: app.translator.trans('core.forum.index.mark_all_as_read_tooltip'),
|
title: app.translator.trans('core.forum.index.mark_all_as_read_tooltip'),
|
||||||
icon: 'fas fa-check',
|
icon: 'fas fa-check',
|
||||||
className: 'Button Button--icon',
|
className: 'Button Button--icon',
|
||||||
onclick: this.markAllAsRead.bind(this)
|
onclick: this.markAllAsRead.bind(this),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -321,7 +329,7 @@ export default class IndexPage extends Page {
|
|||||||
stickyParams() {
|
stickyParams() {
|
||||||
return {
|
return {
|
||||||
sort: m.route.param('sort'),
|
sort: m.route.param('sort'),
|
||||||
q: m.route.param('q')
|
q: m.route.param('q'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,12 +17,15 @@ export default class LogInButton extends Button {
|
|||||||
const height = 400;
|
const height = 400;
|
||||||
const $window = $(window);
|
const $window = $(window);
|
||||||
|
|
||||||
window.open(app.forum.attribute('baseUrl') + props.path, 'logInPopup',
|
window.open(
|
||||||
|
app.forum.attribute('baseUrl') + props.path,
|
||||||
|
'logInPopup',
|
||||||
`width=${width},` +
|
`width=${width},` +
|
||||||
`height=${height},` +
|
`height=${height},` +
|
||||||
`top=${$window.height() / 2 - height / 2},` +
|
`top=${$window.height() / 2 - height / 2},` +
|
||||||
`left=${$window.width() / 2 - width / 2},` +
|
`left=${$window.width() / 2 - width / 2},` +
|
||||||
'status=no,scrollbars=yes,resizable=no');
|
'status=no,scrollbars=yes,resizable=no'
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
super.initProps(props);
|
super.initProps(props);
|
||||||
|
@ -6,11 +6,7 @@ import ItemList from '../../common/utils/ItemList';
|
|||||||
*/
|
*/
|
||||||
export default class LogInButtons extends Component {
|
export default class LogInButtons extends Component {
|
||||||
view() {
|
view() {
|
||||||
return (
|
return <div className="LogInButtons">{this.items().toArray()}</div>;
|
||||||
<div className="LogInButtons">
|
|
||||||
{this.items().toArray()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,58 +49,71 @@ export default class LogInModal extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
content() {
|
content() {
|
||||||
return [
|
return [<div className="Modal-body">{this.body()}</div>, <div className="Modal-footer">{this.footer()}</div>];
|
||||||
<div className="Modal-body">
|
|
||||||
{this.body()}
|
|
||||||
</div>,
|
|
||||||
<div className="Modal-footer">
|
|
||||||
{this.footer()}
|
|
||||||
</div>
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body() {
|
body() {
|
||||||
return [
|
return [<LogInButtons />, <div className="Form Form--centered">{this.fields().toArray()}</div>];
|
||||||
<LogInButtons/>,
|
|
||||||
|
|
||||||
<div className="Form Form--centered">
|
|
||||||
{this.fields().toArray()}
|
|
||||||
</div>
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fields() {
|
fields() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('identification', <div className="Form-group">
|
items.add(
|
||||||
<input className="FormControl" name="identification" type="text" placeholder={extractText(app.translator.trans('core.forum.log_in.username_or_email_placeholder'))}
|
'identification',
|
||||||
|
<div className="Form-group">
|
||||||
|
<input
|
||||||
|
className="FormControl"
|
||||||
|
name="identification"
|
||||||
|
type="text"
|
||||||
|
placeholder={extractText(app.translator.trans('core.forum.log_in.username_or_email_placeholder'))}
|
||||||
bidi={this.identification}
|
bidi={this.identification}
|
||||||
disabled={this.loading} />
|
disabled={this.loading}
|
||||||
</div>, 30);
|
/>
|
||||||
|
</div>,
|
||||||
|
30
|
||||||
|
);
|
||||||
|
|
||||||
items.add('password', <div className="Form-group">
|
items.add(
|
||||||
<input className="FormControl" name="password" type="password" placeholder={extractText(app.translator.trans('core.forum.log_in.password_placeholder'))}
|
'password',
|
||||||
|
<div className="Form-group">
|
||||||
|
<input
|
||||||
|
className="FormControl"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
placeholder={extractText(app.translator.trans('core.forum.log_in.password_placeholder'))}
|
||||||
bidi={this.password}
|
bidi={this.password}
|
||||||
disabled={this.loading} />
|
disabled={this.loading}
|
||||||
</div>, 20);
|
/>
|
||||||
|
</div>,
|
||||||
|
20
|
||||||
|
);
|
||||||
|
|
||||||
items.add('remember', <div className="Form-group">
|
items.add(
|
||||||
|
'remember',
|
||||||
|
<div className="Form-group">
|
||||||
<div>
|
<div>
|
||||||
<label className="checkbox">
|
<label className="checkbox">
|
||||||
<input type="checkbox" bidi={this.remember} disabled={this.loading} />
|
<input type="checkbox" bidi={this.remember} disabled={this.loading} />
|
||||||
{app.translator.trans('core.forum.log_in.remember_me_label')}
|
{app.translator.trans('core.forum.log_in.remember_me_label')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>, 10);
|
</div>,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
items.add('submit', <div className="Form-group">
|
items.add(
|
||||||
|
'submit',
|
||||||
|
<div className="Form-group">
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button Button--primary Button--block',
|
className: 'Button Button--primary Button--block',
|
||||||
type: 'submit',
|
type: 'submit',
|
||||||
loading: this.loading,
|
loading: this.loading,
|
||||||
children: app.translator.trans('core.forum.log_in.submit_button')
|
children: app.translator.trans('core.forum.log_in.submit_button'),
|
||||||
})}
|
})}
|
||||||
</div>, -10);
|
</div>,
|
||||||
|
-10
|
||||||
|
);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -112,10 +125,10 @@ export default class LogInModal extends Modal {
|
|||||||
</p>,
|
</p>,
|
||||||
|
|
||||||
app.forum.attribute('allowSignUp') ? (
|
app.forum.attribute('allowSignUp') ? (
|
||||||
<p className="LogInModal-signUp">
|
<p className="LogInModal-signUp">{app.translator.trans('core.forum.log_in.sign_up_text', { a: <a onclick={this.signUp.bind(this)} /> })}</p>
|
||||||
{app.translator.trans('core.forum.log_in.sign_up_text', {a: <a onclick={this.signUp.bind(this)}/>})}
|
) : (
|
||||||
</p>
|
''
|
||||||
) : ''
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,11 +172,9 @@ export default class LogInModal extends Modal {
|
|||||||
const password = this.password();
|
const password = this.password();
|
||||||
const remember = this.remember();
|
const remember = this.remember();
|
||||||
|
|
||||||
app.session.login({identification, password, remember}, {errorHandler: this.onerror.bind(this)})
|
app.session
|
||||||
.then(
|
.login({ identification, password, remember }, { errorHandler: this.onerror.bind(this) })
|
||||||
() => window.location.reload(),
|
.then(() => window.location.reload(), this.loaded.bind(this));
|
||||||
this.loaded.bind(this)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onerror(error) {
|
onerror(error) {
|
||||||
|
@ -20,31 +20,32 @@ export default class Notification extends Component {
|
|||||||
const href = this.href();
|
const href = this.href();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a className={'Notification Notification--' + notification.contentType() + ' ' + (!notification.isRead() ? 'unread' : '')}
|
<a
|
||||||
|
className={'Notification Notification--' + notification.contentType() + ' ' + (!notification.isRead() ? 'unread' : '')}
|
||||||
href={href}
|
href={href}
|
||||||
config={function (element, isInitialized) {
|
config={function (element, isInitialized) {
|
||||||
if (href.indexOf('://') === -1) m.route.apply(this, arguments);
|
if (href.indexOf('://') === -1) m.route.apply(this, arguments);
|
||||||
|
|
||||||
if (!isInitialized) $(element).click(this.markAsRead.bind(this));
|
if (!isInitialized) $(element).click(this.markAsRead.bind(this));
|
||||||
}}>
|
}}
|
||||||
{!notification.isRead() && Button.component({
|
>
|
||||||
|
{!notification.isRead() &&
|
||||||
|
Button.component({
|
||||||
className: 'Notification-action Button Button--icon Button--link',
|
className: 'Notification-action Button Button--icon Button--link',
|
||||||
icon: 'fas fa-check',
|
icon: 'fas fa-check',
|
||||||
title: app.translator.trans('core.forum.notifications.mark_as_read_tooltip'),
|
title: app.translator.trans('core.forum.notifications.mark_as_read_tooltip'),
|
||||||
onclick: e => {
|
onclick: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
this.markAsRead();
|
this.markAsRead();
|
||||||
}
|
},
|
||||||
})}
|
})}
|
||||||
{avatar(notification.fromUser())}
|
{avatar(notification.fromUser())}
|
||||||
{icon(this.icon(), { className: 'Notification-icon' })}
|
{icon(this.icon(), { className: 'Notification-icon' })}
|
||||||
<span className="Notification-content">{this.content()}</span>
|
<span className="Notification-content">{this.content()}</span>
|
||||||
{humanTime(notification.createdAt())}
|
{humanTime(notification.createdAt())}
|
||||||
<div className="Notification-excerpt">
|
<div className="Notification-excerpt">{this.excerpt()}</div>
|
||||||
{this.excerpt()}
|
|
||||||
</div>
|
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -55,8 +56,7 @@ export default class Notification extends Component {
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
icon() {
|
icon() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the URL that the notification should link to.
|
* Get the URL that the notification should link to.
|
||||||
@ -64,8 +64,7 @@ export default class Notification extends Component {
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
href() {
|
href() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the content of the notification.
|
* Get the content of the notification.
|
||||||
@ -73,8 +72,7 @@ export default class Notification extends Component {
|
|||||||
* @return {VirtualElement}
|
* @return {VirtualElement}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
content() {
|
content() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the excerpt of the notification.
|
* Get the excerpt of the notification.
|
||||||
@ -82,8 +80,7 @@ export default class Notification extends Component {
|
|||||||
* @return {VirtualElement}
|
* @return {VirtualElement}
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
excerpt() {
|
excerpt() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the notification as read.
|
* Mark the notification as read.
|
||||||
|
@ -37,15 +37,15 @@ export default class NotificationGrid extends Component {
|
|||||||
|
|
||||||
// For each of the notification type-method combinations, create and store a
|
// For each of the notification type-method combinations, create and store a
|
||||||
// new checkbox component instance, which we will render in the view.
|
// new checkbox component instance, which we will render in the view.
|
||||||
this.types.forEach(type => {
|
this.types.forEach((type) => {
|
||||||
this.methods.forEach(method => {
|
this.methods.forEach((method) => {
|
||||||
const key = this.preferenceKey(type.name, method.name);
|
const key = this.preferenceKey(type.name, method.name);
|
||||||
const preference = this.props.user.preferences()[key];
|
const preference = this.props.user.preferences()[key];
|
||||||
|
|
||||||
this.inputs[key] = new Checkbox({
|
this.inputs[key] = new Checkbox({
|
||||||
state: !!preference,
|
state: !!preference,
|
||||||
disabled: typeof preference === 'undefined',
|
disabled: typeof preference === 'undefined',
|
||||||
onchange: () => this.toggle([key])
|
onchange: () => this.toggle([key]),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -57,7 +57,7 @@ export default class NotificationGrid extends Component {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td />
|
<td />
|
||||||
{this.methods.map(method => (
|
{this.methods.map((method) => (
|
||||||
<th className="NotificationGrid-groupToggle" onclick={this.toggleMethod.bind(this, method.name)}>
|
<th className="NotificationGrid-groupToggle" onclick={this.toggleMethod.bind(this, method.name)}>
|
||||||
{icon(method.icon)} {method.label}
|
{icon(method.icon)} {method.label}
|
||||||
</th>
|
</th>
|
||||||
@ -66,15 +66,13 @@ export default class NotificationGrid extends Component {
|
|||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{this.types.map(type => (
|
{this.types.map((type) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td className="NotificationGrid-groupToggle" onclick={this.toggleType.bind(this, type.name)}>
|
<td className="NotificationGrid-groupToggle" onclick={this.toggleType.bind(this, type.name)}>
|
||||||
{icon(type.icon)} {type.label}
|
{icon(type.icon)} {type.label}
|
||||||
</td>
|
</td>
|
||||||
{this.methods.map(method => (
|
{this.methods.map((method) => (
|
||||||
<td className="NotificationGrid-checkbox">
|
<td className="NotificationGrid-checkbox">{this.inputs[this.preferenceKey(type.name, method.name)].render()}</td>
|
||||||
{this.inputs[this.preferenceKey(type.name, method.name)].render()}
|
|
||||||
</td>
|
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
@ -88,11 +86,17 @@ export default class NotificationGrid extends Component {
|
|||||||
|
|
||||||
this.$('thead .NotificationGrid-groupToggle').bind('mouseenter mouseleave', function (e) {
|
this.$('thead .NotificationGrid-groupToggle').bind('mouseenter mouseleave', function (e) {
|
||||||
const i = parseInt($(this).index(), 10) + 1;
|
const i = parseInt($(this).index(), 10) + 1;
|
||||||
$(this).parents('table').find('td:nth-child(' + i + ')').toggleClass('highlighted', e.type === 'mouseenter');
|
$(this)
|
||||||
|
.parents('table')
|
||||||
|
.find('td:nth-child(' + i + ')')
|
||||||
|
.toggleClass('highlighted', e.type === 'mouseenter');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$('tbody .NotificationGrid-groupToggle').bind('mouseenter mouseleave', function (e) {
|
this.$('tbody .NotificationGrid-groupToggle').bind('mouseenter mouseleave', function (e) {
|
||||||
$(this).parent().find('td').toggleClass('highlighted', e.type === 'mouseenter');
|
$(this)
|
||||||
|
.parent()
|
||||||
|
.find('td')
|
||||||
|
.toggleClass('highlighted', e.type === 'mouseenter');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +111,7 @@ export default class NotificationGrid extends Component {
|
|||||||
const preferences = user.preferences();
|
const preferences = user.preferences();
|
||||||
const enabled = !preferences[keys[0]];
|
const enabled = !preferences[keys[0]];
|
||||||
|
|
||||||
keys.forEach(key => {
|
keys.forEach((key) => {
|
||||||
const control = this.inputs[key];
|
const control = this.inputs[key];
|
||||||
|
|
||||||
control.loading = true;
|
control.loading = true;
|
||||||
@ -117,7 +121,7 @@ export default class NotificationGrid extends Component {
|
|||||||
m.redraw();
|
m.redraw();
|
||||||
|
|
||||||
user.save({ preferences }).then(() => {
|
user.save({ preferences }).then(() => {
|
||||||
keys.forEach(key => this.inputs[key].loading = false);
|
keys.forEach((key) => (this.inputs[key].loading = false));
|
||||||
|
|
||||||
m.redraw();
|
m.redraw();
|
||||||
});
|
});
|
||||||
@ -129,9 +133,7 @@ export default class NotificationGrid extends Component {
|
|||||||
* @param {String} method
|
* @param {String} method
|
||||||
*/
|
*/
|
||||||
toggleMethod(method) {
|
toggleMethod(method) {
|
||||||
const keys = this.types
|
const keys = this.types.map((type) => this.preferenceKey(type.name, method)).filter((key) => !this.inputs[key].props.disabled);
|
||||||
.map(type => this.preferenceKey(type.name, method))
|
|
||||||
.filter(key => !this.inputs[key].props.disabled);
|
|
||||||
|
|
||||||
this.toggle(keys);
|
this.toggle(keys);
|
||||||
}
|
}
|
||||||
@ -142,9 +144,7 @@ export default class NotificationGrid extends Component {
|
|||||||
* @param {String} type
|
* @param {String} type
|
||||||
*/
|
*/
|
||||||
toggleType(type) {
|
toggleType(type) {
|
||||||
const keys = this.methods
|
const keys = this.methods.map((method) => this.preferenceKey(type, method.name)).filter((key) => !this.inputs[key].props.disabled);
|
||||||
.map(method => this.preferenceKey(type, method.name))
|
|
||||||
.filter(key => !this.inputs[key].props.disabled);
|
|
||||||
|
|
||||||
this.toggle(keys);
|
this.toggle(keys);
|
||||||
}
|
}
|
||||||
@ -207,7 +207,7 @@ export default class NotificationGrid extends Component {
|
|||||||
items.add('discussionRenamed', {
|
items.add('discussionRenamed', {
|
||||||
name: 'discussionRenamed',
|
name: 'discussionRenamed',
|
||||||
icon: 'fas fa-pencil-alt',
|
icon: 'fas fa-pencil-alt',
|
||||||
label: app.translator.trans('core.forum.settings.notify_discussion_renamed_label')
|
label: app.translator.trans('core.forum.settings.notify_discussion_renamed_label'),
|
||||||
});
|
});
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
@ -36,7 +36,7 @@ export default class NotificationList extends Component {
|
|||||||
className: 'Button Button--icon Button--link',
|
className: 'Button Button--icon Button--link',
|
||||||
icon: 'fas fa-check',
|
icon: 'fas fa-check',
|
||||||
title: app.translator.trans('core.forum.notifications.mark_all_as_read_tooltip'),
|
title: app.translator.trans('core.forum.notifications.mark_all_as_read_tooltip'),
|
||||||
onclick: this.markAllAsRead.bind(this)
|
onclick: this.markAllAsRead.bind(this),
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -44,11 +44,12 @@ export default class NotificationList extends Component {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="NotificationList-content">
|
<div className="NotificationList-content">
|
||||||
{pages.length ? pages.map(notifications => {
|
{pages.length
|
||||||
|
? pages.map((notifications) => {
|
||||||
const groups = [];
|
const groups = [];
|
||||||
const discussions = {};
|
const discussions = {};
|
||||||
|
|
||||||
notifications.forEach(notification => {
|
notifications.forEach((notification) => {
|
||||||
const subject = notification.subject();
|
const subject = notification.subject();
|
||||||
|
|
||||||
if (typeof subject === 'undefined') return;
|
if (typeof subject === 'undefined') return;
|
||||||
@ -71,27 +72,22 @@ export default class NotificationList extends Component {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return groups.map(group => {
|
return groups.map((group) => {
|
||||||
const badges = group.discussion && group.discussion.badges().toArray();
|
const badges = group.discussion && group.discussion.badges().toArray();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="NotificationGroup">
|
<div className="NotificationGroup">
|
||||||
{group.discussion
|
{group.discussion ? (
|
||||||
? (
|
<a className="NotificationGroup-header" href={app.route.discussion(group.discussion)} config={m.route}>
|
||||||
<a className="NotificationGroup-header"
|
|
||||||
href={app.route.discussion(group.discussion)}
|
|
||||||
config={m.route}>
|
|
||||||
{badges && badges.length ? <ul className="NotificationGroup-badges badges">{listItems(badges)}</ul> : ''}
|
{badges && badges.length ? <ul className="NotificationGroup-badges badges">{listItems(badges)}</ul> : ''}
|
||||||
{group.discussion.title()}
|
{group.discussion.title()}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<div className="NotificationGroup-header">
|
<div className="NotificationGroup-header">{app.forum.attribute('title')}</div>
|
||||||
{app.forum.attribute('title')}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ul className="NotificationGroup-content">
|
<ul className="NotificationGroup-content">
|
||||||
{group.notifications.map(notification => {
|
{group.notifications.map((notification) => {
|
||||||
const NotificationComponent = app.notificationComponents[notification.contentType()];
|
const NotificationComponent = app.notificationComponents[notification.contentType()];
|
||||||
return NotificationComponent ? <li>{NotificationComponent.component({ notification })}</li> : '';
|
return NotificationComponent ? <li>{NotificationComponent.component({ notification })}</li> : '';
|
||||||
})}
|
})}
|
||||||
@ -99,10 +95,15 @@ export default class NotificationList extends Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}) : ''}
|
})
|
||||||
{this.loading
|
: ''}
|
||||||
? <LoadingIndicator className="LoadingIndicator--block" />
|
{this.loading ? (
|
||||||
: (pages.length ? '' : <div className="NotificationList-empty">{app.translator.trans('core.forum.notifications.empty_text')}</div>)}
|
<LoadingIndicator className="LoadingIndicator--block" />
|
||||||
|
) : pages.length ? (
|
||||||
|
''
|
||||||
|
) : (
|
||||||
|
<div className="NotificationList-empty">{app.translator.trans('core.forum.notifications.empty_text')}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -161,7 +162,8 @@ export default class NotificationList extends Component {
|
|||||||
|
|
||||||
const params = app.cache.notifications ? { page: { offset: app.cache.notifications.length * 10 } } : null;
|
const params = app.cache.notifications ? { page: { offset: app.cache.notifications.length * 10 } } : null;
|
||||||
|
|
||||||
return app.store.find('notifications', params)
|
return app.store
|
||||||
|
.find('notifications', params)
|
||||||
.then(this.parseResults.bind(this))
|
.then(this.parseResults.bind(this))
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -194,13 +196,13 @@ export default class NotificationList extends Component {
|
|||||||
|
|
||||||
app.session.user.pushAttributes({ unreadNotificationCount: 0 });
|
app.session.user.pushAttributes({ unreadNotificationCount: 0 });
|
||||||
|
|
||||||
app.cache.notifications.forEach(notifications => {
|
app.cache.notifications.forEach((notifications) => {
|
||||||
notifications.forEach(notification => notification.pushAttributes({isRead: true}))
|
notifications.forEach((notification) => notification.pushAttributes({ isRead: true }));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.request({
|
app.request({
|
||||||
url: app.forum.attribute('apiUrl') + '/notifications/read',
|
url: app.forum.attribute('apiUrl') + '/notifications/read',
|
||||||
method: 'POST'
|
method: 'POST',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user