1
0
mirror of https://github.com/flarum/core.git synced 2025-08-17 22:01:44 +02:00

Compare commits

..

9 Commits

Author SHA1 Message Date
David Wheatley
7de9afdcb9 chore: remove moz vendor prefix 2021-12-24 13:51:32 +01:00
David Wheatley
140a6e4477 unformat 2021-12-24 13:49:06 +01:00
David Wheatley
ec0c233d15 chore: remove unneeded CSS vendor prefixes 2021-12-24 13:47:18 +01:00
flarum-bot
dc661bf144 Bundled output for commit 7a27f494c6
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2021-12-22 18:58:42 +00:00
David Wheatley
7a27f494c6 fix: hide WelcomeHero when content is empty (#3219) 2021-12-22 13:54:21 -05:00
Sebastian Kessler
edde6be301 docs: fix broken contribution link in README; add screenshot (#3211) 2021-12-20 14:55:25 -05:00
flarum-bot
96fdaac3ef Bundled output for commit e57655553f
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2021-12-20 16:32:17 +00:00
David Wheatley
e57655553f fix: breaking change in Search component - renaming of state property (#3212)
* fix: breaking change in search component's public api

* fix: add setter

* feat: add deprecation warning helper

This reduces bundle size as a result of deprecation warning in our JS, as well as maintaining a consistent format across warnings.

* feat: fire deprecation warning on usage of `Search.state`

* chore: use consistent deprecation warning across core

* fix: `/pull` not `/issue`

* chore: format
2021-12-20 16:28:28 +00:00
David Wheatley
a1cc456f3a fix(postmeta): use app baseUrl instead of location.origin (#3216) 2021-12-20 16:25:04 +00:00
43 changed files with 233 additions and 164 deletions

View File

@@ -9,7 +9,6 @@
<a href="https://github.styleci.io/repos/28257573"><img src="https://github.styleci.io/repos/28257573/shield?style=flat" alt="StyleCI"></a>
</p>
## About Flarum
**[Flarum](https://flarum.org/) is a delightfully simple discussion platform for your website.** It's fast and easy to use, with all the features you need to run a successful community. It is designed to be:
@@ -20,13 +19,15 @@
* **Powerful and extensible.** Customize, extend, and integrate Flarum to suit your community. Flarums architecture is amazingly flexible, with a powerful Extension API.
![Screenshot of a Flarum instance, showing multiple discussions and tags.](https://flarum.org/assets/img/home-screenshot.png)
## Installation
This repository contains Flarum's core code. If you want to set up a forum, visit the [Flarum skeleton repository](https://github.com/flarum/flarum).
This repository contains Flarum's core code. If you want to set up a forum, visit the [Flarum skeleton repository](https://github.com/flarum/flarum). For support, refer to the [documentation](https://docs.flarum.org/), and ask questions on [Flarum Discuss](https://discuss.flarum.org/) (our community forum) or [Discord server](https://flarum.org/discord/).
## Contributing
Thank you for considering contributing to Flarum! Please read the **[Contributing guide](https://flarum.org/docs/contributing.html)** to learn how you can help.
Thank you for considering contributing to Flarum! Please read the **[Contributing guide](https://docs.flarum.org/contributing)** to learn how you can help.
## Security Vulnerabilities

View File

@@ -10,3 +10,17 @@
* can fix.
*/
export default function fireDebugWarning(...args: Parameters<typeof console.warn>): void;
/**
* Fire a Flarum deprecation warning which is shown in the JS console.
*
* These warnings are only shown when the forum is in debug mode, and the function exists to
* reduce bundle size caused by multiple warnings across our JavaScript.
*
* @param message The message to display. (Short, but sweet, please!)
* @param githubId The PR or Issue ID with more info in relation to this change.
* @param [removedFrom] The version in which this feature will be completely removed. (default: 2.0)
* @param [repo] The repo which the issue or PR is located in. (default: flarum/core)
*
* @see {@link fireDebugWarning}
*/
export declare function fireDeprecationWarning(message: string, githubId: string, removedFrom?: string, repo?: string): void;

View File

@@ -42,12 +42,22 @@ export interface SearchAttrs extends ComponentAttrs {
*
* - state: SearchState instance.
*/
export default class Search<T extends SearchAttrs = SearchAttrs> extends Component<T> {
export default class Search<T extends SearchAttrs = SearchAttrs> extends Component<T, SearchState> {
/**
* The minimum query length before sources are searched.
*/
protected static MIN_SEARCH_LEN: number;
/**
* The instance of `SearchState` for this component.
*/
protected searchState: SearchState;
/**
* The instance of `SearchState` for this component.
*
* @deprecated Replace with`this.searchState` instead.
*/
protected get state(): SearchState;
protected set state(state: SearchState);
/**
* Whether or not the search input has focus.
*/

View File

@@ -1,13 +1,26 @@
import Component from '../../common/Component';
import type Mithril from 'mithril';
export interface IWelcomeHeroAttrs {
}
/**
* The `WelcomeHero` component displays a hero that welcomes the user to the
* forum.
*/
export default class WelcomeHero extends Component<import("../../common/Component").ComponentAttrs, undefined> {
constructor();
hidden: string | boolean | null | undefined;
export default class WelcomeHero extends Component<IWelcomeHeroAttrs> {
/**
* @deprecated Extend the `isHidden` method instead.
*/
hidden: boolean;
oninit(vnode: Mithril.Vnode<IWelcomeHeroAttrs, this>): void;
view(vnode: Mithril.Vnode<IWelcomeHeroAttrs, this>): JSX.Element | null;
/**
* Hide the welcome hero.
*/
hide(): void;
/**
* Determines whether the welcome hero should be hidden.
*
* @returns if the welcome hero is hidden.
*/
isHidden(): boolean;
}
import Component from "../../common/Component";

2
js/dist/admin.js generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/admin.js.map generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/forum.js generated vendored

File diff suppressed because one or more lines are too long

2
js/dist/forum.js.map generated vendored

File diff suppressed because one or more lines are too long

View File

@@ -20,6 +20,6 @@ export default class DashboardWidget extends Component {
* @return {VirtualElement}
*/
content() {
return null;
return [];
}
}

View File

@@ -21,7 +21,7 @@ export default class ExtensionsWidget extends DashboardWidget {
return (
<div className="ExtensionsWidget-list">
{Object.keys(categories).map((category) => !!this.categorizedExtensions[category] && this.extensionCategory(category))}
{Object.keys(categories).map((category) => (this.categorizedExtensions[category] ? this.extensionCategory(category) : ''))}
</div>
);
}
@@ -41,7 +41,7 @@ export default class ExtensionsWidget extends DashboardWidget {
<Link href={app.route('extension', { id: extension.id })}>
<div className="ExtensionListItem-content">
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
{!!extension.icon && icon(extension.icon.name)}
{extension.icon ? icon(extension.icon.name) : ''}
</span>
<span className="ExtensionListItem-title">{extension.extra['flarum-extension'].title}</span>
</div>

View File

@@ -18,7 +18,7 @@ export default class LoadingModal<ModalAttrs extends ILoadingModalAttrs = ILoadi
}
content() {
return null;
return '';
}
onsubmit(e: Event): void {

View File

@@ -56,8 +56,9 @@ export default class PermissionGrid<CustomAttrs extends IPermissionGridAttrs = I
{scopes.map((scope) => (
<th>
{scope.label}{' '}
{!!scope.onremove &&
Button.component({ icon: 'fas fa-times', className: 'Button Button--text PermissionGrid-removeScope', onclick: scope.onremove })}
{scope.onremove
? Button.component({ icon: 'fas fa-times', className: 'Button Button--text PermissionGrid-removeScope', onclick: scope.onremove })
: ''}
</th>
))}
<th>{this.scopeControlItems().toArray()}</th>

View File

@@ -175,7 +175,7 @@ export default class UserListPage extends AdminPage {
'id',
{
name: app.translator.trans('core.admin.users.grid.columns.user_id.title'),
content: (user: User) => user.id() ?? null,
content: (user: User) => user.id() ?? '',
},
100
);

View File

@@ -1,6 +1,6 @@
import app from '../common/app';
import { FlarumRequestOptions } from './Application';
import fireDebugWarning from './helpers/fireDebugWarning';
import { fireDeprecationWarning } from './helpers/fireDebugWarning';
import Model, { ModelData, SavedModelData } from './Model';
export interface MetaInformation {
@@ -123,11 +123,7 @@ export default class Store {
if (!this.models[data.type]) {
if (!allowUnregistered) {
setTimeout(() =>
fireDebugWarning(
`[Flarum 2.0 Deprecation] Cannot push object of type \`${data.type}\`, as that type has not yet been registered in the store. This will throw an error in Flarum 2.0 and later.
For more information, see https://github.com/flarum/core/pull/3206.`
)
fireDeprecationWarning(`Pushing object of type \`${data.type}\` not allowed, as type not yet registered in the store.`, '3206')
);
}

View File

@@ -37,7 +37,7 @@ export default class Dropdown extends Component {
}
view(vnode) {
const items = vnode.children ? listItems(vnode.children) : null;
const items = vnode.children ? listItems(vnode.children) : [];
const renderItems = this.attrs.lazyDraw ? this.showing : true;
return (
@@ -128,9 +128,9 @@ export default class Dropdown extends Component {
*/
getButtonContent(children) {
return [
!!this.attrs.icon && icon(this.attrs.icon, { className: 'Button-icon' }),
this.attrs.icon ? icon(this.attrs.icon, { className: 'Button-icon' }) : '',
<span className="Button-label">{this.attrs.label}</span>,
!!this.attrs.caretIcon && icon(this.attrs.caretIcon, { className: 'Button-caret' }),
this.attrs.caretIcon ? icon(this.attrs.caretIcon, { className: 'Button-caret' }) : '',
];
}

View File

@@ -89,7 +89,7 @@ export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEd
disabled={this.nonAdminEditingAdmin()}
/>
</div>
{!this.isEmailConfirmed() && this.userIsAdmin(app.session.user) && (
{!this.isEmailConfirmed() && this.userIsAdmin(app.session.user) ? (
<div>
{Button.component(
{
@@ -100,6 +100,8 @@ export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEd
app.translator.trans('core.lib.edit_user.activate_button')
)}
</div>
) : (
''
)}
</div>,
30
@@ -124,7 +126,7 @@ export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEd
/>
{app.translator.trans('core.lib.edit_user.set_password_label')}
</label>
{this.setPassword() && (
{this.setPassword() ? (
<input
className="FormControl"
type="password"
@@ -133,6 +135,8 @@ export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEd
bidi={this.password}
disabled={this.nonAdminEditingAdmin()}
/>
) : (
''
)}
</div>
</div>,

View File

@@ -105,7 +105,7 @@ export default abstract class Modal<ModalAttrs extends IInternalModalAttrs = IIn
<h3 className="App-titleControl App-titleControl--text">{this.title()}</h3>
</div>
{!!this.alertAttrs && <div className="Modal-alert">{Alert.component(this.alertAttrs)}</div>}
{this.alertAttrs ? <div className="Modal-alert">{Alert.component(this.alertAttrs)}</div> : ''}
{this.content()}
</form>

View File

@@ -65,7 +65,7 @@ export default class Navigation extends Component {
getPaneButton() {
const { pane } = app;
if (!pane || !pane.active) return null;
if (!pane || !pane.active) return '';
return Button.component({
className: 'Button Button--icon Navigation-pin' + (pane.pinned ? ' active' : ''),
@@ -81,7 +81,7 @@ export default class Navigation extends Component {
* @protected
*/
getDrawerButton() {
if (!this.attrs.drawer) return null;
if (!this.attrs.drawer) return '';
const { drawer } = app;
const user = app.session.user;

View File

@@ -12,7 +12,7 @@ export default class RequestErrorModal<CustomAttrs extends IRequestErrorModalAtt
}
title() {
return !!this.attrs.error.xhr && `${this.attrs.error.xhr.status} ${this.attrs.error.xhr.statusText}`;
return this.attrs.error.xhr ? `${this.attrs.error.xhr.status} ${this.attrs.error.xhr.statusText}` : '';
}
content() {

View File

@@ -12,6 +12,6 @@ export default class Switch extends Checkbox {
}
getDisplay() {
return !!this.attrs.loading && super.getDisplay();
return this.attrs.loading ? super.getDisplay() : '';
}
}

View File

@@ -16,3 +16,21 @@ export default function fireDebugWarning(...args: Parameters<typeof console.warn
console.warn(...args);
}
/**
* Fire a Flarum deprecation warning which is shown in the JS console.
*
* These warnings are only shown when the forum is in debug mode, and the function exists to
* reduce bundle size caused by multiple warnings across our JavaScript.
*
* @param message The message to display. (Short, but sweet, please!)
* @param githubId The PR or Issue ID with more info in relation to this change.
* @param [removedFrom] The version in which this feature will be completely removed. (default: 2.0)
* @param [repo] The repo which the issue or PR is located in. (default: flarum/core)
*
* @see {@link fireDebugWarning}
*/
export function fireDeprecationWarning(message: string, githubId: string, removedFrom: string = '2.0', repo: string = 'flarum/core'): void {
// GitHub auto-redirects between `/pull` and `/issues` for us, so using `/pull` saves 2 bytes!
fireDebugWarning(`[Flarum ${removedFrom} Deprecation] ${message}\n\nSee: https://github.com/${repo}/pull/${githubId}`);
}

View File

@@ -1,15 +1,14 @@
import { fireDeprecationWarning } from '../helpers/fireDebugWarning';
const deprecatedNotice = 'The `evented` util is deprecated and no longer supported.';
const deprecationIssueId = '2547';
/**
* The `evented` mixin provides methods allowing an object to trigger events,
* running externally registered event handlers.
*
* @deprecated v1.2, to be removed in v2.0
*/
import fireDebugWarning from '../helpers/fireDebugWarning';
const deprecatedNotice =
'The `evented` util is deprecated and will be removed in Flarum 2.0. For more info, please see https://github.com/flarum/core/issues/2547';
export default {
/**
* Arrays of registered event handlers, grouped by the event name.
@@ -27,7 +26,7 @@ export default {
* @protected
*/
getHandlers(event) {
fireDebugWarning(deprecatedNotice);
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
this.handlers = this.handlers || {};
@@ -44,7 +43,7 @@ export default {
* @public
*/
trigger(event, ...args) {
fireDebugWarning(deprecatedNotice);
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
this.getHandlers(event).forEach((handler) => handler.apply(this, args));
},
@@ -56,7 +55,7 @@ export default {
* @param {function} handler The function to handle the event.
*/
on(event, handler) {
fireDebugWarning(deprecatedNotice);
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
this.getHandlers(event).push(handler);
},
@@ -69,7 +68,7 @@ export default {
* @param {function} handler The function to handle the event.
*/
one(event, handler) {
fireDebugWarning(deprecatedNotice);
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
const wrapper = function () {
handler.apply(this, arguments);
@@ -87,7 +86,7 @@ export default {
* @param {function} handler The function that handles the event.
*/
off(event, handler) {
fireDebugWarning(deprecatedNotice);
fireDeprecationWarning(deprecatedNotice, deprecationIssueId);
const handlers = this.getHandlers(event);
const index = handlers.indexOf(handler);

View File

@@ -51,7 +51,7 @@ export default class Composer extends Component {
<div className="Composer-handle" oncreate={this.configHandle.bind(this)} />
<ul className="Composer-controls">{listItems(this.controlItems().toArray())}</ul>
<div className="Composer-content" onclick={showIfMinimized}>
{!!body.componentClass && body.componentClass.component({ ...body.attrs, composer: this.state, disabled: classes.minimized })}
{body.componentClass ? body.componentClass.component({ ...body.attrs, composer: this.state, disabled: classes.minimized }) : ''}
</div>
</div>
);

View File

@@ -125,16 +125,17 @@ export default class LogInModal<CustomAttrs extends ILoginModalAttrs = ILoginMod
}
footer() {
return (
<>
<p className="LogInModal-forgotPassword">
<a onclick={this.forgotPassword.bind(this)}>{app.translator.trans('core.forum.log_in.forgot_password_link')}</a>
</p>
{!!app.forum.attribute('allowSignUp') && (
<p className="LogInModal-signUp">{app.translator.trans('core.forum.log_in.sign_up_text', { a: <a onclick={this.signUp.bind(this)} /> })}</p>
)}
</>
);
return [
<p className="LogInModal-forgotPassword">
<a onclick={this.forgotPassword.bind(this)}>{app.translator.trans('core.forum.log_in.forgot_password_link')}</a>
</p>,
app.forum.attribute('allowSignUp') ? (
<p className="LogInModal-signUp">{app.translator.trans('core.forum.log_in.sign_up_text', { a: <a onclick={this.signUp.bind(this)} /> })}</p>
) : (
''
),
];
}
/**

View File

@@ -96,7 +96,7 @@ export default class NotificationList extends Component {
<ul className="NotificationGroup-content">
{group.notifications.map((notification) => {
const NotificationComponent = app.notificationComponents[notification.contentType()];
return !!NotificationComponent && <li>{NotificationComponent.component({ notification })}</li>;
return NotificationComponent ? <li>{NotificationComponent.component({ notification })}</li> : '';
})}
</ul>
</div>

View File

@@ -59,7 +59,7 @@ export default class Post extends Component {
<aside className="Post-actions">
<ul>
{listItems(this.actionItems().toArray())}
{!!controls.length && (
{controls.length ? (
<li>
<Dropdown
className="Post-controls"
@@ -73,6 +73,8 @@ export default class Post extends Component {
{controls}
</Dropdown>
</li>
) : (
''
)}
</ul>
</aside>
@@ -112,7 +114,7 @@ export default class Post extends Component {
* @return {Array}
*/
content() {
return null;
return [];
}
/**

View File

@@ -55,6 +55,6 @@ export default class PostMeta extends Component {
* @returns {String}
*/
getPermalink(post) {
return window.location.origin + app.route.post(post);
return app.forum.attribute('baseUrl') + app.route.post(post);
}
}

View File

@@ -18,7 +18,7 @@ export default class PostPreview extends Component {
const post = this.attrs.post;
const user = post.user();
const content = post.contentType() === 'comment' && post.contentPlain();
const excerpt = !!content && highlight(content, this.attrs.highlight, 300);
const excerpt = content ? highlight(content, this.attrs.highlight, 300) : '';
return (
<Link className="PostPreview" href={app.route.post(post)} onclick={this.attrs.onclick}>

View File

@@ -29,10 +29,7 @@ export default class PostUser extends Component {
);
}
/**
* @type {import('mithril').Children}
*/
let card = null;
let card = '';
if (!post.isHidden() && this.attrs.cardVisible) {
card = UserCard.component({

View File

@@ -9,8 +9,8 @@ import icon from '../../common/helpers/icon';
import SearchState from '../states/SearchState';
import DiscussionsSearchSource from './DiscussionsSearchSource';
import UsersSearchSource from './UsersSearchSource';
import { fireDeprecationWarning } from '../../common/helpers/fireDebugWarning';
import type Mithril from 'mithril';
import Model from '../../common/Model';
/**
* The `SearchSource` interface defines a section of search results in the
@@ -53,14 +53,33 @@ export interface SearchAttrs extends ComponentAttrs {
*
* - state: SearchState instance.
*/
export default class Search<T extends SearchAttrs = SearchAttrs> extends Component<T> {
export default class Search<T extends SearchAttrs = SearchAttrs> extends Component<T, SearchState> {
/**
* The minimum query length before sources are searched.
*/
protected static MIN_SEARCH_LEN = 3;
/**
* The instance of `SearchState` for this component.
*/
protected searchState!: SearchState;
/**
* The instance of `SearchState` for this component.
*
* @deprecated Replace with`this.searchState` instead.
*/
// TODO: [Flarum 2.0] Remove this.
// @ts-expect-error This is a get accessor, while superclass defines this as a property. This is needed to prevent breaking changes, however.
protected get state() {
fireDeprecationWarning('`state` property of the Search component is deprecated', '3212');
return this.searchState;
}
protected set state(state: SearchState) {
fireDeprecationWarning('`state` property of the Search component is deprecated', '3212');
this.searchState = state;
}
/**
* Whether or not the search input has focus.
*/

View File

@@ -34,18 +34,19 @@ export default class UserCard extends Component {
<div className={'UserCard ' + (this.attrs.className || '')} style={color && { '--usercard-bg': color }}>
<div className="darkenBackground">
<div className="container">
{!!controls.length &&
Dropdown.component(
{
className: 'UserCard-controls App-primaryControl',
menuClassName: 'Dropdown-menu--right',
buttonClassName: this.attrs.controlsButtonClassName,
label: app.translator.trans('core.forum.user_controls.button'),
accessibleToggleLabel: app.translator.trans('core.forum.user_controls.toggle_dropdown_accessible_label'),
icon: 'fas fa-ellipsis-v',
},
controls
)}
{controls.length
? Dropdown.component(
{
className: 'UserCard-controls App-primaryControl',
menuClassName: 'Dropdown-menu--right',
buttonClassName: this.attrs.controlsButtonClassName,
label: app.translator.trans('core.forum.user_controls.button'),
accessibleToggleLabel: app.translator.trans('core.forum.user_controls.toggle_dropdown_accessible_label'),
icon: 'fas fa-ellipsis-v',
},
controls
)
: ''}
<div className="UserCard-profile">
<h2 className="UserCard-identity">
@@ -59,7 +60,7 @@ export default class UserCard extends Component {
)}
</h2>
{!!badges.length && <ul className="UserCard-badges badges">{listItems(badges)}</ul>}
{badges.length ? <ul className="UserCard-badges badges">{listItems(badges)}</ul> : ''}
<ul className="UserCard-info">{listItems(this.infoItems().toArray())}</ul>
</div>

View File

@@ -39,7 +39,7 @@ export default class UsersSearchResults implements SearchSource {
.filter((e, i, arr) => arr.lastIndexOf(e) === i)
.sort((a, b) => a.displayName().localeCompare(b.displayName()));
if (!results.length) return null;
if (!results.length) return [];
return [
<li className="Dropdown-header">{app.translator.trans('core.forum.search.users_heading')}</li>,

View File

@@ -1,50 +0,0 @@
import app from '../../forum/app';
import Component from '../../common/Component';
import Button from '../../common/components/Button';
/**
* The `WelcomeHero` component displays a hero that welcomes the user to the
* forum.
*/
export default class WelcomeHero extends Component {
oninit(vnode) {
super.oninit(vnode);
this.hidden = localStorage.getItem('welcomeHidden');
}
view() {
if (this.hidden) return <div />;
const slideUp = () => {
this.$().slideUp(this.hide.bind(this));
};
return (
<header className="Hero WelcomeHero">
<div class="container">
{Button.component({
icon: 'fas fa-times',
onclick: slideUp,
className: 'Hero-close Button Button--icon Button--link',
'aria-label': app.translator.trans('core.forum.welcome_hero.hide'),
})}
<div className="containerNarrow">
<h2 className="Hero-title">{app.forum.attribute('welcomeTitle')}</h2>
<div className="Hero-subtitle">{m.trust(app.forum.attribute('welcomeMessage'))}</div>
</div>
</div>
</header>
);
}
/**
* Hide the welcome hero.
*/
hide() {
localStorage.setItem('welcomeHidden', 'true');
this.hidden = true;
}
}

View File

@@ -0,0 +1,69 @@
import app from '../app';
import Component from '../../common/Component';
import Button from '../../common/components/Button';
import type Mithril from 'mithril';
export interface IWelcomeHeroAttrs {}
const LOCAL_STORAGE_KEY = 'welcomeHidden';
/**
* The `WelcomeHero` component displays a hero that welcomes the user to the
* forum.
*/
export default class WelcomeHero extends Component<IWelcomeHeroAttrs> {
/**
* @deprecated Extend the `isHidden` method instead.
*/
hidden: boolean = false;
oninit(vnode: Mithril.Vnode<IWelcomeHeroAttrs, this>) {
super.oninit(vnode);
}
view(vnode: Mithril.Vnode<IWelcomeHeroAttrs, this>) {
if (this.isHidden()) return null;
const slideUp = () => {
this.$().slideUp(this.hide.bind(this));
};
return (
<header class="Hero WelcomeHero">
<div class="container">
<Button
icon="fas fa-times"
onclick={slideUp}
className="Hero-close Button Button--icon Button--link"
aria-label={app.translator.trans('core.forum.welcome_hero.hide')}
/>
<div class="containerNarrow">
<h2 class="Hero-title">{app.forum.attribute('welcomeTitle')}</h2>
<div class="Hero-subtitle">{m.trust(app.forum.attribute('welcomeMessage'))}</div>
</div>
</div>
</header>
);
}
/**
* Hide the welcome hero.
*/
hide() {
localStorage.setItem(LOCAL_STORAGE_KEY, 'true');
}
/**
* Determines whether the welcome hero should be hidden.
*
* @returns if the welcome hero is hidden.
*/
isHidden(): boolean {
if (!app.forum.attribute<string>('welcomeTitle')?.trim()) return true;
if (localStorage.getItem(LOCAL_STORAGE_KEY)) return true;
if (this.hidden) return true;
return false;
}
}

View File

@@ -260,7 +260,7 @@ class ComposerState {
// let the CSS decide how high it is. If it's fullscreen, then we need to
// make it as high as the window.
if (this.position === ComposerState.Position.MINIMIZED) {
return null;
return '';
} else if (this.position === ComposerState.Position.FULLSCREEN) {
return $(window).height();
}

View File

@@ -50,7 +50,6 @@
color: var(--muted-color);
}
thead th {
position: -webkit-sticky;
position: sticky;
top: 0;
padding-bottom: 10px;
@@ -74,7 +73,6 @@
}
tbody {
th {
position: -webkit-sticky;
position: sticky;
left: 0;
padding-right: 50px;

View File

@@ -142,8 +142,7 @@
z-index: var(--zindex-modal);
.drawerOpen & {
-webkit-transform: none !important;
transform: none !important;
transform: none !important;
}
}
.drawer-backdrop {

View File

@@ -177,7 +177,6 @@
box-shadow: 0 2px 6px var(--shadow-color);
visibility: hidden;
overflow: auto;
-webkit-overflow-scrolling: touch;
transform: translate(0, 70vh);
transition: transform 0.3s, visibility 0s 0.3s;
@@ -208,8 +207,7 @@
}
}
.open& {
-webkit-transform: none;
transform: none;
transform: none;
visibility: visible;
transition-delay: 0s;
}

View File

@@ -33,7 +33,6 @@
bottom: 0;
left: 0;
z-index: var(--zindex-modal);
-webkit-overflow-scrolling: touch;
// When fading in the modal, animate it to slide down
.Modal {
@@ -143,8 +142,7 @@
transform: translate(0, 100vh);
&.in {
-webkit-transform: none !important;
transform: none !important;
transform: none !important;
}
&:before {
content: " ";
@@ -155,8 +153,7 @@
.Modal {
max-width: 100%;
margin: 0;
-webkit-transform: none !important;
transform: none !important;
transform: none !important;
}
.Modal-content {
border-radius: 0;
@@ -183,7 +180,6 @@
z-index: 1;
}
.Modal-content {
border: 0;
border-radius: var(--border-radius);
box-shadow: 0 7px 15px var(--shadow-color);

View File

@@ -6,7 +6,6 @@
display: inline-block;
width: auto;
-webkit-appearance: none;
-moz-appearance: none;
padding-right: 30px;
cursor: pointer;
line-height: 1;

View File

@@ -65,11 +65,7 @@
// CSS3 Content Columns
/** @deprecated */
.content-columns(@column-count; @column-gap: @grid-gutter-width) {
// Safari
-webkit-column-count: @column-count;
column-count: @column-count;
// Safari
-webkit-column-gap: @column-gap;
column-gap: @column-gap;
}
@@ -85,7 +81,6 @@
// Placeholder text
.placeholder(@color) {
// Safari
&::-webkit-input-placeholder,
&::placeholder {
color: @color;
}
@@ -176,7 +171,7 @@
// User select
// For selecting text on the page
.user-select(@select) {
// Safari + MS Edge
// Safari
-webkit-user-select: @select;
user-select: @select;
}

View File

@@ -80,7 +80,6 @@
padding: 15px 0;
white-space: nowrap;
overflow: auto;
-webkit-overflow-scrolling: touch;
&:after {
content: " ";

View File

@@ -107,25 +107,15 @@
}
.animated {
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
-webkit-animation-duration: 0.5s;
animation-duration: 0.5s;
animation-delay: 1.7s;
-webkit-animation-delay: 1.7s;
}
@-webkit-keyframes fadeIn {
0% {opacity: 0}
100% {opacity: 1}
}
@keyframes fadeIn {
0% {opacity: 0}
100% {opacity: 1}
}
.fadeIn {
-webkit-animation-name: fadeIn;
animation-name: fadeIn;
}