1
0
mirror of https://github.com/flarum/core.git synced 2025-08-13 20:04:24 +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
24 changed files with 191 additions and 121 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

@@ -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

@@ -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

@@ -6,7 +6,6 @@ import PostControls from '../utils/PostControls';
import listItems from '../../common/helpers/listItems';
import ItemList from '../../common/utils/ItemList';
import LoadingIndicator from '../../common/components/LoadingIndicator';
import classList from '../../common/utils/classList';
/**
* The `Post` component displays a single post. The basic post template just
@@ -125,16 +124,24 @@ export default class Post extends Component {
* @returns {string[]}
*/
classes(existing) {
let classes = (existing || '').split(' ').concat(['Post']);
const user = this.attrs.post.user();
const discussion = this.attrs.post.discussion();
return classList(existing, 'Post', {
'Post--loading': this.loading,
'Post--by-actor': user === app.session.user,
'Post--by-start-user': user?.id() === discussion.attribute('startUserId'),
})
.split(' ')
.filter((x) => !!x);
if (this.loading) {
classes.push('Post--loading');
}
if (user && user === app.session.user) {
classes.push('Post--by-actor');
}
if (user && user.id() === discussion.attribute('startUserId')) {
classes.push('Post--by-start-user');
}
return classes;
}
/**

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

@@ -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

@@ -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

@@ -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;
}