mirror of
https://github.com/flarum/framework.git
synced 2025-04-21 11:28:48 +08:00
Compare commits
No commits in common. "2.x" and "v2.0.0-beta.1" have entirely different histories.
2.x
...
v2.0.0-bet
@ -18,7 +18,7 @@ trim_trailing_whitespace = false
|
||||
[*.{php,xml,json}]
|
||||
indent_size = 4
|
||||
|
||||
[{tsconfig.json,prettierrc.json,package.json}]
|
||||
[{tsconfig.json,prettierrc.json}]
|
||||
indent_size = 2
|
||||
|
||||
[*.neon]
|
||||
|
24
CHANGELOG.md
24
CHANGELOG.md
@ -1,29 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [v2.0.0-beta.2](https://github.com/flarum/framework/compare/v2.0.0-beta.1...v2.0.0-beta.2)
|
||||
### Fixed
|
||||
- (em) incorrect extension compatibility check [#4155]
|
||||
- (webpack) produces incorrect ext namespace (a7d584f8e1ec650035dafd660a70586d1d0d6bb9)
|
||||
- bad modal alert text alignment [#4152]
|
||||
- beta.1 early bugs (a81d13e26c1c2191859493de2ad45a515ad07b90)
|
||||
- code split fails with common module [#4151]
|
||||
- composer no longer autofocusing [#4149]
|
||||
- conditional renders 0 (1cd644d27feb4eeea5cbaedd009a3af2643af396)
|
||||
- custom styles from 1.x can crash the app [#4159]
|
||||
- discussion page renders before loading is finished [#4158]
|
||||
- discussion posts not always properly loaded [#4156]
|
||||
- fixed side nav missing top spacing [#4147]
|
||||
- invisible dropdown text when header is colored (958dec594486cbc14cf8f922db324a8ffc0245e3)
|
||||
- lazy module import always returns default module [#4148]
|
||||
- mistakenly removed code (33121ed1cc260bf967f0b8c4d10ab5099410bac0)
|
||||
- select input cuts off [#4157]
|
||||
- tag selection icon alignment [#4153]
|
||||
- unexpected subscription breaks rendering [#4150]
|
||||
- use correct human time format key [#4154]
|
||||
### Changed
|
||||
- (mentions) only access related mentions if loaded (9fe17b3c24c5b9236e419a00c1230b2994b8c009)
|
||||
- extensibility improvements (00426c85e38efc91554af33644b088e72b3b3c1b)
|
||||
|
||||
## [v2.0.0-beta.1](https://github.com/flarum/framework/compare/v1.8.9...v2.0.0-beta.1)
|
||||
### Changed
|
||||
- php 8.4 [#4103]
|
||||
|
@ -171,7 +171,7 @@
|
||||
"mockery/mockery": "^1.5",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
"phpstan/phpstan": "^1.10.0",
|
||||
"larastan/larastan": "2.9.14",
|
||||
"larastan/larastan": "2.9.12",
|
||||
"symfony/var-dumper": "^7.0",
|
||||
"flarum/testing-tests": "*@dev"
|
||||
},
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2",
|
||||
"flarum/core": "^2.0.0-beta.1",
|
||||
"flarum/approval": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2",
|
||||
"flarum/core": "^2.0.0-beta.1",
|
||||
"flarum/flags": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -37,8 +37,10 @@ class ScopeFlagVisibility
|
||||
if ($actor->hasPermission('discussion.viewFlags')) {
|
||||
$query->orWhereDoesntHave('post.discussion.tags');
|
||||
}
|
||||
} elseif (! $actor->hasPermission('discussion.viewFlags')) {
|
||||
$query->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
if (! $actor->hasPermission('discussion.viewFlags')) {
|
||||
$query->orWhere('flags.user_id', $actor->id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ class ListTest extends TestCase
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function regular_user_does_not_see_own_flags_of_visible_posts()
|
||||
public function regular_user_sees_own_flags_of_visible_posts()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/flags', [
|
||||
@ -109,7 +109,7 @@ class ListTest extends TestCase
|
||||
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||
|
||||
$ids = Arr::pluck($data, 'id');
|
||||
$this->assertEqualsCanonicalizing([], $ids);
|
||||
$this->assertEqualsCanonicalizing(['2', '4'], $ids);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
@ -122,7 +122,7 @@ class ListWithTagsTest extends TestCase
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function regular_user_does_not_see_own_flags()
|
||||
public function regular_user_sees_own_flags()
|
||||
{
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/flags', [
|
||||
@ -135,7 +135,7 @@ class ListWithTagsTest extends TestCase
|
||||
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||
|
||||
$ids = Arr::pluck($data, 'id');
|
||||
$this->assertEqualsCanonicalizing([], $ids);
|
||||
$this->assertEqualsCanonicalizing(['2', '4'], $ids);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
@ -144,7 +144,7 @@ class IncludeFlagsVisibilityTest extends TestCase
|
||||
'user_with_general_permission_sees_where_unrestricted_tag' => [2, [6, 7, 8]],
|
||||
'user_with_tag1_permission_sees_tag1_flags' => [3, [1, 2, 3, 4, 5]],
|
||||
'normal_user_sees_none' => [4, []],
|
||||
'normal_user_does_not_see_own' => [5, []],
|
||||
'normal_user_sees_own' => [5, [2, 7, 4, 8]],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -25,13 +25,9 @@ class FormatGroupMentions
|
||||
public function __invoke(Renderer $renderer, mixed $context, string $xml): string
|
||||
{
|
||||
return Utils::replaceAttributes($xml, 'GROUPMENTION', function ($attributes) use ($context) {
|
||||
/** @var Group|null $group */
|
||||
$group = match (true) {
|
||||
$context instanceof AbstractModel && $context->isRelation('mentionsGroups') => $context->relationLoaded('mentionsGroups')
|
||||
? $context->mentionsGroups->find($attributes['id']) // @phpstan-ignore-line
|
||||
: $context->mentionsGroups()->find($attributes['id']), // @phpstan-ignore-line
|
||||
default => Group::query()->find($attributes['id']),
|
||||
};
|
||||
$group = ($context instanceof AbstractModel && $context->isRelation('mentionsGroups'))
|
||||
? $context->mentionsGroups->find($attributes['id']) // @phpstan-ignore-line
|
||||
: Group::find($attributes['id']);
|
||||
|
||||
if ($group) {
|
||||
$attributes['groupname'] = $group->name_plural;
|
||||
|
@ -31,13 +31,9 @@ class FormatPostMentions
|
||||
public function __invoke(Renderer $renderer, mixed $context, string $xml): string
|
||||
{
|
||||
return Utils::replaceAttributes($xml, 'POSTMENTION', function ($attributes) use ($context) {
|
||||
/** @var Post|null $post */
|
||||
$post = match (true) {
|
||||
$context instanceof AbstractModel && $context->isRelation('mentionsPosts') => $context->relationLoaded('mentionsPosts')
|
||||
? $context->mentionsPosts->find($attributes['id']) // @phpstan-ignore-line
|
||||
: $context->mentionsPosts()->find($attributes['id']), // @phpstan-ignore-line
|
||||
default => Post::query()->find($attributes['id']),
|
||||
};
|
||||
$post = ($context instanceof AbstractModel && $context->isRelation('mentionsPosts'))
|
||||
? $context->mentionsPosts->find($attributes['id']) // @phpstan-ignore-line
|
||||
: Post::find($attributes['id']);
|
||||
|
||||
if ($post && $post->user) {
|
||||
$attributes['displayname'] = $post->user->display_name;
|
||||
|
@ -21,12 +21,9 @@ class FormatTagMentions
|
||||
{
|
||||
return Utils::replaceAttributes($xml, 'TAGMENTION', function ($attributes) use ($context) {
|
||||
/** @var Tag|null $tag */
|
||||
$tag = match (true) {
|
||||
$context instanceof AbstractModel && $context->isRelation('mentionsTags') => $context->relationLoaded('mentionsTags')
|
||||
? $context->mentionsTags->find($attributes['id']) // @phpstan-ignore-line
|
||||
: $context->mentionsTags()->find($attributes['id']), // @phpstan-ignore-line
|
||||
default => Tag::query()->find($attributes['id']),
|
||||
};
|
||||
$tag = ($context instanceof AbstractModel && $context->isRelation('mentionsTags'))
|
||||
? $context->mentionsTags->find($attributes['id']) // @phpstan-ignore-line
|
||||
: Tag::query()->find($attributes['id']);
|
||||
|
||||
if ($tag) {
|
||||
$attributes['deleted'] = false;
|
||||
|
@ -27,13 +27,9 @@ class FormatUserMentions
|
||||
public function __invoke(Renderer $renderer, mixed $context, string $xml): string
|
||||
{
|
||||
return Utils::replaceAttributes($xml, 'USERMENTION', function ($attributes) use ($context) {
|
||||
/** @var User|null $user */
|
||||
$user = match (true) {
|
||||
$context instanceof AbstractModel && $context->isRelation('mentionsUsers') => $context->relationLoaded('mentionsUsers')
|
||||
? $context->mentionsUsers->find($attributes['id']) // @phpstan-ignore-line
|
||||
: $context->mentionsUsers()->find($attributes['id']), // @phpstan-ignore-line
|
||||
default => User::query()->find($attributes['id']),
|
||||
};
|
||||
$user = ($context instanceof AbstractModel && $context->isRelation('mentionsUsers'))
|
||||
? $context->mentionsUsers->find($attributes['id']) // @phpstan-ignore-line
|
||||
: User::find($attributes['id']);
|
||||
|
||||
$attributes['deleted'] = false;
|
||||
|
||||
|
@ -34,13 +34,9 @@ class UnparsePostMentions
|
||||
protected function updatePostMentionTags(mixed $context, string $xml): string
|
||||
{
|
||||
return Utils::replaceAttributes($xml, 'POSTMENTION', function ($attributes) use ($context) {
|
||||
/** @var Post|null $post */
|
||||
$post = match (true) {
|
||||
$context instanceof AbstractModel && $context->isRelation('mentionsPosts') => $context->relationLoaded('mentionsPosts')
|
||||
? $context->mentionsPosts->find($attributes['id']) // @phpstan-ignore-line
|
||||
: $context->mentionsPosts()->find($attributes['id']), // @phpstan-ignore-line
|
||||
default => Post::query()->find($attributes['id']),
|
||||
};
|
||||
$post = ($context instanceof AbstractModel && $context->isRelation('mentionsPosts'))
|
||||
? $context->mentionsPosts->find($attributes['id']) // @phpstan-ignore-line
|
||||
: Post::find($attributes['id']);
|
||||
|
||||
if ($post && $post->user) {
|
||||
$attributes['displayname'] = $post->user->display_name;
|
||||
|
@ -29,12 +29,9 @@ class UnparseTagMentions
|
||||
{
|
||||
return Utils::replaceAttributes($xml, 'TAGMENTION', function (array $attributes) use ($context) {
|
||||
/** @var Tag|null $tag */
|
||||
$tag = match (true) {
|
||||
$context instanceof AbstractModel && $context->isRelation('mentionsTags') => $context->relationLoaded('mentionsTags')
|
||||
? $context->mentionsTags->find($attributes['id']) // @phpstan-ignore-line
|
||||
: $context->mentionsTags()->find($attributes['id']), // @phpstan-ignore-line
|
||||
default => Tag::query()->find($attributes['id']),
|
||||
};
|
||||
$tag = ($context instanceof AbstractModel && $context->isRelation('mentionsTags'))
|
||||
? $context->mentionsTags->find($attributes['id']) // @phpstan-ignore-line
|
||||
: Tag::query()->find($attributes['id']);
|
||||
|
||||
if ($tag) {
|
||||
$attributes['tagname'] = $tag->name;
|
||||
|
@ -34,13 +34,9 @@ class UnparseUserMentions
|
||||
protected function updateUserMentionTags(mixed $context, string $xml): string
|
||||
{
|
||||
return Utils::replaceAttributes($xml, 'USERMENTION', function ($attributes) use ($context) {
|
||||
/** @var User|null $user */
|
||||
$user = match (true) {
|
||||
$context instanceof AbstractModel && $context->isRelation('mentionsUsers') => $context->relationLoaded('mentionsUsers')
|
||||
? $context->mentionsUsers->find($attributes['id']) // @phpstan-ignore-line
|
||||
: $context->mentionsUsers()->find($attributes['id']), // @phpstan-ignore-line
|
||||
default => User::query()->find($attributes['id']),
|
||||
};
|
||||
$user = ($context instanceof AbstractModel && $context->isRelation('mentionsUsers'))
|
||||
? $context->mentionsUsers->find($attributes['id']) // @phpstan-ignore-line
|
||||
: User::find($attributes['id']);
|
||||
|
||||
$attributes['displayname'] = $user?->display_name ?? $this->translator->trans('core.lib.username.deleted_text');
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
"type": "flarum-extension",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ return [
|
||||
->css(__DIR__.'/less/forum.less')
|
||||
->jsDirectory(__DIR__.'/js/dist/forum')
|
||||
->route('/messages', 'messages')
|
||||
->route('/messages/dialog/{id:\d+}[/{near:\d+}]', 'messages.dialog'),
|
||||
->route('/messages/dialog/{id:\d+}', 'messages.dialog'),
|
||||
|
||||
(new Extend\Frontend('admin'))
|
||||
->js(__DIR__.'/js/dist/admin.js')
|
||||
@ -51,9 +51,7 @@ return [
|
||||
(new Extend\ApiResource(Resource\UserResource::class))
|
||||
->fields(fn () => [
|
||||
Schema\Boolean::make('canSendAnyMessage')
|
||||
->get(fn (User $user, Context $context) => $user->can('sendAnyMessage')),
|
||||
Schema\Boolean::make('canDeleteOwnMessages')
|
||||
->visible(fn (User $user, Context $context) => $context->getActor()->is($user)),
|
||||
->get(fn (object $model, Context $context) => $context->getActor()->can('sendAnyMessage')),
|
||||
Schema\Integer::make('messageCount')
|
||||
->get(function (object $model, Context $context) {
|
||||
return Dialog::whereVisibleTo($context->getActor())
|
||||
|
@ -3,7 +3,7 @@ import DialogListState from '../forum/states/DialogListState';
|
||||
|
||||
declare module 'flarum/forum/routes' {
|
||||
export interface ForumRoutes {
|
||||
dialog: (dialog: Dialog, near?: number) => string;
|
||||
dialog: (tag: Dialog) => string;
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,9 +19,3 @@ declare module 'flarum/forum/states/ComposerState' {
|
||||
composingMessageTo(dialog: Dialog): boolean;
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'flarum/common/models/User' {
|
||||
export default interface User {
|
||||
canSendAnyMessage(): boolean;
|
||||
}
|
||||
}
|
2
extensions/messages/js/dist-typings/admin/extend.d.ts
generated
vendored
2
extensions/messages/js/dist-typings/admin/extend.d.ts
generated
vendored
@ -1,2 +1,2 @@
|
||||
declare const _default: (import("flarum/common/extenders/Store").default | import("flarum/common/extenders/Model").default | import("flarum/common/extenders/Admin").default)[];
|
||||
declare const _default: (import("flarum/common/extenders/Store").default | import("flarum/common/extenders/Admin").default)[];
|
||||
export default _default;
|
||||
|
2
extensions/messages/js/dist-typings/common/extend.d.ts
generated
vendored
2
extensions/messages/js/dist-typings/common/extend.d.ts
generated
vendored
@ -1,2 +1,2 @@
|
||||
declare const _default: (import("flarum/common/extenders/Store").default | import("flarum/common/extenders/Model").default)[];
|
||||
declare const _default: import("flarum/common/extenders/Store").default[];
|
||||
export default _default;
|
||||
|
2
extensions/messages/js/dist-typings/common/models/DialogMessage.d.ts
generated
vendored
2
extensions/messages/js/dist-typings/common/models/DialogMessage.d.ts
generated
vendored
@ -2,7 +2,6 @@ import Model from 'flarum/common/Model';
|
||||
import type Dialog from './Dialog';
|
||||
import type User from 'flarum/common/models/User';
|
||||
export default class DialogMessage extends Model {
|
||||
number(): number;
|
||||
content(): string | null | undefined;
|
||||
contentHtml(): string | null | undefined;
|
||||
renderFailed(): boolean | undefined;
|
||||
@ -10,5 +9,4 @@ export default class DialogMessage extends Model {
|
||||
createdAt(): Date;
|
||||
dialog(): false | Dialog;
|
||||
user(): false | User;
|
||||
canDelete(): boolean;
|
||||
}
|
||||
|
1
extensions/messages/js/dist-typings/forum/components/DialogSection.d.ts
generated
vendored
1
extensions/messages/js/dist-typings/forum/components/DialogSection.d.ts
generated
vendored
@ -10,7 +10,6 @@ export default class DialogSection<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
protected loading: boolean;
|
||||
protected messages: MessageStreamState;
|
||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
|
||||
requestParams(forgetNear?: boolean): any;
|
||||
view(): JSX.Element;
|
||||
actionItems(): ItemList<Mithril.Children>;
|
||||
controlItems(): ItemList<Mithril.Children>;
|
||||
|
2
extensions/messages/js/dist-typings/forum/components/Message.d.ts
generated
vendored
2
extensions/messages/js/dist-typings/forum/components/Message.d.ts
generated
vendored
@ -3,10 +3,8 @@ import Mithril from 'mithril';
|
||||
import AbstractPost, { type IAbstractPostAttrs } from 'flarum/forum/components/AbstractPost';
|
||||
import type User from 'flarum/common/models/User';
|
||||
import DialogMessage from '../../common/models/DialogMessage';
|
||||
import type MessageStreamState from '../states/MessageStreamState';
|
||||
export interface IMessageAttrs extends IAbstractPostAttrs {
|
||||
message: DialogMessage;
|
||||
state: MessageStreamState;
|
||||
}
|
||||
/**
|
||||
* The `Post` component displays a single post. The basic post template just
|
||||
|
4
extensions/messages/js/dist-typings/forum/components/MessagesPage.d.ts
generated
vendored
4
extensions/messages/js/dist-typings/forum/components/MessagesPage.d.ts
generated
vendored
@ -7,7 +7,6 @@ export interface IMessagesPageAttrs extends IPageAttrs {
|
||||
}
|
||||
export default class MessagesPage<CustomAttrs extends IMessagesPageAttrs = IMessagesPageAttrs> extends Page<CustomAttrs> {
|
||||
protected selectedDialog: Stream<Dialog | null>;
|
||||
protected currentDialogId: string | null;
|
||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
|
||||
dialogRequestParams(): {
|
||||
include: string;
|
||||
@ -16,7 +15,6 @@ export default class MessagesPage<CustomAttrs extends IMessagesPageAttrs = IMess
|
||||
onupdate(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void;
|
||||
view(): JSX.Element;
|
||||
hero(): Mithril.Children;
|
||||
contentItems(): ItemList<Mithril.Children>;
|
||||
/**
|
||||
* Build an item list for the part of the toolbar which is concerned with how
|
||||
* the results are displayed. By default this is just a select box to change
|
||||
@ -25,7 +23,7 @@ export default class MessagesPage<CustomAttrs extends IMessagesPageAttrs = IMess
|
||||
viewItems(): ItemList<Mithril.Children>;
|
||||
/**
|
||||
* Build an item list for the part of the toolbar which is about taking action
|
||||
* on the results. By default, this is just a "mark all as read" button.
|
||||
* on the results. By default this is just a "mark all as read" button.
|
||||
*/
|
||||
actionItems(): ItemList<Mithril.Children>;
|
||||
}
|
||||
|
2
extensions/messages/js/dist-typings/forum/extend.d.ts
generated
vendored
2
extensions/messages/js/dist-typings/forum/extend.d.ts
generated
vendored
@ -1,2 +1,2 @@
|
||||
declare const _default: (import("flarum/common/extenders/Store").default | import("flarum/common/extenders/Model").default | import("flarum/common/extenders/Routes").default)[];
|
||||
declare const _default: (import("flarum/common/extenders/Store").default | import("flarum/common/extenders/Routes").default)[];
|
||||
export default _default;
|
||||
|
17
extensions/messages/js/dist-typings/forum/utils/MessageControls.d.ts
generated
vendored
17
extensions/messages/js/dist-typings/forum/utils/MessageControls.d.ts
generated
vendored
@ -1,17 +0,0 @@
|
||||
import ItemList from 'flarum/common/utils/ItemList';
|
||||
import type Mithril from 'mithril';
|
||||
import type DialogMessage from '../../common/models/DialogMessage';
|
||||
import type Message from '../components/Message';
|
||||
declare const MessageControls: {
|
||||
controls(message: DialogMessage, context: Message<any>): ItemList<Mithril.Children>;
|
||||
sections(): {
|
||||
user: (message: DialogMessage, context: Message) => ItemList<Mithril.Children>;
|
||||
moderation: (message: DialogMessage, context: Message) => ItemList<Mithril.Children>;
|
||||
destructive: (message: DialogMessage, context: Message) => ItemList<Mithril.Children>;
|
||||
};
|
||||
userControls(message: DialogMessage, context: Message): ItemList<Mithril.Children>;
|
||||
moderationControls(message: DialogMessage, context: Message): ItemList<Mithril.Children>;
|
||||
destructiveControls(message: DialogMessage, context: Message): ItemList<Mithril.Children>;
|
||||
deleteAction(message: DialogMessage, context: Message): Promise<void> | undefined;
|
||||
};
|
||||
export default MessageControls;
|
2
extensions/messages/js/dist/admin.js
generated
vendored
2
extensions/messages/js/dist/admin.js
generated
vendored
@ -1,2 +1,2 @@
|
||||
(()=>{var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var s in a)e.o(a,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:a[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t),e.d(t,{extend:()=>w});const a=flarum.reg.get("core","admin/app");var s=e.n(a);const r=flarum.reg.get("core","common/extenders");var n=e.n(r);const l=flarum.reg.get("core","common/Model");var o=e.n(l);const i=flarum.reg.get("core","common/utils/computed");var u=e.n(i);const c=flarum.reg.get("core","common/utils/string");class d extends(o()){number(){return o().attribute("number").call(this)}content(){return o().attribute("content").call(this)}contentHtml(){return o().attribute("contentHtml").call(this)}renderFailed(){return o().attribute("renderFailed").call(this)}contentPlain(){return u()("contentHtml",(e=>"string"==typeof e?(0,c.getPlainContent)(e):e)).call(this)}createdAt(){return o().attribute("createdAt",o().transformDate).call(this)}dialog(){return o().hasOne("dialog").call(this)}user(){return o().hasOne("user").call(this)}canDelete(){return o().attribute("canDelete").call(this)}}flarum.reg.add("flarum-messages","common/models/DialogMessage",d);const g=flarum.reg.get("core","common/app");var f=e.n(g);class b extends(o()){title(){return o().attribute("title").call(this)}type(){return o().attribute("type").call(this)}lastMessageAt(){return o().attribute("lastMessageAt",o().transformDate).call(this)}createdAt(){return o().attribute("createdAt",o().transformDate).call(this)}users(){return o().hasMany("users").call(this)}firstMessage(){return o().hasOne("firstMessage").call(this)}lastMessage(){return o().hasOne("lastMessage").call(this)}unreadCount(){return o().attribute("unreadCount").call(this)}lastReadMessageId(){return o().attribute("lastReadMessageId").call(this)}lastReadAt(){return o().attribute("lastReadAt",o().transformDate).call(this)}recipient(){let e=this.users();return e?e.find((e=>e&&e.id()!==f().session.user.id())):null}}flarum.reg.add("flarum-messages","common/models/Dialog",b);const p=flarum.reg.get("core","common/models/User");var _=e.n(p);const h=[(new(n().Store)).add("dialogs",b).add("dialog-messages",d),new(n().Model)(_()).attribute("canSendAnyMessage").attribute("canDeleteOwnMessage")],y=flarum.reg.get("core","admin/components/SettingDropdown");var v=e.n(y);const w=[...h,(new(n().Admin)).permission((()=>({icon:"fas fa-envelope-open-text",label:s().translator.trans("flarum-messages.admin.permissions.send_messages_label"),permission:"dialog.sendMessage",allowGuest:!1})),"start",95).permission((()=>({icon:"far fa-trash-alt",label:s().translator.trans("flarum-messages.admin.permissions.delete_own_messages_label"),id:"flarum-messages.allow_delete_own_messages",setting:()=>(parseInt(s().data.settings["flarum-messages.allow_delete_own_messages"],10),m(v(),{default:"0",key:"flarum-messages.allow_delete_own_messages",options:[{value:"-1",label:s().translator.trans("core.admin.permissions_controls.allow_indefinitely_button")},{value:"10",label:s().translator.trans("core.admin.permissions_controls.allow_ten_minutes_button")},{value:"reply",label:s().translator.trans("core.admin.permissions_controls.allow_until_reply_button")},{value:"0",label:s().translator.trans("core.admin.permissions_controls.allow_never_button")}]}))})),"reply",80)];s().initializers.add("flarum-messages",(()=>{}))})(),module.exports=t})();
|
||||
(()=>{var e={n:t=>{var r=t&&t.__esModule?()=>t.default:()=>t;return e.d(r,{a:r}),r},d:(t,r)=>{for(var a in r)e.o(r,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:r[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t),e.d(t,{extend:()=>h});const r=flarum.reg.get("core","admin/app");var a=e.n(r);const s=flarum.reg.get("core","common/extenders");var n=e.n(s);const l=flarum.reg.get("core","common/Model");var o=e.n(l);const i=flarum.reg.get("core","common/utils/computed");var u=e.n(i);const d=flarum.reg.get("core","common/utils/string");class c extends(o()){content(){return o().attribute("content").call(this)}contentHtml(){return o().attribute("contentHtml").call(this)}renderFailed(){return o().attribute("renderFailed").call(this)}contentPlain(){return u()("contentHtml",(e=>"string"==typeof e?(0,d.getPlainContent)(e):e)).call(this)}createdAt(){return o().attribute("createdAt",o().transformDate).call(this)}dialog(){return o().hasOne("dialog").call(this)}user(){return o().hasOne("user").call(this)}}flarum.reg.add("flarum-messages","common/models/DialogMessage",c);const m=flarum.reg.get("core","common/app");var g=e.n(m);class f extends(o()){title(){return o().attribute("title").call(this)}type(){return o().attribute("type").call(this)}lastMessageAt(){return o().attribute("lastMessageAt",o().transformDate).call(this)}createdAt(){return o().attribute("createdAt",o().transformDate).call(this)}users(){return o().hasMany("users").call(this)}firstMessage(){return o().hasOne("firstMessage").call(this)}lastMessage(){return o().hasOne("lastMessage").call(this)}unreadCount(){return o().attribute("unreadCount").call(this)}lastReadMessageId(){return o().attribute("lastReadMessageId").call(this)}lastReadAt(){return o().attribute("lastReadAt",o().transformDate).call(this)}recipient(){let e=this.users();return e?e.find((e=>e&&e.id()!==g().session.user.id())):null}}flarum.reg.add("flarum-messages","common/models/Dialog",f);const h=[(new(n().Store)).add("dialogs",f).add("dialog-messages",c),(new(n().Admin)).permission((()=>({icon:"fas fa-envelope-open-text",label:a().translator.trans("flarum-messages.admin.permissions.send_messages"),permission:"dialog.sendMessage",allowGuest:!1})),"start",98)];a().initializers.add("flarum-messages",(()=>{}))})(),module.exports=t})();
|
||||
//# sourceMappingURL=admin.js.map
|
2
extensions/messages/js/dist/admin.js.map
generated
vendored
2
extensions/messages/js/dist/admin.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/messages/js/dist/forum.js
generated
vendored
2
extensions/messages/js/dist/forum.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/messages/js/dist/forum.js.map
generated
vendored
2
extensions/messages/js/dist/forum.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/messages/js/dist/forum/components/MessagesPage.js
generated
vendored
2
extensions/messages/js/dist/forum/components/MessagesPage.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/messages/js/dist/forum/components/MessagesPage.js.map
generated
vendored
2
extensions/messages/js/dist/forum/components/MessagesPage.js.map
generated
vendored
File diff suppressed because one or more lines are too long
21
extensions/messages/js/src/@types/shims.d.ts
vendored
Normal file
21
extensions/messages/js/src/@types/shims.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import type Dialog from '../common/models/Dialog';
|
||||
import DialogListState from '../forum/states/DialogListState';
|
||||
|
||||
declare module 'flarum/forum/routes' {
|
||||
export interface ForumRoutes {
|
||||
dialog: (tag: Dialog) => string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'flarum/forum/ForumApplication' {
|
||||
export default interface ForumApplication {
|
||||
dialogs: DialogListState;
|
||||
dropdownDialogs: DialogListState;
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'flarum/forum/states/ComposerState' {
|
||||
export default interface ComposerState {
|
||||
composingMessageTo(dialog: Dialog): boolean;
|
||||
}
|
||||
}
|
18
extensions/messages/js/src/admin/extend.ts
Normal file
18
extensions/messages/js/src/admin/extend.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import Extend from 'flarum/common/extenders';
|
||||
import commonExtend from '../common/extend';
|
||||
import app from 'flarum/admin/app';
|
||||
|
||||
export default [
|
||||
...commonExtend,
|
||||
|
||||
new Extend.Admin().permission(
|
||||
() => ({
|
||||
icon: 'fas fa-envelope-open-text',
|
||||
label: app.translator.trans('flarum-messages.admin.permissions.send_messages'),
|
||||
permission: 'dialog.sendMessage',
|
||||
allowGuest: false,
|
||||
}),
|
||||
'start',
|
||||
98
|
||||
),
|
||||
];
|
@ -1,45 +0,0 @@
|
||||
import Extend from 'flarum/common/extenders';
|
||||
import commonExtend from '../common/extend';
|
||||
import app from 'flarum/admin/app';
|
||||
import SettingDropdown from 'flarum/admin/components/SettingDropdown';
|
||||
|
||||
export default [
|
||||
...commonExtend,
|
||||
|
||||
new Extend.Admin()
|
||||
.permission(
|
||||
() => ({
|
||||
icon: 'fas fa-envelope-open-text',
|
||||
label: app.translator.trans('flarum-messages.admin.permissions.send_messages_label'),
|
||||
permission: 'dialog.sendMessage',
|
||||
allowGuest: false,
|
||||
}),
|
||||
'start',
|
||||
95
|
||||
)
|
||||
.permission(
|
||||
() => ({
|
||||
icon: 'far fa-trash-alt',
|
||||
label: app.translator.trans('flarum-messages.admin.permissions.delete_own_messages_label'),
|
||||
id: 'flarum-messages.allow_delete_own_messages',
|
||||
setting: () => {
|
||||
const minutes = parseInt(app.data.settings['flarum-messages.allow_delete_own_messages'], 10);
|
||||
|
||||
return (
|
||||
<SettingDropdown
|
||||
default={'0'}
|
||||
key="flarum-messages.allow_delete_own_messages"
|
||||
options={[
|
||||
{ value: '-1', label: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button') },
|
||||
{ value: '10', label: app.translator.trans('core.admin.permissions_controls.allow_ten_minutes_button') },
|
||||
{ value: 'reply', label: app.translator.trans('core.admin.permissions_controls.allow_until_reply_button') },
|
||||
{ value: '0', label: app.translator.trans('core.admin.permissions_controls.allow_never_button') },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
},
|
||||
}),
|
||||
'reply',
|
||||
80
|
||||
),
|
||||
];
|
@ -1,14 +1,9 @@
|
||||
import DialogMessage from './models/DialogMessage';
|
||||
import Dialog from './models/Dialog';
|
||||
import Extend from 'flarum/common/extenders';
|
||||
import User from 'flarum/common/models/User';
|
||||
|
||||
export default [
|
||||
new Extend.Store()
|
||||
.add('dialogs', Dialog) //
|
||||
.add('dialog-messages', DialogMessage), //
|
||||
|
||||
new Extend.Model(User) //
|
||||
.attribute<boolean>('canSendAnyMessage')
|
||||
.attribute<boolean>('canDeleteOwnMessage'),
|
||||
];
|
||||
|
@ -5,9 +5,6 @@ import type Dialog from './Dialog';
|
||||
import type User from 'flarum/common/models/User';
|
||||
|
||||
export default class DialogMessage extends Model {
|
||||
number() {
|
||||
return Model.attribute<number>('number').call(this);
|
||||
}
|
||||
content() {
|
||||
return Model.attribute<string | null | undefined>('content').call(this);
|
||||
}
|
||||
@ -36,8 +33,4 @@ export default class DialogMessage extends Model {
|
||||
user() {
|
||||
return Model.hasOne<User>('user').call(this);
|
||||
}
|
||||
|
||||
canDelete() {
|
||||
return Model.attribute<boolean>('canDelete').call(this);
|
||||
}
|
||||
}
|
||||
|
@ -24,27 +24,14 @@ export default class DialogSection<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.messages = new MessageStreamState(this.requestParams());
|
||||
|
||||
this.messages.refresh();
|
||||
}
|
||||
|
||||
requestParams(forgetNear = false): any {
|
||||
const params: any = {
|
||||
this.messages = new MessageStreamState({
|
||||
filter: {
|
||||
dialog: this.attrs.dialog.id(),
|
||||
},
|
||||
sort: '-number',
|
||||
};
|
||||
sort: '-createdAt',
|
||||
});
|
||||
|
||||
const near = m.route.param('near');
|
||||
|
||||
if (near && !forgetNear) {
|
||||
params.page = params.page || {};
|
||||
params.page.near = parseInt(near);
|
||||
}
|
||||
|
||||
return params;
|
||||
this.messages.refresh();
|
||||
}
|
||||
|
||||
view() {
|
||||
@ -55,14 +42,11 @@ export default class DialogSection<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
<div className="DialogSection-header">
|
||||
<Avatar user={recipient} />
|
||||
<div className="DialogSection-header-info">
|
||||
<h2 className="DialogSection-header-info-title">
|
||||
{(recipient && <Link href={app.route.user(recipient!)}>{username(recipient)}</Link>) || username(recipient)}
|
||||
{recipient && recipient.canSendAnyMessage() ? null : (
|
||||
<span className="DialogSection-header-info-helperText">
|
||||
{app.translator.trans('flarum-messages.forum.dialog_section.cannot_reply_text')}
|
||||
</span>
|
||||
)}
|
||||
</h2>
|
||||
{(recipient && (
|
||||
<Link href={app.route.user(recipient!)}>
|
||||
<h2>{username(recipient)}</h2>
|
||||
</Link>
|
||||
)) || <h2>{username(recipient)}</h2>}
|
||||
<div className="badges">{listItems(recipient?.badges().toArray() || [])}</div>
|
||||
</div>
|
||||
<div className="DialogSection-header-actions">{this.actionItems().toArray()}</div>
|
||||
@ -75,13 +59,6 @@ export default class DialogSection<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
actionItems() {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
items.add(
|
||||
'back',
|
||||
<Button className="Button Button--icon DialogSection-back" icon="fas fa-arrow-left" onclick={this.attrs.onback}>
|
||||
{app.translator.trans('flarum-messages.forum.dialog_section.back_label')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
items.add(
|
||||
'details',
|
||||
<Dropdown
|
||||
|
@ -9,12 +9,9 @@ import Comment from 'flarum/forum/components/Comment';
|
||||
import PostUser from 'flarum/forum/components/PostUser';
|
||||
import PostMeta from 'flarum/forum/components/PostMeta';
|
||||
import classList from 'flarum/common/utils/classList';
|
||||
import MessageControls from '../utils/MessageControls';
|
||||
import type MessageStreamState from '../states/MessageStreamState';
|
||||
|
||||
export interface IMessageAttrs extends IAbstractPostAttrs {
|
||||
message: DialogMessage;
|
||||
state: MessageStreamState;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,7 +29,7 @@ export default abstract class Message<CustomAttrs extends IMessageAttrs = IMessa
|
||||
}
|
||||
|
||||
controls(): Mithril.Children[] {
|
||||
return MessageControls.controls(this.attrs.message, this).toArray();
|
||||
return [];
|
||||
}
|
||||
|
||||
freshness(): Date {
|
||||
@ -100,7 +97,7 @@ export default abstract class Message<CustomAttrs extends IMessageAttrs = IMessa
|
||||
}
|
||||
|
||||
avatar(): Mithril.Children {
|
||||
return this.attrs.message.user() ? <Avatar user={this.attrs.message.user()} className="Post-avatar" /> : '';
|
||||
return this.attrs.message.user() ? <Avatar user={this.attrs.message.user()} /> : '';
|
||||
}
|
||||
|
||||
headerItems() {
|
||||
@ -108,21 +105,7 @@ export default abstract class Message<CustomAttrs extends IMessageAttrs = IMessa
|
||||
const message = this.attrs.message;
|
||||
|
||||
items.add('user', <PostUser post={message} />, 100);
|
||||
items.add(
|
||||
'meta',
|
||||
<PostMeta
|
||||
post={message}
|
||||
permalink={() => {
|
||||
const dialog = message.dialog();
|
||||
|
||||
if (!dialog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return app.forum.attribute('baseOrigin') + app.route.dialog(dialog, message.number());
|
||||
}}
|
||||
/>
|
||||
);
|
||||
items.add('meta', <PostMeta post={message} />);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
@ -77,20 +77,18 @@ export default class MessageStream<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
content() {
|
||||
const items: Mithril.Children[] = [];
|
||||
|
||||
const messages = Array.from(new Map(this.attrs.state.getAllItems().map((msg) => [msg.id(), msg])).values()).sort(
|
||||
(a, b) => a.number() - b.number()
|
||||
);
|
||||
const messages = this.attrs.state.getAllItems().sort((a, b) => a.createdAt().getTime() - b.createdAt().getTime());
|
||||
|
||||
const ReplyPlaceholder = this.replyPlaceholderComponent();
|
||||
const LoadingPost = this.loadingPostComponent();
|
||||
|
||||
if (messages[0].id() !== (this.attrs.dialog.data.relationships?.firstMessage.data as ModelIdentifier).id) {
|
||||
items.push(
|
||||
<div className="MessageStream-item" key="loadNext">
|
||||
<div className="MessageStream-item" key="loadPrevious">
|
||||
<Button
|
||||
onclick={() => this.whileMaintainingScroll(() => this.attrs.state.loadNext())}
|
||||
type="button"
|
||||
className="Button Button--block MessageStream-loadNext"
|
||||
className="Button Button--block MessageStream-loadPrev"
|
||||
>
|
||||
{app.translator.trans('flarum-messages.forum.messages_page.stream.load_previous_button')}
|
||||
</Button>
|
||||
@ -99,7 +97,7 @@ export default class MessageStream<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
|
||||
if (LoadingPost) {
|
||||
items.push(
|
||||
<div className="MessageStream-item" key="loading-next">
|
||||
<div className="MessageStream-item" key="loading-prev">
|
||||
<LoadingPost />
|
||||
</div>
|
||||
);
|
||||
@ -108,31 +106,9 @@ export default class MessageStream<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
|
||||
messages.forEach((message, index) => items.push(this.messageItem(message, index)));
|
||||
|
||||
if (messages[messages.length - 1].id() !== (this.attrs.dialog.data.relationships?.lastMessage.data as ModelIdentifier).id) {
|
||||
if (LoadingPost) {
|
||||
items.push(
|
||||
<div className="MessageStream-item" key="loading-prev">
|
||||
<LoadingPost />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (ReplyPlaceholder) {
|
||||
items.push(
|
||||
<div className="MessageStream-item" key="loadPrev">
|
||||
<Button
|
||||
onclick={() => this.whileMaintainingScroll(() => this.attrs.state.loadPrev())}
|
||||
type="button"
|
||||
className="Button Button--block MessageStream-loadPrev"
|
||||
>
|
||||
{app.translator.trans('flarum-messages.forum.messages_page.stream.load_next_button')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (app.session.user!.canSendAnyMessage() && ReplyPlaceholder) {
|
||||
items.push(
|
||||
<div className="MessageStream-item" key="reply">
|
||||
<div className="MessageStream-item" key="reply" /*data-index={this.attrs.state.count()}*/>
|
||||
<ReplyPlaceholder
|
||||
discussion={this.attrs.dialog}
|
||||
onclick={() => {
|
||||
@ -159,9 +135,9 @@ export default class MessageStream<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
|
||||
messageItem(message: DialogMessage, index: number) {
|
||||
return (
|
||||
<div className="MessageStream-item" key={index} data-id={message.id()} data-number={message.number()}>
|
||||
<div className="MessageStream-item" key={index} data-id={message.id()}>
|
||||
{this.timeGap(message)}
|
||||
<Message message={message} state={this.attrs.state} />
|
||||
<Message message={message} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -201,7 +177,7 @@ export default class MessageStream<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
return this.attrs.state.loadNext();
|
||||
}
|
||||
|
||||
if (this.element.scrollTop + this.element.clientHeight >= this.element.scrollHeight && this.attrs.state.hasPrev()) {
|
||||
if (this.element.scrollTop + this.element.clientHeight === this.element.scrollHeight && this.attrs.state.hasPrev()) {
|
||||
return this.attrs.state.loadPrev();
|
||||
}
|
||||
|
||||
@ -210,34 +186,16 @@ export default class MessageStream<CustomAttrs extends IDialogStreamAttrs = IDia
|
||||
}
|
||||
|
||||
scrollToBottom() {
|
||||
const near = m.route.param('near');
|
||||
|
||||
if (near) {
|
||||
const $message = this.element.querySelector(`.MessageStream-item[data-number="${near}"]`);
|
||||
|
||||
if ($message) {
|
||||
this.element.scrollTop = $message.getBoundingClientRect().top - this.element.getBoundingClientRect().top;
|
||||
$message.classList.add('flash');
|
||||
|
||||
// forget near
|
||||
window.history.replaceState(null, '', app.route.dialog(this.attrs.dialog));
|
||||
} else {
|
||||
this.element.scrollTop = this.element.scrollHeight;
|
||||
}
|
||||
} else {
|
||||
this.element.scrollTop = this.element.scrollHeight;
|
||||
}
|
||||
this.element.scrollTop = this.element.scrollHeight;
|
||||
}
|
||||
|
||||
whileMaintainingScroll(callback: () => null | Promise<void>) {
|
||||
const scrollTop = this.element.scrollTop;
|
||||
const scrollHeight = this.element.scrollHeight;
|
||||
|
||||
const closerToBottomThanTop = scrollTop > (scrollHeight - this.element.clientHeight) / 2;
|
||||
|
||||
const result = callback();
|
||||
|
||||
if (result instanceof Promise && !closerToBottomThanTop) {
|
||||
if (result instanceof Promise) {
|
||||
result.then(() => {
|
||||
requestAnimationFrame(() => {
|
||||
this.element.scrollTop = this.element.scrollHeight - scrollHeight + scrollTop;
|
||||
|
@ -14,13 +14,11 @@ import listItems from 'flarum/common/helpers/listItems';
|
||||
import ItemList from 'flarum/common/utils/ItemList';
|
||||
import Dropdown from 'flarum/common/components/Dropdown';
|
||||
import Button from 'flarum/common/components/Button';
|
||||
import classList from 'flarum/common/utils/classList';
|
||||
|
||||
export interface IMessagesPageAttrs extends IPageAttrs {}
|
||||
|
||||
export default class MessagesPage<CustomAttrs extends IMessagesPageAttrs = IMessagesPageAttrs> extends Page<CustomAttrs> {
|
||||
protected selectedDialog = Stream<Dialog | null>(null);
|
||||
protected currentDialogId: string | null = null;
|
||||
|
||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
|
||||
super.oninit(vnode);
|
||||
@ -51,7 +49,6 @@ export default class MessagesPage<CustomAttrs extends IMessagesPageAttrs = IMess
|
||||
|
||||
protected async initDialog() {
|
||||
const dialogId = m.route.param('id');
|
||||
this.currentDialogId = dialogId;
|
||||
|
||||
const title = app.translator.trans('flarum-messages.forum.messages_page.title', {}, true);
|
||||
|
||||
@ -97,12 +94,19 @@ export default class MessagesPage<CustomAttrs extends IMessagesPageAttrs = IMess
|
||||
) : !app.dialogs.hasItems() ? (
|
||||
<InfoTile icon="far fa-envelope-open">{app.translator.trans('flarum-messages.forum.messages_page.empty_text')}</InfoTile>
|
||||
) : (
|
||||
<div
|
||||
className={classList('MessagesPage-content', {
|
||||
'MessagesPage-content--onDialog': this.currentDialogId,
|
||||
})}
|
||||
>
|
||||
{this.contentItems().toArray()}
|
||||
<div className="MessagesPage-content">
|
||||
<div className="MessagesPage-sidebar" key="sidebar">
|
||||
<div className="IndexPage-toolbar" key="toolbar">
|
||||
<ul className="IndexPage-toolbar-view">{listItems(this.viewItems().toArray())}</ul>
|
||||
<ul className="IndexPage-toolbar-action">{listItems(this.actionItems().toArray())}</ul>
|
||||
</div>
|
||||
<DialogList key="list" state={app.dialogs} activeDialog={this.selectedDialog()} />
|
||||
</div>
|
||||
{this.selectedDialog() ? (
|
||||
<DialogSection key="dialog" dialog={this.selectedDialog()} />
|
||||
) : (
|
||||
<LoadingIndicator key="loading" display="block" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</PageStructure>
|
||||
@ -124,40 +128,6 @@ export default class MessagesPage<CustomAttrs extends IMessagesPageAttrs = IMess
|
||||
);
|
||||
}
|
||||
|
||||
contentItems() {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
items.add(
|
||||
'sidebar',
|
||||
<div className="MessagesPage-sidebar" key="sidebar">
|
||||
<div className="IndexPage-toolbar" key="toolbar">
|
||||
<ul className="IndexPage-toolbar-view">{listItems(this.viewItems().toArray())}</ul>
|
||||
<ul className="IndexPage-toolbar-action">{listItems(this.actionItems().toArray())}</ul>
|
||||
</div>
|
||||
<DialogList key="list" state={app.dialogs} activeDialog={this.selectedDialog()} />
|
||||
</div>,
|
||||
100
|
||||
);
|
||||
|
||||
items.add(
|
||||
'dialog',
|
||||
this.selectedDialog() ? (
|
||||
<DialogSection
|
||||
key="dialog"
|
||||
dialog={this.selectedDialog()}
|
||||
onback={() => {
|
||||
this.currentDialogId = null;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LoadingIndicator key="loading" display="block" />
|
||||
),
|
||||
80
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list for the part of the toolbar which is concerned with how
|
||||
* the results are displayed. By default this is just a select box to change
|
||||
@ -198,7 +168,7 @@ export default class MessagesPage<CustomAttrs extends IMessagesPageAttrs = IMess
|
||||
|
||||
/**
|
||||
* Build an item list for the part of the toolbar which is about taking action
|
||||
* on the results. By default, this is just a "mark all as read" button.
|
||||
* on the results. By default this is just a "mark all as read" button.
|
||||
*/
|
||||
actionItems() {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
@ -14,6 +14,8 @@ export default class MessagesSidebar<CustomAttrs extends IMessagesSidebarAttrs =
|
||||
items(): ItemList<Mithril.Children> {
|
||||
const items = super.items();
|
||||
|
||||
const canSendAnyMessage = app.session.user!.attribute<boolean>('canSendAnyMessage');
|
||||
|
||||
items.remove('newDiscussion');
|
||||
|
||||
items.add(
|
||||
@ -25,11 +27,9 @@ export default class MessagesSidebar<CustomAttrs extends IMessagesSidebarAttrs =
|
||||
onclick={() => {
|
||||
return this.newMessageAction();
|
||||
}}
|
||||
disabled={!app.session.user!.canSendAnyMessage()}
|
||||
disabled={!canSendAnyMessage}
|
||||
>
|
||||
{app.session.user!.canSendAnyMessage()
|
||||
? app.translator.trans('flarum-messages.forum.messages_page.send_message_button')
|
||||
: app.translator.trans('flarum-messages.forum.messages_page.cannot_send_message_button')}
|
||||
{app.translator.trans('flarum-messages.forum.messages_page.new_message_button')}
|
||||
</Button>,
|
||||
10
|
||||
);
|
||||
|
@ -9,6 +9,5 @@ export default [
|
||||
new Extend.Routes() //
|
||||
.add('messages', '/messages', () => import('./components/MessagesPage'))
|
||||
.add('dialog', '/messages/dialog/:id', () => import('./components/MessagesPage'))
|
||||
.add('dialog.message', '/messages/dialog/:id/:near', () => import('./components/MessagesPage'))
|
||||
.helper('dialog', (dialog: Dialog, near?: number) => app.route(near ? 'dialog.message' : 'dialog', { id: dialog.id(), near: near })),
|
||||
.helper('dialog', (dialog: Dialog) => app.route('dialog', { id: dialog.id() })),
|
||||
];
|
||||
|
@ -8,7 +8,6 @@ import Button from 'flarum/common/components/Button';
|
||||
import type Dialog from '../common/models/Dialog';
|
||||
import DialogsDropdown from './components/DialogsDropdown';
|
||||
import DialogListState from './states/DialogListState';
|
||||
import type User from 'flarum/common/models/User';
|
||||
|
||||
export { default as extend } from './extend';
|
||||
|
||||
@ -45,14 +44,14 @@ app.initializers.add('flarum-messages', () => {
|
||||
});
|
||||
|
||||
extend(HeaderSecondary.prototype, 'items', function (items) {
|
||||
if (app.session.user?.canSendAnyMessage()) {
|
||||
if (app.session.user?.attribute<boolean>('canSendAnyMessage')) {
|
||||
items.add('messages', <DialogsDropdown state={app.dropdownDialogs} />, 15);
|
||||
}
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
extend(UserControls, 'userControls', (items, user: User) => {
|
||||
if (app.session.user?.canSendAnyMessage()) {
|
||||
extend(UserControls, 'userControls', (items, user) => {
|
||||
if (app.session.user?.attribute<boolean>('canSendAnyMessage')) {
|
||||
items.add(
|
||||
'sendMessage',
|
||||
<Button
|
||||
@ -67,7 +66,6 @@ app.initializers.add('flarum-messages', () => {
|
||||
.then(() => app.composer.show());
|
||||
});
|
||||
}}
|
||||
helperText={user.canSendAnyMessage() ? null : app.translator.trans('flarum-messages.forum.user_controls.cannot_reply_text')}
|
||||
>
|
||||
{app.translator.trans('flarum-messages.forum.user_controls.send_message_button')}
|
||||
</Button>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import PaginatedListState, { PaginatedListParams } from 'flarum/common/states/PaginatedListState';
|
||||
import DialogMessage from '../../common/models/DialogMessage';
|
||||
import { ApiQueryParamsPlural } from 'flarum/common/Store';
|
||||
|
||||
export interface MessageStreamParams extends PaginatedListParams {
|
||||
//
|
||||
|
@ -1,67 +0,0 @@
|
||||
import ItemList from 'flarum/common/utils/ItemList';
|
||||
import Separator from 'flarum/common/components/Separator';
|
||||
import type Mithril from 'mithril';
|
||||
import type DialogMessage from '../../common/models/DialogMessage';
|
||||
import type Message from '../components/Message';
|
||||
import Button from 'flarum/common/components/Button';
|
||||
import app from 'flarum/forum/app';
|
||||
import extractText from 'flarum/common/utils/extractText';
|
||||
|
||||
const MessageControls = {
|
||||
controls(message: DialogMessage, context: Message<any>) {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
Object.entries(this.sections()).forEach(([section, method]) => {
|
||||
const controls = method.call(this, message, context).toArray();
|
||||
|
||||
if (controls.length) {
|
||||
controls.forEach((item) => items.add(item.itemName, item));
|
||||
items.add(section + 'Separator', <Separator />);
|
||||
}
|
||||
});
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
sections() {
|
||||
return {
|
||||
user: this.userControls,
|
||||
moderation: this.moderationControls,
|
||||
destructive: this.destructiveControls,
|
||||
};
|
||||
},
|
||||
|
||||
userControls(message: DialogMessage, context: Message) {
|
||||
return new ItemList<Mithril.Children>();
|
||||
},
|
||||
|
||||
moderationControls(message: DialogMessage, context: Message) {
|
||||
return new ItemList<Mithril.Children>();
|
||||
},
|
||||
|
||||
destructiveControls(message: DialogMessage, context: Message) {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
if (message.canDelete()) {
|
||||
items.add(
|
||||
'delete',
|
||||
<Button icon="far fa-trash-alt" onclick={() => this.deleteAction(message, context)}>
|
||||
{app.translator.trans('flarum-messages.forum.message_controls.delete_button')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
deleteAction(message: DialogMessage, context: Message) {
|
||||
if (!confirm(extractText(app.translator.trans('flarum-messages.forum.message_controls.delete_confirmation')))) return;
|
||||
|
||||
return message.delete().then(() => {
|
||||
context.attrs.state.remove(message);
|
||||
m.redraw();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default MessageControls;
|
@ -1,68 +1,17 @@
|
||||
.MessagesPage {
|
||||
padding-bottom: 0;
|
||||
.MessagesPage-sidebar {
|
||||
flex-shrink: 0;
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.MessagesPage-content {
|
||||
--messages-page-gap: 32px;
|
||||
display: flex;
|
||||
gap: var(--messages-page-gap);
|
||||
gap: 32px;
|
||||
|
||||
.Avatar {
|
||||
--size: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.MessagesPage-sidebar {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
|
||||
.MessagesPage-content--onDialog & {
|
||||
// margin-inline-start: calc(~"0px - 100% - var(--messages-page-gap)");
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media @tablet-up {
|
||||
width: 280px;
|
||||
|
||||
.MessagesPage-content--onDialog & {
|
||||
// margin-inline-start: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.DialogSection {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
|
||||
@media @tablet-up {
|
||||
padding-inline-start: 32px;
|
||||
}
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--control-bg);
|
||||
|
||||
a {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
&-actions {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
&-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.MessageComposer-recipients {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -196,6 +145,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
.DialogSection {
|
||||
flex-grow: 1;
|
||||
padding-inline-start: 32px;
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--control-bg);
|
||||
|
||||
a {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
&-actions {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
&-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Message {
|
||||
padding-right: 0;
|
||||
|
||||
@ -214,41 +191,8 @@
|
||||
}
|
||||
|
||||
.MessageStream, .DialogList {
|
||||
--additional-gap: 52px;
|
||||
max-height: calc(100vh - var(--header-height) - 140px - var(--additional-gap));
|
||||
max-height: calc(100vh - var(--header-height) - 140px - 235px);
|
||||
overflow: auto;
|
||||
|
||||
@media @tablet-up {
|
||||
--additional-gap: 235px;
|
||||
}
|
||||
}
|
||||
|
||||
.MessageStream .ReplyPlaceholder {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.DialogSection-header-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.DialogSection-header-info-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.DialogSection-header-info-helperText {
|
||||
font-size: 0.8rem;
|
||||
font-weight: normal;
|
||||
color: var(--control-color);
|
||||
}
|
||||
|
||||
.DialogSection-back {
|
||||
display: flex;
|
||||
|
||||
@media @tablet-up {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.DialogList-loadMore {
|
||||
|
@ -3,8 +3,7 @@ flarum-messages:
|
||||
# Translations in this namespace are used by the admin interface.
|
||||
admin:
|
||||
permissions:
|
||||
send_messages_label: Send private messages
|
||||
delete_own_messages_label: Delete own messages
|
||||
send_messages: Send private messages
|
||||
|
||||
# Translations in this namespace are used by the forum user interface.
|
||||
forum:
|
||||
@ -22,8 +21,6 @@ flarum-messages:
|
||||
view_all: View all messages
|
||||
|
||||
dialog_section:
|
||||
back_label: Go back
|
||||
cannot_reply_text: This user cannot reply
|
||||
controls:
|
||||
details_button: Details
|
||||
controls_toggle_label: Dialog control actions
|
||||
@ -43,22 +40,17 @@ flarum-messages:
|
||||
newest_button: Newest
|
||||
oldest_button: Oldest
|
||||
|
||||
message_controls:
|
||||
delete_button: Delete
|
||||
delete_confirmation: Are you sure you want to delete this message? This action cannot be undone.
|
||||
|
||||
messages_page:
|
||||
cannot_send_message_button: Can't Send a Message
|
||||
empty_text: No new messages
|
||||
empty_text: You have no messages yet. When you send or receive messages, they
|
||||
will appear here.
|
||||
hero:
|
||||
title: Messages
|
||||
subtitle: Your private conversations with other users
|
||||
mark_all_as_read_tooltip: Mark all as read
|
||||
new_message_button: Send a Message
|
||||
refresh_tooltip: Refresh
|
||||
send_message_button: Send a Message
|
||||
stream:
|
||||
load_previous_button: Load previous messages
|
||||
load_next_button: Load next messages
|
||||
start_of_the_conversation: Start of the conversation
|
||||
time_lapsed_text: => core.forum.post_stream.time_lapsed_text
|
||||
title: Messages
|
||||
@ -71,7 +63,6 @@ flarum-messages:
|
||||
|
||||
user_controls:
|
||||
send_message_button: Send a message
|
||||
cannot_reply_text: This user cannot reply
|
||||
|
||||
notifications:
|
||||
message_received_text: Message Received notification from {user}
|
||||
|
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$schema->table('dialog_messages', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('number')->nullable()->after('content');
|
||||
});
|
||||
|
||||
$numbers = [];
|
||||
|
||||
$schema->getConnection()
|
||||
->table('dialogs')
|
||||
->orderBy('id')
|
||||
->each(function (object $dialog) use ($schema, &$numbers) {
|
||||
$numbers[$dialog->id] = 0;
|
||||
|
||||
$schema->getConnection()
|
||||
->table('dialog_messages')
|
||||
->where('dialog_id', $dialog->id)
|
||||
->orderBy('id')
|
||||
->each(function (object $message) use ($schema, &$numbers) {
|
||||
$schema->getConnection()
|
||||
->table('dialog_messages')
|
||||
->where('id', $message->id)
|
||||
->update(['number' => ++$numbers[$message->dialog_id]]);
|
||||
});
|
||||
|
||||
unset($numbers[$dialog->id]);
|
||||
});
|
||||
|
||||
$schema->table('dialog_messages', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('number')->nullable(false)->change();
|
||||
});
|
||||
},
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('dialog_messages', function (Blueprint $table) {
|
||||
$table->dropColumn('number');
|
||||
});
|
||||
}
|
||||
];
|
@ -9,36 +9,14 @@
|
||||
|
||||
namespace Flarum\Messages\Access;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Messages\DialogMessage;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\Access\AbstractPolicy;
|
||||
use Flarum\User\User;
|
||||
|
||||
class DialogMessagePolicy extends AbstractPolicy
|
||||
{
|
||||
public function __construct(
|
||||
protected SettingsRepositoryInterface $settings
|
||||
) {
|
||||
}
|
||||
|
||||
public function update(User $actor, DialogMessage $message): ?bool
|
||||
public function update(User $actor, DialogMessage $dialogMessage): bool
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function delete(User $actor, DialogMessage $message): bool|null|string
|
||||
{
|
||||
if ($message->user_id === $actor->id) {
|
||||
$allowHiding = $this->settings->get('flarum-messages.allow_delete_own_messages');
|
||||
|
||||
if ($allowHiding === '-1'
|
||||
|| ($allowHiding === 'reply' && $message->number >= $message->dialog->lastMessage->number)
|
||||
|| (is_numeric($allowHiding) && $message->created_at->diffInMinutes(new Carbon, true) < $allowHiding)) {
|
||||
return $this->allow();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Tobyz\JsonApiServer\Context as OriginalContext;
|
||||
use Tobyz\JsonApiServer\Exception\BadRequestException;
|
||||
|
||||
/**
|
||||
* @extends Resource\AbstractDatabaseResource<DialogMessage>
|
||||
@ -78,11 +77,6 @@ class DialogMessageResource extends Resource\AbstractDatabaseResource
|
||||
return $actor->can('sendAnyMessage');
|
||||
}
|
||||
}),
|
||||
Endpoint\Delete::make()
|
||||
->authenticated()
|
||||
->visible(function (DialogMessage $message, Context $context): bool {
|
||||
return $context->getActor()->can('delete', $message);
|
||||
}),
|
||||
Endpoint\Index::make()
|
||||
->authenticated()
|
||||
->defaultInclude([
|
||||
@ -92,7 +86,6 @@ class DialogMessageResource extends Resource\AbstractDatabaseResource
|
||||
'mentionsGroups',
|
||||
'mentionsTags',
|
||||
])
|
||||
->defaultSort('-number')
|
||||
->eagerLoad(function () {
|
||||
if ($this->extensions->isEnabled('flarum-mentions')) {
|
||||
return ['mentionsUsers', 'mentionsPosts', 'mentionsGroups', 'mentionsTags'];
|
||||
@ -100,35 +93,6 @@ class DialogMessageResource extends Resource\AbstractDatabaseResource
|
||||
|
||||
return [];
|
||||
})
|
||||
->extractOffset(function (Context $context, array $defaultExtracts): int {
|
||||
$queryParams = $context->request->getQueryParams();
|
||||
$near = intval(Arr::get($queryParams, 'page.near'));
|
||||
|
||||
if ($near > 1) {
|
||||
$sort = $defaultExtracts['sort'];
|
||||
$filter = $defaultExtracts['filter'];
|
||||
$dialogId = $filter['dialog'] ?? null;
|
||||
|
||||
if (count($filter) > 1 || ! $dialogId || ($sort && $sort !== ['number' => 'desc'])) {
|
||||
throw new BadRequestException(
|
||||
'You can only use page[near] with filter[dialog] and the default sort order'
|
||||
);
|
||||
}
|
||||
|
||||
$limit = $defaultExtracts['limit'];
|
||||
|
||||
$index = DialogMessage::query()
|
||||
->where('dialog_id', $dialogId)
|
||||
->where('number', '>=', $near)
|
||||
->orderBy('number', 'desc')
|
||||
->whereVisibleTo($context->getActor())
|
||||
->count();
|
||||
|
||||
return max(0, $index - $limit / 2);
|
||||
}
|
||||
|
||||
return $defaultExtracts['offset'];
|
||||
})
|
||||
->paginate(),
|
||||
];
|
||||
}
|
||||
@ -137,7 +101,6 @@ class DialogMessageResource extends Resource\AbstractDatabaseResource
|
||||
{
|
||||
return [
|
||||
|
||||
Schema\Number::make('number'),
|
||||
Schema\Str::make('content')
|
||||
->requiredOnCreate()
|
||||
->writableOnCreate()
|
||||
@ -171,12 +134,6 @@ class DialogMessageResource extends Resource\AbstractDatabaseResource
|
||||
->items(1)
|
||||
->set(fn () => null),
|
||||
|
||||
// Read-only.
|
||||
Schema\Boolean::make('canDelete')
|
||||
->get(function (DialogMessage $message, Context $context) {
|
||||
return $context->getActor()->can('delete', $message);
|
||||
}),
|
||||
|
||||
Schema\Relationship\ToOne::make('user')
|
||||
->type('users')
|
||||
->includable(),
|
||||
@ -204,7 +161,7 @@ class DialogMessageResource extends Resource\AbstractDatabaseResource
|
||||
public function sorts(): array
|
||||
{
|
||||
return [
|
||||
SortColumn::make('number'),
|
||||
SortColumn::make('createdAt'),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -21,14 +21,12 @@ use Flarum\Tags\Tag;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $dialog_id
|
||||
* @property int|null $user_id
|
||||
* @property string $content
|
||||
* @property int|Expression $number
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property-read Dialog $dialog
|
||||
@ -50,28 +48,6 @@ class DialogMessage extends AbstractModel implements Formattable
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'dialog_id' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
'number' => 'integer',
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function (self $message) {
|
||||
$db = static::getConnectionResolver()->connection();
|
||||
|
||||
$message->number = new Expression('('.
|
||||
$db->table('dialog_messages', 'dm')
|
||||
->whereRaw($db->getTablePrefix().'dm.dialog_id = '.intval($message->dialog_id))
|
||||
->selectRaw('COALESCE(MAX('.$db->getTablePrefix().'dm.number), 0) + 1')
|
||||
->toSql()
|
||||
.')');
|
||||
});
|
||||
}
|
||||
|
||||
public function dialog(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Dialog::class);
|
||||
|
@ -39,12 +39,12 @@ class ListTest extends TestCase
|
||||
['id' => 104, 'type' => 'direct'],
|
||||
],
|
||||
DialogMessage::class => [
|
||||
['id' => 102, 'dialog_id' => 102, 'user_id' => 3, 'content' => 'Hello, Gale!', 'number' => 1],
|
||||
['id' => 103, 'dialog_id' => 102, 'user_id' => 4, 'content' => 'Hello, Astarion!', 'number' => 2],
|
||||
['id' => 104, 'dialog_id' => 103, 'user_id' => 3, 'content' => 'Hello, Karlach!', 'number' => 1],
|
||||
['id' => 105, 'dialog_id' => 103, 'user_id' => 5, 'content' => 'Hello, Astarion!', 'number' => 2],
|
||||
['id' => 106, 'dialog_id' => 104, 'user_id' => 4, 'content' => 'Hello, Karlach!', 'number' => 1],
|
||||
['id' => 107, 'dialog_id' => 104, 'user_id' => 5, 'content' => 'Hello, Gale!', 'number' => 2],
|
||||
['id' => 102, 'dialog_id' => 102, 'user_id' => 3, 'content' => 'Hello, Gale!'],
|
||||
['id' => 103, 'dialog_id' => 102, 'user_id' => 4, 'content' => 'Hello, Astarion!'],
|
||||
['id' => 104, 'dialog_id' => 103, 'user_id' => 3, 'content' => 'Hello, Karlach!'],
|
||||
['id' => 105, 'dialog_id' => 103, 'user_id' => 5, 'content' => 'Hello, Astarion!'],
|
||||
['id' => 106, 'dialog_id' => 104, 'user_id' => 4, 'content' => 'Hello, Karlach!'],
|
||||
['id' => 107, 'dialog_id' => 104, 'user_id' => 5, 'content' => 'Hello, Gale!'],
|
||||
],
|
||||
'dialog_user' => [
|
||||
['dialog_id' => 102, 'user_id' => 3, 'joined_at' => Carbon::now()],
|
||||
@ -125,49 +125,4 @@ class ListTest extends TestCase
|
||||
'Karlach can see messages in dialogs with Astarion and Gale' => [5, [104, 105, 106, 107]],
|
||||
];
|
||||
}
|
||||
|
||||
public function test_can_list_near_accessible_dialog_messages(): void
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
for ($i = 1; $i <= 40; $i++) {
|
||||
$messages[] = ['id' => 200 + $i, 'dialog_id' => 200, 'user_id' => $i % 2 === 0 ? 3 : 4, 'content' => '<t>Hello, Gale!</t>', 'number' => $i];
|
||||
}
|
||||
|
||||
$this->prepareDatabase([
|
||||
Dialog::class => [
|
||||
['id' => 200, 'type' => 'direct'],
|
||||
],
|
||||
DialogMessage::class => $messages,
|
||||
'dialog_user' => [
|
||||
['dialog_id' => 200, 'user_id' => 3, 'joined_at' => Carbon::now()],
|
||||
['dialog_id' => 200, 'user_id' => 4, 'joined_at' => Carbon::now()],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->database()->table('dialogs')->where('id', '!=', 200)->delete();
|
||||
$this->database()->table('dialog_messages')->where('dialog_id', '!=', 200)->delete();
|
||||
|
||||
$response = $this->send(
|
||||
$this->request('GET', '/api/dialog-messages', [
|
||||
'authenticatedAs' => 3,
|
||||
])->withQueryParams([
|
||||
'include' => 'dialog',
|
||||
'page' => ['near' => 10],
|
||||
'filter' => ['dialog' => 200],
|
||||
]),
|
||||
);
|
||||
|
||||
$json = $response->getBody()->getContents();
|
||||
$prettyJson = json_encode($json, JSON_PRETTY_PRINT);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode(), $prettyJson);
|
||||
$this->assertJson($json);
|
||||
|
||||
$data = json_decode($json, true)['data'];
|
||||
$prettyJson = json_encode(json_decode($json), JSON_PRETTY_PRINT);
|
||||
|
||||
$this->assertEquals(40, $this->database()->table('dialog_messages')->count());
|
||||
$this->assertCount(19, $data, $prettyJson);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class CreateTest extends TestCase
|
||||
['id' => 102, 'type' => 'direct'],
|
||||
],
|
||||
DialogMessage::class => [
|
||||
['id' => 102, 'dialog_id' => 102, 'user_id' => 4, 'content' => 'Hello, Karlach!', 'number' => 1],
|
||||
['id' => 102, 'dialog_id' => 102, 'user_id' => 4, 'content' => 'Hello, Karlach!'],
|
||||
],
|
||||
'dialog_user' => [
|
||||
['dialog_id' => 102, 'user_id' => 4, 'joined_at' => Carbon::now()],
|
||||
|
@ -37,16 +37,16 @@ class UpdateTest extends TestCase
|
||||
['id' => 102, 'type' => 'direct', 'last_message_id' => 111],
|
||||
],
|
||||
DialogMessage::class => [
|
||||
['id' => 102, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>', 'number' => 1],
|
||||
['id' => 103, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>', 'number' => 2],
|
||||
['id' => 104, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>', 'number' => 3],
|
||||
['id' => 105, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>', 'number' => 4],
|
||||
['id' => 106, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>', 'number' => 5],
|
||||
['id' => 107, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>', 'number' => 6],
|
||||
['id' => 108, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>', 'number' => 7],
|
||||
['id' => 109, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>', 'number' => 8],
|
||||
['id' => 110, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>', 'number' => 9],
|
||||
['id' => 111, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>', 'number' => 10],
|
||||
['id' => 102, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
|
||||
['id' => 103, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
|
||||
['id' => 104, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
|
||||
['id' => 105, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
|
||||
['id' => 106, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
|
||||
['id' => 107, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
|
||||
['id' => 108, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
|
||||
['id' => 109, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
|
||||
['id' => 110, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
|
||||
['id' => 111, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
|
||||
],
|
||||
'dialog_user' => [
|
||||
['dialog_id' => 102, 'user_id' => 3, 'last_read_message_id' => 0, 'last_read_at' => null, 'joined_at' => Carbon::now()],
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -22,7 +22,7 @@
|
||||
"source": "https://github.com/flarum/extension-manager"
|
||||
},
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2",
|
||||
"flarum/core": "^2.0.0-beta.1",
|
||||
"composer/composer": "^2.7"
|
||||
},
|
||||
"require-dev": {
|
||||
|
2
extensions/package-manager/js/dist-typings/models/ExternalExtension.d.ts
generated
vendored
2
extensions/package-manager/js/dist-typings/models/ExternalExtension.d.ts
generated
vendored
@ -19,8 +19,8 @@ export default class ExternalExtension extends Model {
|
||||
locale: () => string;
|
||||
latestFlarumVersionSupported: () => string;
|
||||
downloads: () => number;
|
||||
isSupported: () => boolean;
|
||||
readonly installed = false;
|
||||
isSupported(): boolean;
|
||||
isProductionReady(): boolean;
|
||||
toLocalExtension(): Extension;
|
||||
}
|
||||
|
2
extensions/package-manager/js/dist/admin.js
generated
vendored
2
extensions/package-manager/js/dist/admin.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/package-manager/js/dist/admin.js.map
generated
vendored
2
extensions/package-manager/js/dist/admin.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@ -22,9 +22,21 @@ export default class ExternalExtension extends Model {
|
||||
locale = Model.attribute<string>('locale');
|
||||
latestFlarumVersionSupported = Model.attribute<string>('latestFlarumVersionSupported');
|
||||
downloads = Model.attribute<number>('downloads');
|
||||
isSupported = Model.attribute<boolean>('isSupported');
|
||||
readonly installed = false;
|
||||
|
||||
public isSupported(): boolean {
|
||||
const currentVersion = app.data.settings.version;
|
||||
const latestCompatibleVersion = this.latestFlarumVersionSupported();
|
||||
|
||||
// If stability is not the same, it's not compatible.
|
||||
if (currentVersion.split('-')[1] !== latestCompatibleVersion.split('-')[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Minor versions are compatible.
|
||||
return currentVersion.split('.')[0] === latestCompatibleVersion.split('.')[0];
|
||||
}
|
||||
|
||||
public isProductionReady(): boolean {
|
||||
return isProductionReady(this.highestVersion());
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
namespace Flarum\ExtensionManager\Api\Resource;
|
||||
|
||||
use Composer\Semver\Semver;
|
||||
use Flarum\Api\Endpoint;
|
||||
use Flarum\Api\Resource\AbstractResource;
|
||||
use Flarum\Api\Resource\Contracts\Countable;
|
||||
@ -32,7 +31,7 @@ use Tobyz\JsonApiServer\Schema\CustomFilter;
|
||||
|
||||
class ExternalExtensionResource extends AbstractResource implements Listable, Paginatable, Countable
|
||||
{
|
||||
protected ?int $totalResults = null;
|
||||
protected int|null $totalResults = null;
|
||||
|
||||
public function __construct(
|
||||
protected Repository $cache,
|
||||
@ -82,11 +81,6 @@ class ExternalExtensionResource extends AbstractResource implements Listable, Pa
|
||||
Schema\Boolean::make('compatibleWithLatestFlarum')
|
||||
->property('compatible_with_latest_flarum'),
|
||||
Schema\Integer::make('downloads'),
|
||||
|
||||
Schema\Boolean::make('isSupported')
|
||||
->get(function (Extension $extension) {
|
||||
return Semver::satisfies(Application::VERSION, $extension->latest_flarum_version_supported);
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
namespace Flarum\ExtensionManager\Command;
|
||||
|
||||
use Composer\Semver\Semver;
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\ExtensionManager\Composer\ComposerAdapter;
|
||||
@ -17,21 +16,16 @@ use Flarum\ExtensionManager\Composer\ComposerJson;
|
||||
use Flarum\ExtensionManager\Exception\ComposerCommandFailedException;
|
||||
use Flarum\ExtensionManager\Settings\LastUpdateCheck;
|
||||
use Flarum\ExtensionManager\Support\Util;
|
||||
use Flarum\Foundation\Application;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
|
||||
class CheckForUpdatesHandler
|
||||
{
|
||||
protected array $meta = [];
|
||||
|
||||
public function __construct(
|
||||
protected ComposerAdapter $composer,
|
||||
protected LastUpdateCheck $lastUpdateCheck,
|
||||
protected ExtensionManager $extensions,
|
||||
protected ComposerJson $composerJson,
|
||||
protected Client $http
|
||||
protected ComposerJson $composerJson
|
||||
) {
|
||||
}
|
||||
|
||||
@ -103,10 +97,6 @@ class CheckForUpdatesHandler
|
||||
|
||||
$mainPackageUpdate['required-as'] = $composerJson['require'][$mainPackageUpdate['name']] ?? null;
|
||||
|
||||
if (! $this->compatibleWithCurrentFlarumVersion($mainPackageUpdate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$updates->push($mainPackageUpdate);
|
||||
}
|
||||
|
||||
@ -146,49 +136,4 @@ class CheckForUpdatesHandler
|
||||
|
||||
return $output->getContents();
|
||||
}
|
||||
|
||||
private function compatibleWithCurrentFlarumVersion(array $mainPackageUpdate): bool
|
||||
{
|
||||
if (empty($mainPackageUpdate['latest-major']) || str_contains($mainPackageUpdate['latest-major'], 'dev-')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! empty($this->meta[$mainPackageUpdate['name']])) {
|
||||
$json = $this->meta[$mainPackageUpdate['name']];
|
||||
} else {
|
||||
$response = $this->http->get("https://repo.packagist.org/p2/{$mainPackageUpdate['name']}.json");
|
||||
|
||||
$body = $response->getBody()->getContents();
|
||||
|
||||
if ($response->getStatusCode() > 299 || $response->getStatusCode() < 200) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$json = json_decode($body, true);
|
||||
|
||||
$this->meta[$mainPackageUpdate['name']] = $json;
|
||||
}
|
||||
|
||||
$packages = new Collection($json['packages'][$mainPackageUpdate['name']] ?? []);
|
||||
|
||||
if ($packages->isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$package = $packages->firstWhere('version', $mainPackageUpdate['latest-major']);
|
||||
|
||||
if (! $package) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$flarumVersion = Application::VERSION;
|
||||
|
||||
$require = $package['require']['flarum/core'] ?? null;
|
||||
|
||||
if (! $require || str_contains($require, 'dev-')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Semver::satisfies($flarumVersion, $require);
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,6 @@ use Throwable;
|
||||
|
||||
class ComposerCommandJob extends AbstractJob implements ShouldBeUnique
|
||||
{
|
||||
/**
|
||||
* The number of seconds the job can run before timing out.
|
||||
*/
|
||||
public int $timeout = 60 * 3;
|
||||
|
||||
public function __construct(
|
||||
protected AbstractActionCommand $command,
|
||||
protected string $phpVersion
|
||||
|
@ -21,9 +21,6 @@ class Util
|
||||
if (str_starts_with($currentVersion, 'v')) {
|
||||
$currentVersion = substr($currentVersion, 1);
|
||||
}
|
||||
if (str_starts_with($latestVersion, 'v')) {
|
||||
$latestVersion = substr($latestVersion, 1);
|
||||
}
|
||||
|
||||
$currentVersion = explode('.', $currentVersion);
|
||||
$latestVersion = explode('.', $latestVersion);
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2",
|
||||
"flarum/core": "^2.0.0-beta.1",
|
||||
"pusher/pusher-php-server": "^7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
2
extensions/subscriptions/js/dist/forum.js
generated
vendored
2
extensions/subscriptions/js/dist/forum.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/subscriptions/js/dist/forum.js.map
generated
vendored
2
extensions/subscriptions/js/dist/forum.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@ -51,7 +51,7 @@ export default class SubscriptionMenu<CustomAttrs extends ISubscriptionMenuAttrs
|
||||
const discussion = this.attrs.discussion;
|
||||
const subscription = discussion.subscription();
|
||||
|
||||
const buttonAttrs = this.possibleButtonAttrs[subscription ?? 'null'] ?? this.possibleButtonAttrs.null;
|
||||
const buttonAttrs = this.possibleButtonAttrs[subscription];
|
||||
|
||||
const preferences = app.session.user!.preferences()!;
|
||||
const notifyEmail = preferences['notify_newPost_email'];
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -26,7 +26,7 @@ class UserResourceFields
|
||||
Schema\Str::make('suspendMessage')
|
||||
->writable($canSuspend)
|
||||
->visible(fn (User $user, Context $context) => $context->getActor()->id === $user->id || $canSuspend($user, $context)),
|
||||
Schema\DateTime::make('suspendedUntil')
|
||||
Schema\Date::make('suspendedUntil')
|
||||
->writable($canSuspend)
|
||||
->visible(fn (User $user, Context $context) => $context->getActor()->id === $user->id || $canSuspend($user, $context))
|
||||
->nullable(),
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"flarum/core": "^2.0.0-beta.2"
|
||||
"flarum/core": "^2.0.0-beta.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
10
extensions/tags/js/dist-typings/forum/components/TagHero.d.ts
generated
vendored
10
extensions/tags/js/dist-typings/forum/components/TagHero.d.ts
generated
vendored
@ -1,15 +1,5 @@
|
||||
export default class TagHero extends Component<import("flarum/common/Component").ComponentAttrs, undefined> {
|
||||
constructor();
|
||||
view(): JSX.Element;
|
||||
/**
|
||||
* @returns {ItemList<Mithril.Children>}
|
||||
*/
|
||||
viewItems(): ItemList<Mithril.Children>;
|
||||
/**
|
||||
* @returns {ItemList<Mithril.Children>}
|
||||
*/
|
||||
contentItems(): ItemList<Mithril.Children>;
|
||||
}
|
||||
import Component from "flarum/common/Component";
|
||||
import ItemList from "flarum/common/utils/ItemList";
|
||||
import Mithril from "mithril";
|
||||
|
2
extensions/tags/js/dist/admin.js
generated
vendored
2
extensions/tags/js/dist/admin.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/tags/js/dist/admin.js.map
generated
vendored
2
extensions/tags/js/dist/admin.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/tags/js/dist/common/components/TagSelectionModal.js
generated
vendored
2
extensions/tags/js/dist/common/components/TagSelectionModal.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/tags/js/dist/common/components/TagSelectionModal.js.map
generated
vendored
2
extensions/tags/js/dist/common/components/TagSelectionModal.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/tags/js/dist/forum.js
generated
vendored
2
extensions/tags/js/dist/forum.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/tags/js/dist/forum.js.map
generated
vendored
2
extensions/tags/js/dist/forum.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/tags/js/dist/forum/components/TagDiscussionModal.js
generated
vendored
2
extensions/tags/js/dist/forum/components/TagDiscussionModal.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/tags/js/dist/forum/components/TagDiscussionModal.js.map
generated
vendored
2
extensions/tags/js/dist/forum/components/TagDiscussionModal.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@ -78,14 +78,14 @@ export default function () {
|
||||
'tag',
|
||||
<Dropdown
|
||||
className="Dropdown--restrictByTag"
|
||||
buttonClassName="Button Button--link"
|
||||
buttonClassName="Button Button--text"
|
||||
label={app.translator.trans('flarum-tags.admin.permissions.restrict_by_tag_heading')}
|
||||
icon="fas fa-plus"
|
||||
caretIcon={null}
|
||||
>
|
||||
{tags.map((tag) => (
|
||||
<Button icon={tagIcon(tag, { className: 'Button-icon' })} onclick={() => tag.save({ isRestricted: true })}>
|
||||
{tag.name()}
|
||||
<Button icon={true} onclick={() => tag.save({ isRestricted: true })}>
|
||||
{[tagIcon(tag, { className: 'Button-icon' }), ' ', tag.name()]}
|
||||
</Button>
|
||||
))}
|
||||
</Dropdown>
|
||||
|
@ -19,12 +19,7 @@ function tagItem(tag) {
|
||||
<div className="TagListItem-info">
|
||||
{tagIcon(tag)}
|
||||
<span className="TagListItem-name">{tag.name()}</span>
|
||||
<Button
|
||||
className="Button Button--link"
|
||||
icon="fas fa-pencil-alt"
|
||||
aria-label={app.translator.trans('flarum-tags.admin.tags.edit_tag_label', { tag: tag.name() })}
|
||||
onclick={() => app.modal.show(EditTagModal, { model: tag })}
|
||||
/>
|
||||
<Button className="Button Button--link" icon="fas fa-pencil-alt" onclick={() => app.modal.show(EditTagModal, { model: tag })} />
|
||||
</div>
|
||||
{!tag.isChild() && tag.position() !== null && (
|
||||
<ol className="TagListItem-children TagList">
|
||||
|
@ -2,8 +2,6 @@ import Component from 'flarum/common/Component';
|
||||
import textContrastClass from 'flarum/common/helpers/textContrastClass';
|
||||
import tagIcon from '../../common/helpers/tagIcon';
|
||||
import classList from 'flarum/common/utils/classList';
|
||||
import ItemList from 'flarum/common/utils/ItemList';
|
||||
import Mithril from 'mithril';
|
||||
|
||||
export default class TagHero extends Component {
|
||||
view() {
|
||||
@ -15,39 +13,15 @@ export default class TagHero extends Component {
|
||||
className={classList('Hero', 'TagHero', { 'TagHero--colored': color, [textContrastClass(color)]: color })}
|
||||
style={color ? { '--hero-bg': color } : undefined}
|
||||
>
|
||||
<div className="container">{this.viewItems().toArray()}</div>
|
||||
<div className="container">
|
||||
<div className="containerNarrow">
|
||||
<h1 className="Hero-title">
|
||||
{tag.icon() && tagIcon(tag, {}, { useColor: false })} {tag.name()}
|
||||
</h1>
|
||||
<div className="Hero-subtitle">{tag.description()}</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {ItemList<Mithril.Children>}
|
||||
*/
|
||||
viewItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('content', <div className="containerNarrow">{this.contentItems().toArray()}</div>, 80);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {ItemList<Mithril.Children>}
|
||||
*/
|
||||
contentItems() {
|
||||
const items = new ItemList();
|
||||
const tag = this.attrs.model;
|
||||
|
||||
items.add(
|
||||
'tag-title',
|
||||
<h1 className="Hero-title">
|
||||
{tag.icon() && tagIcon(tag, {}, { useColor: false })} {tag.name()}
|
||||
</h1>,
|
||||
100
|
||||
);
|
||||
|
||||
items.add('tag-subtitle', <div className="Hero-subtitle">{tag.description()}</div>, 90);
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: -3px;
|
||||
margin-left: 1px;
|
||||
background: var(--color, var(--control-bg));
|
||||
|
||||
&.untagged {
|
||||
|
@ -57,8 +57,6 @@
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@ -78,8 +76,6 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.pinned:not(.child) {
|
||||
padding-top: 10px;
|
||||
@ -103,9 +99,16 @@
|
||||
background: var(--control-bg);
|
||||
}
|
||||
|
||||
.icon::before {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
.SelectTagListItem-checkIcon {
|
||||
display: inline-flex;
|
||||
display: inline-block;
|
||||
color: var(--muted-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
@ -120,12 +123,10 @@
|
||||
}
|
||||
.SelectTagListItem-icon {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-top: 3px;
|
||||
margin-left: 0;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
.SelectTagListItem-name {
|
||||
display: inline-block;
|
||||
|
@ -44,7 +44,7 @@
|
||||
opacity: 1;
|
||||
}
|
||||
@media @tablet-up {
|
||||
.IndexPage, .UserPage, .DiscussionList--floatingTags {
|
||||
.IndexPage, .UserPage {
|
||||
.DiscussionListItem-title {
|
||||
margin-right: 155px;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user