mirror of
https://github.com/flarum/framework.git
synced 2025-03-26 00:35:16 +08:00
chore: enable and set up prettier for flarum/tags
(#3496)
This commit is contained in:
parent
4923253fbf
commit
824fb2feff
.github/workflows
extensions/tags/js
package.json
yarn.locksrc
@types
admin
addTagChangePermission.jsaddTagPermission.jsaddTagsHomePageOption.jsaddTagsPermissionScope.tsx
components
index.tscommon
forum
2
.github/workflows/flarum-tags-frontend.yml
vendored
2
.github/workflows/flarum-tags-frontend.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
||||
uses: ./.github/workflows/REUSABLE_frontend.yml
|
||||
with:
|
||||
enable_bundlewatch: false
|
||||
enable_prettier: false
|
||||
enable_prettier: true
|
||||
enable_typescript: true
|
||||
|
||||
frontend_directory: ./extensions/tags/js
|
||||
|
@ -2,6 +2,7 @@
|
||||
"private": true,
|
||||
"name": "@flarum/tags",
|
||||
"version": "0.0.0",
|
||||
"prettier": "@flarum/prettier-config",
|
||||
"dependencies": {
|
||||
"sortablejs": "^1.14.0"
|
||||
},
|
||||
@ -13,14 +14,17 @@
|
||||
"build-typings": "yarn run clean-typings && ([ -e src/@types ] && cp -r src/@types dist-typings/@types || true) && tsc && yarn run post-build-typings",
|
||||
"post-build-typings": "find dist-typings -type f -name '*.d.ts' -print0 | xargs -0 sed -i 's,../src/@types,@types,g'",
|
||||
"check-typings": "tsc --noEmit --emitDeclarationOnly false",
|
||||
"check-typings-coverage": "typescript-coverage-report"
|
||||
"check-typings-coverage": "typescript-coverage-report",
|
||||
"format": "prettier --write src",
|
||||
"format-check": "prettier --check src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"flarum-webpack-config": "^2.0.0",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"flarum-tsconfig": "^1.0.2",
|
||||
"flarum-webpack-config": "^2.0.0",
|
||||
"prettier": "^2.7.1",
|
||||
"typescript": "^4.5.4",
|
||||
"typescript-coverage-report": "^0.6.1"
|
||||
"typescript-coverage-report": "^0.6.1",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.1"
|
||||
}
|
||||
}
|
||||
|
4
extensions/tags/js/src/@types/shims.d.ts
vendored
4
extensions/tags/js/src/@types/shims.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
import type Tag from "../common/models/Tag";
|
||||
import type TagListState from "../forum/states/TagListState";
|
||||
import type Tag from '../common/models/Tag';
|
||||
import type TagListState from '../forum/states/TagListState';
|
||||
|
||||
declare module 'flarum/forum/routes' {
|
||||
export interface ForumRoutes {
|
||||
|
@ -2,26 +2,30 @@ import { extend } from 'flarum/common/extend';
|
||||
import PermissionGrid from 'flarum/admin/components/PermissionGrid';
|
||||
import SettingDropdown from 'flarum/admin/components/SettingDropdown';
|
||||
|
||||
export default function() {
|
||||
extend(PermissionGrid.prototype, 'startItems', items => {
|
||||
items.add('allowTagChange', {
|
||||
icon: 'fas fa-tag',
|
||||
label: app.translator.trans('flarum-tags.admin.permissions.allow_edit_tags_label'),
|
||||
setting: () => {
|
||||
const minutes = parseInt(app.data.settings.allow_tag_change, 10);
|
||||
export default function () {
|
||||
extend(PermissionGrid.prototype, 'startItems', (items) => {
|
||||
items.add(
|
||||
'allowTagChange',
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
label: app.translator.trans('flarum-tags.admin.permissions.allow_edit_tags_label'),
|
||||
setting: () => {
|
||||
const minutes = parseInt(app.data.settings.allow_tag_change, 10);
|
||||
|
||||
return SettingDropdown.component({
|
||||
defaultLabel: minutes
|
||||
? app.translator.trans('core.admin.permissions_controls.allow_some_minutes_button', {count: minutes})
|
||||
: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'),
|
||||
key: 'allow_tag_change',
|
||||
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')}
|
||||
]
|
||||
});
|
||||
}
|
||||
}, 90);
|
||||
return SettingDropdown.component({
|
||||
defaultLabel: minutes
|
||||
? app.translator.trans('core.admin.permissions_controls.allow_some_minutes_button', { count: minutes })
|
||||
: app.translator.trans('core.admin.permissions_controls.allow_indefinitely_button'),
|
||||
key: 'allow_tag_change',
|
||||
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') },
|
||||
],
|
||||
});
|
||||
},
|
||||
},
|
||||
90
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
export default function () {
|
||||
app.extensionData
|
||||
.for('flarum-tags')
|
||||
.registerPermission({
|
||||
icon: 'fas fa-tag',
|
||||
label: app.translator.trans('flarum-tags.admin.permissions.tag_discussions_label'),
|
||||
permission: 'discussion.tag',
|
||||
}, 'moderate', 95)
|
||||
.registerPermission({
|
||||
icon: 'fas fa-tags',
|
||||
label: app.translator.trans('flarum-tags.admin.permissions.bypass_tag_counts_label'),
|
||||
permission: 'bypassTagCounts',
|
||||
}, 'start', 89);
|
||||
.registerPermission(
|
||||
{
|
||||
icon: 'fas fa-tag',
|
||||
label: app.translator.trans('flarum-tags.admin.permissions.tag_discussions_label'),
|
||||
permission: 'discussion.tag',
|
||||
},
|
||||
'moderate',
|
||||
95
|
||||
)
|
||||
.registerPermission(
|
||||
{
|
||||
icon: 'fas fa-tags',
|
||||
label: app.translator.trans('flarum-tags.admin.permissions.bypass_tag_counts_label'),
|
||||
permission: 'bypassTagCounts',
|
||||
},
|
||||
'start',
|
||||
89
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { extend } from 'flarum/common/extend';
|
||||
import BasicsPage from 'flarum/admin/components/BasicsPage';
|
||||
|
||||
export default function() {
|
||||
extend(BasicsPage.prototype, 'homePageItems', items => {
|
||||
export default function () {
|
||||
extend(BasicsPage.prototype, 'homePageItems', (items) => {
|
||||
items.add('tags', {
|
||||
path: '/tags',
|
||||
label: app.translator.trans('flarum-tags.admin.basics.tags_label')
|
||||
label: app.translator.trans('flarum-tags.admin.basics.tags_label'),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ import tagIcon from '../common/helpers/tagIcon';
|
||||
import sortTags from '../common/utils/sortTags';
|
||||
import Tag from '../common/models/Tag';
|
||||
|
||||
export default function() {
|
||||
export default function () {
|
||||
extend(PermissionGrid.prototype, 'oninit', function () {
|
||||
this.loading = true;
|
||||
})
|
||||
});
|
||||
|
||||
extend(PermissionGrid.prototype, 'oncreate', function () {
|
||||
app.store.find<Tag[]>('tags', {}).then(() => {
|
||||
@ -30,7 +30,7 @@ export default function() {
|
||||
}
|
||||
|
||||
return original(vnode);
|
||||
})
|
||||
});
|
||||
|
||||
override(app, 'getRequiredPermissions', (original, permission) => {
|
||||
const tagPrefix = permission.match(/^tag\d+\./);
|
||||
@ -40,45 +40,60 @@ export default function() {
|
||||
|
||||
const required = original(globalPermission);
|
||||
|
||||
return required.map(required => tagPrefix[0] + required);
|
||||
return required.map((required) => tagPrefix[0] + required);
|
||||
}
|
||||
|
||||
return original(permission);
|
||||
});
|
||||
|
||||
extend(PermissionGrid.prototype, 'scopeItems', items => {
|
||||
extend(PermissionGrid.prototype, 'scopeItems', (items) => {
|
||||
sortTags(app.store.all('tags'))
|
||||
.filter(tag => tag.isRestricted())
|
||||
.forEach(tag => items.add('tag' + tag.id(), {
|
||||
label: tagLabel(tag),
|
||||
onremove: () => tag.save({isRestricted: false}),
|
||||
render: item => {
|
||||
if ('setting' in item) return '';
|
||||
.filter((tag) => tag.isRestricted())
|
||||
.forEach((tag) =>
|
||||
items.add('tag' + tag.id(), {
|
||||
label: tagLabel(tag),
|
||||
onremove: () => tag.save({ isRestricted: false }),
|
||||
render: (item) => {
|
||||
if ('setting' in item) return '';
|
||||
|
||||
if (item.permission === 'viewForum'
|
||||
|| item.permission === 'startDiscussion'
|
||||
|| (item.permission && item.permission.indexOf('discussion.') === 0 && item.tagScoped !== false)
|
||||
|| item.tagScoped) {
|
||||
return PermissionDropdown.component({
|
||||
permission: 'tag' + tag.id() + '.' + item.permission,
|
||||
allowGuest: item.allowGuest
|
||||
});
|
||||
}
|
||||
if (
|
||||
item.permission === 'viewForum' ||
|
||||
item.permission === 'startDiscussion' ||
|
||||
(item.permission && item.permission.indexOf('discussion.') === 0 && item.tagScoped !== false) ||
|
||||
item.tagScoped
|
||||
) {
|
||||
return PermissionDropdown.component({
|
||||
permission: 'tag' + tag.id() + '.' + item.permission,
|
||||
allowGuest: item.allowGuest,
|
||||
});
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}));
|
||||
return '';
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
extend(PermissionGrid.prototype, 'scopeControlItems', items => {
|
||||
const tags = sortTags(app.store.all<Tag>('tags').filter(tag => !tag.isRestricted()));
|
||||
extend(PermissionGrid.prototype, 'scopeControlItems', (items) => {
|
||||
const tags = sortTags(app.store.all<Tag>('tags').filter((tag) => !tag.isRestricted()));
|
||||
|
||||
if (tags.length) {
|
||||
items.add('tag', <Dropdown className='Dropdown--restrictByTag' 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={true} onclick={() => tag.save({ isRestricted: true })}>
|
||||
{[tagIcon(tag, { className: 'Button-icon' }), ' ', tag.name()]}
|
||||
</Button>)}
|
||||
</Dropdown>);
|
||||
items.add(
|
||||
'tag',
|
||||
<Dropdown
|
||||
className="Dropdown--restrictByTag"
|
||||
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={true} onclick={() => tag.save({ isRestricted: true })}>
|
||||
{[tagIcon(tag, { className: 'Button-icon' }), ' ', tag.name()]}
|
||||
</Button>
|
||||
))}
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -59,9 +59,7 @@ export default class EditTagModal extends Modal<EditTagModalAttrs> {
|
||||
content() {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<div className="Form">
|
||||
{this.fields().toArray()}
|
||||
</div>
|
||||
<div className="Form">{this.fields().toArray()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -69,59 +67,97 @@ export default class EditTagModal extends Modal<EditTagModalAttrs> {
|
||||
fields() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('name', <div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.name_label')}</label>
|
||||
<input className="FormControl" placeholder={app.translator.trans('flarum-tags.admin.edit_tag.name_placeholder')} value={this.name()} oninput={(e: InputEvent) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
this.name(target.value);
|
||||
this.slug(slug(target.value));
|
||||
}} />
|
||||
</div>, 50);
|
||||
items.add(
|
||||
'name',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.name_label')}</label>
|
||||
<input
|
||||
className="FormControl"
|
||||
placeholder={app.translator.trans('flarum-tags.admin.edit_tag.name_placeholder')}
|
||||
value={this.name()}
|
||||
oninput={(e: InputEvent) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
this.name(target.value);
|
||||
this.slug(slug(target.value));
|
||||
}}
|
||||
/>
|
||||
</div>,
|
||||
50
|
||||
);
|
||||
|
||||
items.add('slug', <div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.slug_label')}</label>
|
||||
<input className="FormControl" bidi={this.slug} />
|
||||
</div>, 40);
|
||||
items.add(
|
||||
'slug',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.slug_label')}</label>
|
||||
<input className="FormControl" bidi={this.slug} />
|
||||
</div>,
|
||||
40
|
||||
);
|
||||
|
||||
items.add('description', <div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.description_label')}</label>
|
||||
<textarea className="FormControl" bidi={this.description} />
|
||||
</div>, 30);
|
||||
items.add(
|
||||
'description',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.description_label')}</label>
|
||||
<textarea className="FormControl" bidi={this.description} />
|
||||
</div>,
|
||||
30
|
||||
);
|
||||
|
||||
items.add('color', <div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.color_label')}</label>
|
||||
<ColorPreviewInput className="FormControl" placeholder="#aaaaaa" bidi={this.color} />
|
||||
</div>, 20);
|
||||
items.add(
|
||||
'color',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.color_label')}</label>
|
||||
<ColorPreviewInput className="FormControl" placeholder="#aaaaaa" bidi={this.color} />
|
||||
</div>,
|
||||
20
|
||||
);
|
||||
|
||||
items.add('icon', <div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.icon_label')}</label>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('flarum-tags.admin.edit_tag.icon_text', { a: <a href="https://fontawesome.com/icons?m=free" tabindex="-1" /> })}
|
||||
</div>
|
||||
<input className="FormControl" placeholder="fas fa-bolt" bidi={this.icon} />
|
||||
</div>, 10);
|
||||
items.add(
|
||||
'icon',
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.edit_tag.icon_label')}</label>
|
||||
<div className="helpText">
|
||||
{app.translator.trans('flarum-tags.admin.edit_tag.icon_text', { a: <a href="https://fontawesome.com/icons?m=free" tabindex="-1" /> })}
|
||||
</div>
|
||||
<input className="FormControl" placeholder="fas fa-bolt" bidi={this.icon} />
|
||||
</div>,
|
||||
10
|
||||
);
|
||||
|
||||
items.add('hidden', <div className="Form-group">
|
||||
<div>
|
||||
<label className="checkbox">
|
||||
<input type="checkbox" bidi={this.isHidden} />
|
||||
{app.translator.trans('flarum-tags.admin.edit_tag.hide_label')}
|
||||
</label>
|
||||
</div>
|
||||
</div>, 10);
|
||||
items.add(
|
||||
'hidden',
|
||||
<div className="Form-group">
|
||||
<div>
|
||||
<label className="checkbox">
|
||||
<input type="checkbox" bidi={this.isHidden} />
|
||||
{app.translator.trans('flarum-tags.admin.edit_tag.hide_label')}
|
||||
</label>
|
||||
</div>
|
||||
</div>,
|
||||
10
|
||||
);
|
||||
|
||||
items.add('submit', <div className="Form-group">
|
||||
{Button.component({
|
||||
type: 'submit',
|
||||
className: 'Button Button--primary EditTagModal-save',
|
||||
loading: this.loading,
|
||||
}, app.translator.trans('flarum-tags.admin.edit_tag.submit_button'))}
|
||||
{this.tag.exists ? (
|
||||
<button type="button" className="Button EditTagModal-delete" onclick={this.delete.bind(this)}>
|
||||
{app.translator.trans('flarum-tags.admin.edit_tag.delete_tag_button')}
|
||||
</button>
|
||||
) : ''}
|
||||
</div>, -10);
|
||||
items.add(
|
||||
'submit',
|
||||
<div className="Form-group">
|
||||
{Button.component(
|
||||
{
|
||||
type: 'submit',
|
||||
className: 'Button Button--primary EditTagModal-save',
|
||||
loading: this.loading,
|
||||
},
|
||||
app.translator.trans('flarum-tags.admin.edit_tag.submit_button')
|
||||
)}
|
||||
{this.tag.exists ? (
|
||||
<button type="button" className="Button EditTagModal-delete" onclick={this.delete.bind(this)}>
|
||||
{app.translator.trans('flarum-tags.admin.edit_tag.delete_tag_button')}
|
||||
</button>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>,
|
||||
-10
|
||||
);
|
||||
|
||||
return items;
|
||||
}
|
||||
@ -147,20 +183,22 @@ export default class EditTagModal extends Modal<EditTagModalAttrs> {
|
||||
// This is done for better error visibility on smaller screen heights.
|
||||
this.tag.save(this.submitData()).then(
|
||||
() => this.hide(),
|
||||
() => this.loading = false
|
||||
() => (this.loading = false)
|
||||
);
|
||||
}
|
||||
|
||||
delete() {
|
||||
if (confirm(extractText(app.translator.trans('flarum-tags.admin.edit_tag.delete_tag_confirmation')))) {
|
||||
const children = app.store.all<Tag>('tags').filter(tag => tag.parent() === this.tag);
|
||||
const children = app.store.all<Tag>('tags').filter((tag) => tag.parent() === this.tag);
|
||||
|
||||
this.tag.delete().then(() => {
|
||||
children.forEach(tag => tag.pushData({
|
||||
attributes: { isChild: false },
|
||||
// @deprecated. Temporary hack for type safety, remove before v1.3.
|
||||
relationships: { parent: null as any as [] }
|
||||
}));
|
||||
children.forEach((tag) =>
|
||||
tag.pushData({
|
||||
attributes: { isChild: false },
|
||||
// @deprecated. Temporary hack for type safety, remove before v1.3.
|
||||
relationships: { parent: null as any as [] },
|
||||
})
|
||||
);
|
||||
m.redraw();
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import sortable from 'sortablejs';
|
||||
|
||||
import app from 'flarum/admin/app';
|
||||
import ExtensionPage from 'flarum/admin/components/ExtensionPage';
|
||||
import Button from 'flarum/common/components/Button';
|
||||
import LoadingIndicator from 'flarum/common/components/LoadingIndicator';
|
||||
@ -18,16 +19,18 @@ function tagItem(tag) {
|
||||
{Button.component({
|
||||
className: 'Button Button--link',
|
||||
icon: 'fas fa-pencil-alt',
|
||||
onclick: () => app.modal.show(EditTagModal, { model: tag })
|
||||
onclick: () => app.modal.show(EditTagModal, { model: tag }),
|
||||
})}
|
||||
</div>
|
||||
{!tag.isChild() && tag.position() !== null ? (
|
||||
<ol className="TagListItem-children TagList">
|
||||
{sortTags(app.store.all('tags'))
|
||||
.filter(child => child.parent() === tag)
|
||||
.filter((child) => child.parent() === tag)
|
||||
.map(tagItem)}
|
||||
</ol>
|
||||
) : ''}
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@ -62,81 +65,78 @@ export default class TagsPage extends ExtensionPage {
|
||||
const minSecondaryTags = this.setting('flarum-tags.min_secondary_tags', 0);
|
||||
const maxSecondaryTags = this.setting('flarum-tags.max_secondary_tags', 0);
|
||||
|
||||
const tags = sortTags(app.store.all('tags').filter(tag => !tag.parent()));
|
||||
|
||||
const tags = sortTags(app.store.all('tags').filter((tag) => !tag.parent()));
|
||||
|
||||
return (
|
||||
<div className="TagsContent">
|
||||
<div className="TagsContent-list">
|
||||
<div className="container" key={this.forcedRefreshKey} oncreate={this.onListOnCreate.bind(this)}><div className="SettingsGroups">
|
||||
<div className="TagGroup">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tags.primary_heading')}</label>
|
||||
<ol className="TagList TagList--primary">
|
||||
{tags
|
||||
.filter(tag => tag.position() !== null && !tag.isChild())
|
||||
.map(tagItem)}
|
||||
</ol>
|
||||
{Button.component(
|
||||
{
|
||||
className: 'Button TagList-button',
|
||||
icon: 'fas fa-plus',
|
||||
onclick: () => app.modal.show(EditTagModal, { primary: true }),
|
||||
},
|
||||
app.translator.trans('flarum-tags.admin.tags.create_primary_tag_button')
|
||||
)}
|
||||
</div>
|
||||
<div className="container" key={this.forcedRefreshKey} oncreate={this.onListOnCreate.bind(this)}>
|
||||
<div className="SettingsGroups">
|
||||
<div className="TagGroup">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tags.primary_heading')}</label>
|
||||
<ol className="TagList TagList--primary">{tags.filter((tag) => tag.position() !== null && !tag.isChild()).map(tagItem)}</ol>
|
||||
{Button.component(
|
||||
{
|
||||
className: 'Button TagList-button',
|
||||
icon: 'fas fa-plus',
|
||||
onclick: () => app.modal.show(EditTagModal, { primary: true }),
|
||||
},
|
||||
app.translator.trans('flarum-tags.admin.tags.create_primary_tag_button')
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="TagGroup TagGroup--secondary">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tags.secondary_heading')}</label>
|
||||
<ul className="TagList">
|
||||
{tags
|
||||
.filter(tag => tag.position() === null)
|
||||
.sort((a, b) => a.name().localeCompare(b.name()))
|
||||
.map(tagItem)}
|
||||
</ul>
|
||||
{Button.component(
|
||||
{
|
||||
className: 'Button TagList-button',
|
||||
icon: 'fas fa-plus',
|
||||
onclick: () => app.modal.show(EditTagModal, { primary: false }),
|
||||
},
|
||||
app.translator.trans('flarum-tags.admin.tags.create_secondary_tag_button')
|
||||
)}
|
||||
</div>
|
||||
<div className="Form">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tags.settings_heading')}</label>
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tag_settings.required_primary_heading')}</label>
|
||||
<div className="helpText">{app.translator.trans('flarum-tags.admin.tag_settings.required_primary_text')}</div>
|
||||
<div className="TagSettings-rangeInput">
|
||||
<input
|
||||
className="FormControl"
|
||||
type="number"
|
||||
min="0"
|
||||
value={minPrimaryTags()}
|
||||
oninput={withAttr('value', this.setMinTags.bind(this, minPrimaryTags, maxPrimaryTags))}
|
||||
/>
|
||||
{app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')}
|
||||
<input className="FormControl" type="number" min={minPrimaryTags()} bidi={maxPrimaryTags} />
|
||||
</div>
|
||||
<div className="TagGroup TagGroup--secondary">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tags.secondary_heading')}</label>
|
||||
<ul className="TagList">
|
||||
{tags
|
||||
.filter((tag) => tag.position() === null)
|
||||
.sort((a, b) => a.name().localeCompare(b.name()))
|
||||
.map(tagItem)}
|
||||
</ul>
|
||||
{Button.component(
|
||||
{
|
||||
className: 'Button TagList-button',
|
||||
icon: 'fas fa-plus',
|
||||
onclick: () => app.modal.show(EditTagModal, { primary: false }),
|
||||
},
|
||||
app.translator.trans('flarum-tags.admin.tags.create_secondary_tag_button')
|
||||
)}
|
||||
</div>
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tag_settings.required_secondary_heading')}</label>
|
||||
<div className="helpText">{app.translator.trans('flarum-tags.admin.tag_settings.required_secondary_text')}</div>
|
||||
<div className="TagSettings-rangeInput">
|
||||
<input
|
||||
className="FormControl"
|
||||
type="number"
|
||||
min="0"
|
||||
value={minSecondaryTags()}
|
||||
oninput={withAttr('value', this.setMinTags.bind(this, minSecondaryTags, maxSecondaryTags))}
|
||||
/>
|
||||
{app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')}
|
||||
<input className="FormControl" type="number" min={minSecondaryTags()} bidi={maxSecondaryTags} />
|
||||
<div className="Form">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tags.settings_heading')}</label>
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tag_settings.required_primary_heading')}</label>
|
||||
<div className="helpText">{app.translator.trans('flarum-tags.admin.tag_settings.required_primary_text')}</div>
|
||||
<div className="TagSettings-rangeInput">
|
||||
<input
|
||||
className="FormControl"
|
||||
type="number"
|
||||
min="0"
|
||||
value={minPrimaryTags()}
|
||||
oninput={withAttr('value', this.setMinTags.bind(this, minPrimaryTags, maxPrimaryTags))}
|
||||
/>
|
||||
{app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')}
|
||||
<input className="FormControl" type="number" min={minPrimaryTags()} bidi={maxPrimaryTags} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('flarum-tags.admin.tag_settings.required_secondary_heading')}</label>
|
||||
<div className="helpText">{app.translator.trans('flarum-tags.admin.tag_settings.required_secondary_text')}</div>
|
||||
<div className="TagSettings-rangeInput">
|
||||
<input
|
||||
className="FormControl"
|
||||
type="number"
|
||||
min="0"
|
||||
value={minSecondaryTags()}
|
||||
oninput={withAttr('value', this.setMinTags.bind(this, minSecondaryTags, maxSecondaryTags))}
|
||||
/>
|
||||
{app.translator.trans('flarum-tags.admin.tag_settings.range_separator_text')}
|
||||
<input className="FormControl" type="number" min={minSecondaryTags()} bidi={maxSecondaryTags} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="Form-group">{this.submitButton()}</div>
|
||||
</div>
|
||||
<div className="Form-group">{this.submitButton()}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="TagsContent-footer">
|
||||
<p>{app.translator.trans('flarum-tags.admin.tags.about_tags_text')}</p>
|
||||
</div>
|
||||
@ -147,19 +147,21 @@ export default class TagsPage extends ExtensionPage {
|
||||
}
|
||||
|
||||
onListOnCreate(vnode) {
|
||||
this.$('.TagList').get().map(e => {
|
||||
sortable.create(e, {
|
||||
group: 'tags',
|
||||
delay: 50,
|
||||
delayOnTouchOnly: true,
|
||||
touchStartThreshold: 5,
|
||||
animation: 150,
|
||||
swapThreshold: 0.65,
|
||||
dragClass: 'sortable-dragging',
|
||||
ghostClass: 'sortable-placeholder',
|
||||
onSort: (e) => this.onSortUpdate(e)
|
||||
})
|
||||
});
|
||||
this.$('.TagList')
|
||||
.get()
|
||||
.map((e) => {
|
||||
sortable.create(e, {
|
||||
group: 'tags',
|
||||
delay: 50,
|
||||
delayOnTouchOnly: true,
|
||||
touchStartThreshold: 5,
|
||||
animation: 150,
|
||||
swapThreshold: 0.65,
|
||||
dragClass: 'sortable-dragging',
|
||||
ghostClass: 'sortable-placeholder',
|
||||
onSort: (e) => this.onSortUpdate(e),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setMinTags(minTags, maxTags, value) {
|
||||
@ -175,9 +177,9 @@ export default class TagsPage extends ExtensionPage {
|
||||
app.store.getById('tags', e.item.getAttribute('data-id')).pushData({
|
||||
attributes: {
|
||||
position: null,
|
||||
isChild: false
|
||||
isChild: false,
|
||||
},
|
||||
relationships: { parent: null }
|
||||
relationships: { parent: null },
|
||||
});
|
||||
}
|
||||
|
||||
@ -187,12 +189,15 @@ export default class TagsPage extends ExtensionPage {
|
||||
.map(function () {
|
||||
return {
|
||||
id: $(this).data('id'),
|
||||
children: $(this).find('li')
|
||||
children: $(this)
|
||||
.find('li')
|
||||
.map(function () {
|
||||
return $(this).data('id');
|
||||
}).get()
|
||||
})
|
||||
.get(),
|
||||
};
|
||||
}).get();
|
||||
})
|
||||
.get();
|
||||
|
||||
// Now that we have an accurate representation of the order which the
|
||||
// primary tags are in, we will update the tag attributes in our local
|
||||
@ -202,18 +207,18 @@ export default class TagsPage extends ExtensionPage {
|
||||
parent.pushData({
|
||||
attributes: {
|
||||
position: i,
|
||||
isChild: false
|
||||
isChild: false,
|
||||
},
|
||||
relationships: { parent: null }
|
||||
relationships: { parent: null },
|
||||
});
|
||||
|
||||
tag.children.forEach((child, j) => {
|
||||
app.store.getById('tags', child).pushData({
|
||||
attributes: {
|
||||
position: j,
|
||||
isChild: true
|
||||
isChild: true,
|
||||
},
|
||||
relationships: { parent }
|
||||
relationships: { parent },
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -221,7 +226,7 @@ export default class TagsPage extends ExtensionPage {
|
||||
app.request({
|
||||
url: app.forum.attribute('apiUrl') + '/tags/order',
|
||||
method: 'POST',
|
||||
body: { order }
|
||||
body: { order },
|
||||
});
|
||||
|
||||
this.forcedRefreshKey++;
|
||||
|
@ -6,7 +6,7 @@ import addTagsHomePageOption from './addTagsHomePageOption';
|
||||
import addTagChangePermission from './addTagChangePermission';
|
||||
import TagsPage from './components/TagsPage';
|
||||
|
||||
app.initializers.add('flarum-tags', app => {
|
||||
app.initializers.add('flarum-tags', (app) => {
|
||||
app.store.models.tags = Tag;
|
||||
|
||||
app.extensionData.for('flarum-tags').registerPage(TagsPage);
|
||||
@ -17,7 +17,6 @@ app.initializers.add('flarum-tags', app => {
|
||||
addTagChangePermission();
|
||||
});
|
||||
|
||||
|
||||
// Expose compat API
|
||||
import tagsCompat from './compat';
|
||||
import { compat } from '@flarum/core/admin';
|
||||
|
@ -9,5 +9,5 @@ export default {
|
||||
'tags/models/Tag': Tag,
|
||||
'tags/helpers/tagsLabel': tagsLabel,
|
||||
'tags/helpers/tagIcon': tagIcon,
|
||||
'tags/helpers/tagLabel': tagLabel
|
||||
'tags/helpers/tagLabel': tagLabel,
|
||||
};
|
||||
|
@ -4,11 +4,7 @@ export default function tagIcon(tag, attrs = {}, settings = {}) {
|
||||
const hasIcon = tag && tag.icon();
|
||||
const { useColor = true } = settings;
|
||||
|
||||
attrs.className = classList([
|
||||
attrs.className,
|
||||
'icon',
|
||||
hasIcon ? tag.icon() : 'TagIcon'
|
||||
]);
|
||||
attrs.className = classList([attrs.className, 'icon', hasIcon ? tag.icon() : 'TagIcon']);
|
||||
|
||||
if (tag && useColor) {
|
||||
attrs.style = attrs.style || {};
|
||||
@ -21,5 +17,5 @@ export default function tagIcon(tag, attrs = {}, settings = {}) {
|
||||
attrs.className += ' untagged';
|
||||
}
|
||||
|
||||
return hasIcon ? <i {...attrs}/> : <span {...attrs}/>;
|
||||
return hasIcon ? <i {...attrs} /> : <span {...attrs} />;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ export default function tagLabel(tag, attrs = {}) {
|
||||
|
||||
if (link) {
|
||||
attrs.title = tag.description() || '';
|
||||
attrs.href = app.route('tag', {tags: tag.slug()});
|
||||
attrs.href = app.route('tag', { tags: tag.slug() });
|
||||
}
|
||||
|
||||
if (tag.isChild()) {
|
||||
@ -28,11 +28,11 @@ export default function tagLabel(tag, attrs = {}) {
|
||||
attrs.className += ' untagged';
|
||||
}
|
||||
|
||||
return (
|
||||
m((link ? Link : 'span'), attrs,
|
||||
<span className="TagLabel-text">
|
||||
{tag && tag.icon() && tagIcon(tag, {}, {useColor: false})} {tagText}
|
||||
</span>
|
||||
)
|
||||
return m(
|
||||
link ? Link : 'span',
|
||||
attrs,
|
||||
<span className="TagLabel-text">
|
||||
{tag && tag.icon() && tagIcon(tag, {}, { useColor: false })} {tagText}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ export default function tagsLabel(tags, attrs = {}) {
|
||||
attrs.className = 'TagsLabel ' + (attrs.className || '');
|
||||
|
||||
if (tags) {
|
||||
sortTags(tags).forEach(tag => {
|
||||
sortTags(tags).forEach((tag) => {
|
||||
if (tag || tags.length === 1) {
|
||||
children.push(tagLabel(tag, {link}));
|
||||
children.push(tagLabel(tag, { link }));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Tag from "../models/Tag";
|
||||
import Tag from '../models/Tag';
|
||||
|
||||
export default function sortTags(tags: Tag[]) {
|
||||
return tags.slice(0).sort((a, b) => {
|
||||
@ -7,8 +7,7 @@ export default function sortTags(tags: Tag[]) {
|
||||
|
||||
// If they're both secondary tags, sort them by their discussions count,
|
||||
// descending.
|
||||
if (aPos === null && bPos === null)
|
||||
return b.discussionCount() - a.discussionCount();
|
||||
if (aPos === null && bPos === null) return b.discussionCount() - a.discussionCount();
|
||||
|
||||
// If just one is a secondary tag, then the primary tag should
|
||||
// come first.
|
||||
@ -23,20 +22,14 @@ export default function sortTags(tags: Tag[]) {
|
||||
// If they both have the same parent, then their positions are local,
|
||||
// so we can compare them directly.
|
||||
if (aParent === bParent) return aPos - bPos;
|
||||
|
||||
// If they are both child tags, then we will compare the positions of their
|
||||
// parents.
|
||||
else if (aParent && bParent)
|
||||
return aParent.position()! - bParent.position()!;
|
||||
|
||||
else if (aParent && bParent) return aParent.position()! - bParent.position()!;
|
||||
// If we are comparing a child tag with its parent, then we let the parent
|
||||
// come first. If we are comparing an unrelated parent/child, then we
|
||||
// compare both of the parents.
|
||||
else if (aParent)
|
||||
return aParent === b ? 1 : aParent.position()! - bPos;
|
||||
|
||||
else if (bParent)
|
||||
return bParent === a ? -1 : aPos - bParent.position()!;
|
||||
else if (aParent) return aParent === b ? 1 : aParent.position()! - bPos;
|
||||
else if (bParent) return bParent === a ? -1 : aPos - bParent.position()!;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
@ -15,15 +15,14 @@ export default function () {
|
||||
if (tag) {
|
||||
const parent = tag.parent();
|
||||
const tags = parent ? [parent, tag] : [tag];
|
||||
promise.then(composer => composer.fields.tags = tags);
|
||||
promise.then((composer) => (composer.fields.tags = tags));
|
||||
} else {
|
||||
app.composer.fields.tags = [];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
extend(DiscussionComposer.prototype, 'oninit', function () {
|
||||
app.tagList.load(['parent']).then(() => m.redraw())
|
||||
app.tagList.load(['parent']).then(() => m.redraw());
|
||||
});
|
||||
|
||||
// Add tag-selection abilities to the discussion composer.
|
||||
@ -34,10 +33,10 @@ export default function () {
|
||||
|
||||
app.modal.show(TagDiscussionModal, {
|
||||
selectedTags: (this.composer.fields.tags || []).slice(0),
|
||||
onsubmit: tags => {
|
||||
onsubmit: (tags) => {
|
||||
this.composer.fields.tags = tags;
|
||||
this.$('textarea').focus();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -47,32 +46,38 @@ export default function () {
|
||||
const tags = this.composer.fields.tags || [];
|
||||
const selectableTags = getSelectableTags();
|
||||
|
||||
items.add('tags', (
|
||||
items.add(
|
||||
'tags',
|
||||
<a className={classList(['DiscussionComposer-changeTags', !selectableTags.length && 'disabled'])} onclick={this.chooseTags.bind(this)}>
|
||||
{tags.length
|
||||
? tagsLabel(tags)
|
||||
: <span className="TagLabel untagged">{app.translator.trans('flarum-tags.forum.composer_discussion.choose_tags_link')}</span>}
|
||||
</a>
|
||||
), 10);
|
||||
{tags.length ? (
|
||||
tagsLabel(tags)
|
||||
) : (
|
||||
<span className="TagLabel untagged">{app.translator.trans('flarum-tags.forum.composer_discussion.choose_tags_link')}</span>
|
||||
)}
|
||||
</a>,
|
||||
10
|
||||
);
|
||||
});
|
||||
|
||||
override(DiscussionComposer.prototype, 'onsubmit', function (original) {
|
||||
const chosenTags = this.composer.fields.tags || [];
|
||||
const chosenPrimaryTags = chosenTags.filter(tag => tag.position() !== null && !tag.isChild());
|
||||
const chosenSecondaryTags = chosenTags.filter(tag => tag.position() === null);
|
||||
const chosenPrimaryTags = chosenTags.filter((tag) => tag.position() !== null && !tag.isChild());
|
||||
const chosenSecondaryTags = chosenTags.filter((tag) => tag.position() === null);
|
||||
const selectableTags = getSelectableTags();
|
||||
|
||||
if ((!chosenTags.length
|
||||
|| (chosenPrimaryTags.length < app.forum.attribute('minPrimaryTags'))
|
||||
|| (chosenSecondaryTags.length < app.forum.attribute('minSecondaryTags'))
|
||||
) && selectableTags.length) {
|
||||
if (
|
||||
(!chosenTags.length ||
|
||||
chosenPrimaryTags.length < app.forum.attribute('minPrimaryTags') ||
|
||||
chosenSecondaryTags.length < app.forum.attribute('minSecondaryTags')) &&
|
||||
selectableTags.length
|
||||
) {
|
||||
app.modal.show(TagDiscussionModal, {
|
||||
selectedTags: chosenTags,
|
||||
onsubmit: tags => {
|
||||
this.composer.fields.tags = tags;
|
||||
original();
|
||||
}
|
||||
});
|
||||
selectedTags: chosenTags,
|
||||
onsubmit: (tags) => {
|
||||
this.composer.fields.tags = tags;
|
||||
original();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
original();
|
||||
}
|
||||
|
@ -4,13 +4,16 @@ import Button from 'flarum/common/components/Button';
|
||||
|
||||
import TagDiscussionModal from './components/TagDiscussionModal';
|
||||
|
||||
export default function() {
|
||||
export default function () {
|
||||
// Add a control allowing the discussion to be moved to another category.
|
||||
extend(DiscussionControls, 'moderationControls', function(items, discussion) {
|
||||
extend(DiscussionControls, 'moderationControls', function (items, discussion) {
|
||||
if (discussion.canTag()) {
|
||||
items.add('tags', <Button icon="fas fa-tag" onclick={() => app.modal.show(TagDiscussionModal, { discussion })}>
|
||||
{app.translator.trans('flarum-tags.forum.discussion_controls.edit_tags_button')}
|
||||
</Button>);
|
||||
items.add(
|
||||
'tags',
|
||||
<Button icon="fas fa-tag" onclick={() => app.modal.show(TagDiscussionModal, { discussion })}>
|
||||
{app.translator.trans('flarum-tags.forum.discussion_controls.edit_tags_button')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -10,10 +10,10 @@ import TagHero from './components/TagHero';
|
||||
import Tag from '../common/models/Tag';
|
||||
import { ComponentAttrs } from 'flarum/common/Component';
|
||||
|
||||
const findTag = (slug: string) => app.store.all<Tag>('tags').find(tag => tag.slug().localeCompare(slug, undefined, { sensitivity: 'base' }) === 0);
|
||||
const findTag = (slug: string) => app.store.all<Tag>('tags').find((tag) => tag.slug().localeCompare(slug, undefined, { sensitivity: 'base' }) === 0);
|
||||
|
||||
export default function() {
|
||||
IndexPage.prototype.currentTag = function() {
|
||||
export default function () {
|
||||
IndexPage.prototype.currentTag = function () {
|
||||
if (this.currentActiveTag) {
|
||||
return this.currentActiveTag;
|
||||
}
|
||||
@ -25,7 +25,7 @@ export default function() {
|
||||
tag = findTag(slug);
|
||||
}
|
||||
|
||||
if (slug && !tag || (tag && !tag.isChild() && !tag.children())) {
|
||||
if ((slug && !tag) || (tag && !tag.isChild() && !tag.children())) {
|
||||
if (this.currentTagLoading) {
|
||||
return;
|
||||
}
|
||||
@ -36,13 +36,16 @@ export default function() {
|
||||
// a child tag page, then either:
|
||||
// - We loaded in that child tag (and its siblings) in the API document
|
||||
// - We first navigated to the current tag's parent, which would have loaded in the current tag's siblings.
|
||||
app.store.find('tags', slug, { include: 'children,children.parent,parent,state'}).then(() => {
|
||||
this.currentActiveTag = findTag(slug);
|
||||
app.store
|
||||
.find('tags', slug, { include: 'children,children.parent,parent,state' })
|
||||
.then(() => {
|
||||
this.currentActiveTag = findTag(slug);
|
||||
|
||||
m.redraw();
|
||||
}).finally(() => {
|
||||
this.currentTagLoading = false;
|
||||
});
|
||||
m.redraw();
|
||||
})
|
||||
.finally(() => {
|
||||
this.currentTagLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
if (tag) {
|
||||
@ -54,7 +57,7 @@ export default function() {
|
||||
};
|
||||
|
||||
// If currently viewing a tag, insert a tag hero at the top of the view.
|
||||
override(IndexPage.prototype, 'hero', function(original) {
|
||||
override(IndexPage.prototype, 'hero', function (original) {
|
||||
const tag = this.currentTag();
|
||||
|
||||
if (tag) return <TagHero model={tag} />;
|
||||
@ -62,13 +65,13 @@ export default function() {
|
||||
return original();
|
||||
});
|
||||
|
||||
extend(IndexPage.prototype, 'view', function(vdom: Mithril.Vnode<ComponentAttrs, {}>) {
|
||||
extend(IndexPage.prototype, 'view', function (vdom: Mithril.Vnode<ComponentAttrs, {}>) {
|
||||
const tag = this.currentTag();
|
||||
|
||||
if (tag) vdom.attrs.className += ' IndexPage--tag'+tag.id();
|
||||
if (tag) vdom.attrs.className += ' IndexPage--tag' + tag.id();
|
||||
});
|
||||
|
||||
extend(IndexPage.prototype, 'setTitle', function() {
|
||||
extend(IndexPage.prototype, 'setTitle', function () {
|
||||
const tag = this.currentTag();
|
||||
|
||||
if (tag) {
|
||||
@ -78,7 +81,7 @@ export default function() {
|
||||
|
||||
// If currently viewing a tag, restyle the 'new discussion' button to use
|
||||
// the tag's color, and disable if the user isn't allowed to edit.
|
||||
extend(IndexPage.prototype, 'sidebarItems', function(items) {
|
||||
extend(IndexPage.prototype, 'sidebarItems', function (items) {
|
||||
const tag = this.currentTag();
|
||||
|
||||
if (tag) {
|
||||
@ -92,18 +95,20 @@ export default function() {
|
||||
}
|
||||
|
||||
newDiscussion.attrs.disabled = !canStartDiscussion;
|
||||
newDiscussion.children = app.translator.trans(canStartDiscussion ? 'core.forum.index.start_discussion_button' : 'core.forum.index.cannot_start_discussion_button');
|
||||
newDiscussion.children = app.translator.trans(
|
||||
canStartDiscussion ? 'core.forum.index.start_discussion_button' : 'core.forum.index.cannot_start_discussion_button'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Add a parameter for the global search state to pass on to the
|
||||
// DiscussionListState that will let us filter discussions by tag.
|
||||
extend(GlobalSearchState.prototype, 'params', function(params) {
|
||||
extend(GlobalSearchState.prototype, 'params', function (params) {
|
||||
params.tags = m.route.param('tags');
|
||||
});
|
||||
|
||||
// Translate that parameter into a gambit appended to the search query.
|
||||
extend(DiscussionListState.prototype, 'requestParams', function(this: DiscussionListState, params) {
|
||||
extend(DiscussionListState.prototype, 'requestParams', function (this: DiscussionListState, params) {
|
||||
if (typeof params.include === 'string') {
|
||||
params.include = [params.include];
|
||||
} else {
|
||||
@ -118,7 +123,7 @@ export default function() {
|
||||
if (q) {
|
||||
filter.q = `${q} tag:${this.params.tags}`;
|
||||
}
|
||||
params.filter = filter
|
||||
params.filter = filter;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ import DiscussionHero from 'flarum/forum/components/DiscussionHero';
|
||||
import tagsLabel from '../common/helpers/tagsLabel';
|
||||
import sortTags from '../common/utils/sortTags';
|
||||
|
||||
export default function() {
|
||||
export default function () {
|
||||
// Add tag labels to each discussion in the discussion list.
|
||||
extend(DiscussionListItem.prototype, 'infoItems', function(items) {
|
||||
extend(DiscussionListItem.prototype, 'infoItems', function (items) {
|
||||
const tags = this.attrs.discussion.tags();
|
||||
|
||||
if (tags && tags.length) {
|
||||
@ -16,7 +16,7 @@ export default function() {
|
||||
});
|
||||
|
||||
// Restyle a discussion's hero to use its first tag's color.
|
||||
extend(DiscussionHero.prototype, 'view', function(view) {
|
||||
extend(DiscussionHero.prototype, 'view', function (view) {
|
||||
const tags = sortTags(this.attrs.discussion.tags());
|
||||
|
||||
if (tags && tags.length) {
|
||||
@ -30,11 +30,11 @@ export default function() {
|
||||
|
||||
// Add a list of a discussion's tags to the discussion hero, displayed
|
||||
// before the title. Put the title on its own line.
|
||||
extend(DiscussionHero.prototype, 'items', function(items) {
|
||||
extend(DiscussionHero.prototype, 'items', function (items) {
|
||||
const tags = this.attrs.discussion.tags();
|
||||
|
||||
if (tags && tags.length) {
|
||||
items.add('tags', tagsLabel(tags, {link: true}), 5);
|
||||
items.add('tags', tagsLabel(tags, { link: true }), 5);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -7,14 +7,17 @@ import TagLinkButton from './components/TagLinkButton';
|
||||
import TagsPage from './components/TagsPage';
|
||||
import sortTags from '../common/utils/sortTags';
|
||||
|
||||
export default function() {
|
||||
export default function () {
|
||||
// Add a link to the tags page, as well as a list of all the tags,
|
||||
// to the index page's sidebar.
|
||||
extend(IndexPage.prototype, 'navItems', function (items) {
|
||||
items.add('tags', <LinkButton icon="fas fa-th-large" href={app.route('tags')}>
|
||||
{app.translator.trans('flarum-tags.forum.index.tags_link')}
|
||||
</LinkButton>
|
||||
, -10);
|
||||
items.add(
|
||||
'tags',
|
||||
<LinkButton icon="fas fa-th-large" href={app.route('tags')}>
|
||||
{app.translator.trans('flarum-tags.forum.index.tags_link')}
|
||||
</LinkButton>,
|
||||
-10
|
||||
);
|
||||
|
||||
if (app.current.matches(TagsPage)) return;
|
||||
|
||||
@ -24,7 +27,7 @@ export default function() {
|
||||
const tags = app.store.all('tags');
|
||||
const currentTag = this.currentTag();
|
||||
|
||||
const addTag = tag => {
|
||||
const addTag = (tag) => {
|
||||
let active = currentTag === tag;
|
||||
|
||||
if (!active && currentTag) {
|
||||
@ -36,23 +39,21 @@ export default function() {
|
||||
// use its children to populate the dropdown. The problem here is that `view`
|
||||
// on TagLinkButton is only called AFTER SelectDropdown, so no children are available
|
||||
// for SelectDropdown to use at the time.
|
||||
items.add('tag' + tag.id(), TagLinkButton.component({model: tag, params, active}, tag?.name()), -14);
|
||||
items.add('tag' + tag.id(), TagLinkButton.component({ model: tag, params, active }, tag?.name()), -14);
|
||||
};
|
||||
|
||||
sortTags(tags)
|
||||
.filter(tag => tag.position() !== null && (!tag.isChild() || (currentTag && (tag.parent() === currentTag || tag.parent() === currentTag.parent()))))
|
||||
.filter(
|
||||
(tag) => tag.position() !== null && (!tag.isChild() || (currentTag && (tag.parent() === currentTag || tag.parent() === currentTag.parent())))
|
||||
)
|
||||
.forEach(addTag);
|
||||
|
||||
const more = tags
|
||||
.filter(tag => tag.position() === null)
|
||||
.sort((a, b) => b.discussionCount() - a.discussionCount());
|
||||
const more = tags.filter((tag) => tag.position() === null).sort((a, b) => b.discussionCount() - a.discussionCount());
|
||||
|
||||
more.splice(0, 3).forEach(addTag);
|
||||
|
||||
if (more.length) {
|
||||
items.add('moreTags', <LinkButton href={app.route('tags')}>
|
||||
{app.translator.trans('flarum-tags.forum.index.more_link')}
|
||||
</LinkButton>, -16)
|
||||
items.add('moreTags', <LinkButton href={app.route('tags')}>{app.translator.trans('flarum-tags.forum.index.more_link')}</LinkButton>, -16);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -9,9 +9,7 @@ export default class DiscussionTaggedPost extends EventPost {
|
||||
const newTags = attrs.post.content()[1];
|
||||
|
||||
function diffTags(tags1, tags2) {
|
||||
return tags1
|
||||
.filter(tag => tags2.indexOf(tag) === -1)
|
||||
.map(id => app.store.getById('tags', id));
|
||||
return tags1.filter((tag) => tags2.indexOf(tag) === -1).map((id) => app.store.getById('tags', id));
|
||||
}
|
||||
|
||||
attrs.tagsAdded = diffTags(newTags, oldTags);
|
||||
@ -39,15 +37,15 @@ export default class DiscussionTaggedPost extends EventPost {
|
||||
|
||||
if (this.attrs.tagsAdded.length) {
|
||||
data.tagsAdded = app.translator.trans('flarum-tags.forum.post_stream.tags_text', {
|
||||
tags: tagsLabel(this.attrs.tagsAdded, {link: true}),
|
||||
count: this.attrs.tagsAdded.length
|
||||
tags: tagsLabel(this.attrs.tagsAdded, { link: true }),
|
||||
count: this.attrs.tagsAdded.length,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.attrs.tagsRemoved.length) {
|
||||
data.tagsRemoved = app.translator.trans('flarum-tags.forum.post_stream.tags_text', {
|
||||
tags: tagsLabel(this.attrs.tagsRemoved, {link: true}),
|
||||
count: this.attrs.tagsRemoved.length
|
||||
tags: tagsLabel(this.attrs.tagsRemoved, { link: true }),
|
||||
count: this.attrs.tagsRemoved.length,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -58,11 +58,11 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
const tags = sortTags(getSelectableTags(this.attrs.discussion));
|
||||
this.tags = tags;
|
||||
|
||||
const discussionTags = this.attrs.discussion?.tags()
|
||||
const discussionTags = this.attrs.discussion?.tags();
|
||||
if (this.attrs.selectedTags) {
|
||||
this.attrs.selectedTags.map(this.addTag.bind(this));
|
||||
} else if (discussionTags) {
|
||||
discussionTags.forEach(tag => tag && this.addTag(tag));
|
||||
discussionTags.forEach((tag) => tag && this.addTag(tag));
|
||||
}
|
||||
|
||||
this.selectedTag = tags[0];
|
||||
@ -72,11 +72,11 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
}
|
||||
|
||||
primaryCount() {
|
||||
return this.selected.filter(tag => tag.isPrimary()).length;
|
||||
return this.selected.filter((tag) => tag.isPrimary()).length;
|
||||
}
|
||||
|
||||
secondaryCount() {
|
||||
return this.selected.filter(tag => !tag.isPrimary()).length;
|
||||
return this.selected.filter((tag) => !tag.isPrimary()).length;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,9 +107,7 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
|
||||
// Look through the list of selected tags for any tags which have the tag
|
||||
// we just removed as their parent. We'll need to remove them too.
|
||||
this.selected
|
||||
.filter(selected => selected.parent() === tag)
|
||||
.forEach(this.removeTag.bind(this));
|
||||
this.selected.filter((selected) => selected.parent() === tag).forEach(this.removeTag.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,7 +117,7 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
|
||||
title() {
|
||||
return this.attrs.discussion
|
||||
? app.translator.trans('flarum-tags.forum.choose_tags.edit_title', {title: <em>{this.attrs.discussion.title()}</em>})
|
||||
? app.translator.trans('flarum-tags.forum.choose_tags.edit_title', { title: <em>{this.attrs.discussion.title()}</em> })
|
||||
: app.translator.trans('flarum-tags.forum.choose_tags.title');
|
||||
}
|
||||
|
||||
@ -130,10 +128,10 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
|
||||
if (primaryCount < this.minPrimary) {
|
||||
const remaining = this.minPrimary - primaryCount;
|
||||
return app.translator.trans('flarum-tags.forum.choose_tags.choose_primary_placeholder', {count: remaining});
|
||||
return app.translator.trans('flarum-tags.forum.choose_tags.choose_primary_placeholder', { count: remaining });
|
||||
} else if (secondaryCount < this.minSecondary) {
|
||||
const remaining = this.minSecondary - secondaryCount;
|
||||
return app.translator.trans('flarum-tags.forum.choose_tags.choose_secondary_placeholder', {count: remaining});
|
||||
return app.translator.trans('flarum-tags.forum.choose_tags.choose_secondary_placeholder', { count: remaining });
|
||||
}
|
||||
|
||||
return '';
|
||||
@ -151,7 +149,7 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
|
||||
// Filter out all child tags whose parents have not been selected. This
|
||||
// makes it impossible to select a child if its parent hasn't been selected.
|
||||
tags = tags.filter(tag => {
|
||||
tags = tags.filter((tag) => {
|
||||
const parent = tag.parent();
|
||||
return parent !== null && (parent === false || this.selected.includes(parent));
|
||||
});
|
||||
@ -159,17 +157,17 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
// If the number of selected primary/secondary tags is at the maximum, then
|
||||
// we'll filter out all other tags of that type.
|
||||
if (primaryCount >= this.maxPrimary && !this.bypassReqs) {
|
||||
tags = tags.filter(tag => !tag.isPrimary() || this.selected.includes(tag));
|
||||
tags = tags.filter((tag) => !tag.isPrimary() || this.selected.includes(tag));
|
||||
}
|
||||
|
||||
if (secondaryCount >= this.maxSecondary && !this.bypassReqs) {
|
||||
tags = tags.filter(tag => tag.isPrimary() || this.selected.includes(tag));
|
||||
tags = tags.filter((tag) => tag.isPrimary() || this.selected.includes(tag));
|
||||
}
|
||||
|
||||
// If the user has entered text in the filter input, then filter by tags
|
||||
// whose name matches what they've entered.
|
||||
if (filter) {
|
||||
tags = tags.filter(tag => tag.name().substr(0, filter.length).toLowerCase() === filter);
|
||||
tags = tags.filter((tag) => tag.name().substr(0, filter.length).toLowerCase() === filter);
|
||||
}
|
||||
|
||||
if (!this.selectedTag || !tags.includes(this.selectedTag)) this.selectedTag = tags[0];
|
||||
@ -180,30 +178,38 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
<div className="Modal-body">
|
||||
<div className="TagDiscussionModal-form">
|
||||
<div className="TagDiscussionModal-form-input">
|
||||
<div className={'TagsInput FormControl ' + (this.focused ? 'focus' : '')}
|
||||
onclick={() => this.$('.TagsInput input').focus()}
|
||||
>
|
||||
<div className={'TagsInput FormControl ' + (this.focused ? 'focus' : '')} onclick={() => this.$('.TagsInput input').focus()}>
|
||||
<span className="TagsInput-selected">
|
||||
{this.selected.map(tag =>
|
||||
<span className="TagsInput-tag" onclick={() => {
|
||||
this.removeTag(tag);
|
||||
this.onready();
|
||||
}}>
|
||||
{this.selected.map((tag) => (
|
||||
<span
|
||||
className="TagsInput-tag"
|
||||
onclick={() => {
|
||||
this.removeTag(tag);
|
||||
this.onready();
|
||||
}}
|
||||
>
|
||||
{tagLabel(tag)}
|
||||
</span>
|
||||
)}
|
||||
))}
|
||||
</span>
|
||||
<input className="FormControl"
|
||||
<input
|
||||
className="FormControl"
|
||||
placeholder={extractText(this.getInstruction(primaryCount, secondaryCount))}
|
||||
bidi={this.filter}
|
||||
style={{ width: inputWidth + 'ch' }}
|
||||
onkeydown={this.navigator.navigate.bind(this.navigator)}
|
||||
onfocus={() => this.focused = true}
|
||||
onblur={() => this.focused = false}/>
|
||||
onfocus={() => (this.focused = true)}
|
||||
onblur={() => (this.focused = false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="TagDiscussionModal-form-submit App-primaryControl">
|
||||
<Button type="submit" className="Button Button--primary" disabled={!this.meetsRequirements(primaryCount, secondaryCount)} icon="fas fa-check">
|
||||
<Button
|
||||
type="submit"
|
||||
className="Button Button--primary"
|
||||
disabled={!this.meetsRequirements(primaryCount, secondaryCount)}
|
||||
icon="fas fa-check"
|
||||
>
|
||||
{app.translator.trans('flarum-tags.forum.choose_tags.submit_button')}
|
||||
</Button>
|
||||
</div>
|
||||
@ -213,41 +219,35 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
<div className="Modal-footer">
|
||||
<ul className="TagDiscussionModal-list SelectTagList">
|
||||
{tags
|
||||
.filter(tag => filter || !tag.parent() || this.selected.includes(tag.parent() as Tag))
|
||||
.map(tag => (
|
||||
<li data-index={tag.id()}
|
||||
.filter((tag) => filter || !tag.parent() || this.selected.includes(tag.parent() as Tag))
|
||||
.map((tag) => (
|
||||
<li
|
||||
data-index={tag.id()}
|
||||
className={classList({
|
||||
pinned: tag.position() !== null,
|
||||
child: !!tag.parent(),
|
||||
colored: !!tag.color(),
|
||||
selected: this.selected.includes(tag),
|
||||
active: this.selectedTag === tag
|
||||
active: this.selectedTag === tag,
|
||||
})}
|
||||
style={{color: tag.color()}}
|
||||
onmouseover={() => this.selectedTag = tag}
|
||||
style={{ color: tag.color() }}
|
||||
onmouseover={() => (this.selectedTag = tag)}
|
||||
onclick={this.toggleTag.bind(this, tag)}
|
||||
>
|
||||
{tagIcon(tag)}
|
||||
<span className="SelectTagListItem-name">
|
||||
{highlight(tag.name(), filter)}
|
||||
</span>
|
||||
{tag.description()
|
||||
? (
|
||||
<span className="SelectTagListItem-description">
|
||||
{tag.description()}
|
||||
</span>
|
||||
) : ''}
|
||||
<span className="SelectTagListItem-name">{highlight(tag.name(), filter)}</span>
|
||||
{tag.description() ? <span className="SelectTagListItem-description">{tag.description()}</span> : ''}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{!!app.forum.attribute('canBypassTagCounts') && (
|
||||
<div className="TagDiscussionModal-controls">
|
||||
<ToggleButton className="Button" onclick={() => this.bypassReqs = !this.bypassReqs} isToggled={this.bypassReqs}>
|
||||
<ToggleButton className="Button" onclick={() => (this.bypassReqs = !this.bypassReqs)} isToggled={this.bypassReqs}>
|
||||
{app.translator.trans('flarum-tags.forum.choose_tags.bypass_requirements')}
|
||||
</ToggleButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>,
|
||||
];
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
|
||||
select(e: KeyboardEvent) {
|
||||
// Ctrl + Enter submits the selection, just Enter completes the current entry
|
||||
if (e.metaKey || e.ctrlKey || this.selectedTag && this.selected.includes(this.selectedTag)) {
|
||||
if (e.metaKey || e.ctrlKey || (this.selectedTag && this.selected.includes(this.selectedTag))) {
|
||||
if (this.selected.length) {
|
||||
// The DOM submit method doesn't emit a `submit event, so we
|
||||
// simulate a manual submission so our `onsubmit` logic is run.
|
||||
@ -297,9 +297,7 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
getCurrentNumericIndex() {
|
||||
if (!this.selectedTag) return -1;
|
||||
|
||||
return this.selectableItems().index(
|
||||
this.getItem(this.selectedTag)
|
||||
);
|
||||
return this.selectableItems().index(this.getItem(this.selectedTag));
|
||||
}
|
||||
|
||||
getItem(selectedTag: Tag) {
|
||||
@ -337,7 +335,7 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
}
|
||||
|
||||
if (typeof scrollTop !== 'undefined') {
|
||||
$dropdown.stop(true).animate({scrollTop}, 100);
|
||||
$dropdown.stop(true).animate({ scrollTop }, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -349,13 +347,12 @@ export default class TagDiscussionModal extends Modal<TagDiscussionModalAttrs> {
|
||||
const tags = this.selected;
|
||||
|
||||
if (discussion) {
|
||||
discussion.save({relationships: {tags}})
|
||||
.then(() => {
|
||||
if (app.current.matches(DiscussionPage)) {
|
||||
app.current.get('stream').update();
|
||||
}
|
||||
m.redraw();
|
||||
});
|
||||
discussion.save({ relationships: { tags } }).then(() => {
|
||||
if (app.current.matches(DiscussionPage)) {
|
||||
app.current.get('stream').update();
|
||||
}
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
if (this.attrs.onsubmit) this.attrs.onsubmit(tags);
|
||||
|
@ -7,11 +7,12 @@ export default class TagHero extends Component {
|
||||
const color = tag.color();
|
||||
|
||||
return (
|
||||
<header className={'Hero TagHero' + (color ? ' TagHero--colored' : '')}
|
||||
style={color ? { '--hero-bg': color } : ''}>
|
||||
<header className={'Hero TagHero' + (color ? ' TagHero--colored' : '')} style={color ? { '--hero-bg': color } : ''}>
|
||||
<div className="container">
|
||||
<div className="containerNarrow">
|
||||
<h2 className="Hero-title">{tag.icon() && tagIcon(tag, {}, { useColor: false })} {tag.name()}</h2>
|
||||
<h2 className="Hero-title">
|
||||
{tag.icon() && tagIcon(tag, {}, { useColor: false })} {tag.name()}
|
||||
</h2>
|
||||
<div className="Hero-subtitle">{tag.description()}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,21 +8,12 @@ export default class TagLinkButton extends LinkButton {
|
||||
const tag = this.attrs.model;
|
||||
const active = this.constructor.isActive(this.attrs);
|
||||
const description = tag && tag.description();
|
||||
const className = classList([
|
||||
'TagLinkButton',
|
||||
'hasIcon',
|
||||
this.attrs.className,
|
||||
tag.isChild() && 'child',
|
||||
]);
|
||||
const className = classList(['TagLinkButton', 'hasIcon', this.attrs.className, tag.isChild() && 'child']);
|
||||
|
||||
return (
|
||||
<Link className={className} href={this.attrs.route}
|
||||
style={tag ? { '--color': tag.color() } : ''}
|
||||
title={description || ''}>
|
||||
<Link className={className} href={this.attrs.route} style={tag ? { '--color': tag.color() } : ''} title={description || ''}>
|
||||
{tagIcon(tag, { className: 'Button-icon' })}
|
||||
<span className="Button-label">
|
||||
{tag ? tag.name() : app.translator.trans('flarum-tags.forum.index.untagged_link')}
|
||||
</span>
|
||||
<span className="Button-label">{tag ? tag.name() : app.translator.trans('flarum-tags.forum.index.untagged_link')}</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
@ -20,14 +20,14 @@ export default class TagsPage extends Page {
|
||||
const preloaded = app.preloadedApiDocument();
|
||||
|
||||
if (preloaded) {
|
||||
this.tags = sortTags(preloaded.filter(tag => !tag.isChild()));
|
||||
this.tags = sortTags(preloaded.filter((tag) => !tag.isChild()));
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
|
||||
app.tagList.load(['children', 'lastPostedDiscussion', 'parent']).then(() => {
|
||||
this.tags = sortTags(app.store.all('tags').filter(tag => !tag.isChild()));
|
||||
this.tags = sortTags(app.store.all('tags').filter((tag) => !tag.isChild()));
|
||||
|
||||
this.loading = false;
|
||||
|
||||
@ -40,8 +40,8 @@ export default class TagsPage extends Page {
|
||||
return <LoadingIndicator />;
|
||||
}
|
||||
|
||||
const pinned = this.tags.filter(tag => tag.position() !== null);
|
||||
const cloud = this.tags.filter(tag => tag.position() === null);
|
||||
const pinned = this.tags.filter((tag) => tag.position() !== null);
|
||||
const cloud = this.tags.filter((tag) => tag.position() === null);
|
||||
|
||||
return (
|
||||
<div className="TagsPage">
|
||||
@ -53,53 +53,41 @@ export default class TagsPage extends Page {
|
||||
|
||||
<div className="TagsPage-content sideNavOffset">
|
||||
<ul className="TagTiles">
|
||||
{pinned.map(tag => {
|
||||
{pinned.map((tag) => {
|
||||
const lastPostedDiscussion = tag.lastPostedDiscussion();
|
||||
const children = sortTags(tag.children() || []);
|
||||
|
||||
return (
|
||||
<li className={'TagTile ' + (tag.color() ? 'colored' : '')}
|
||||
style={{ '--tag-bg': tag.color() }}>
|
||||
<li className={'TagTile ' + (tag.color() ? 'colored' : '')} style={{ '--tag-bg': tag.color() }}>
|
||||
<Link className="TagTile-info" href={app.route.tag(tag)}>
|
||||
{tag.icon() && tagIcon(tag, {}, { useColor: false })}
|
||||
<h3 className="TagTile-name">{tag.name()}</h3>
|
||||
<p className="TagTile-description">{tag.description()}</p>
|
||||
{children
|
||||
? (
|
||||
<div className="TagTile-children">
|
||||
{children.map(child => [
|
||||
<Link href={app.route.tag(child)}>
|
||||
{child.name()}
|
||||
</Link>,
|
||||
' '
|
||||
])}
|
||||
</div>
|
||||
) : ''}
|
||||
</Link>
|
||||
{lastPostedDiscussion
|
||||
? (
|
||||
<Link className="TagTile-lastPostedDiscussion"
|
||||
href={app.route.discussion(lastPostedDiscussion, lastPostedDiscussion.lastPostNumber())}
|
||||
>
|
||||
<span className="TagTile-lastPostedDiscussion-title">{lastPostedDiscussion.title()}</span>
|
||||
{humanTime(lastPostedDiscussion.lastPostedAt())}
|
||||
</Link>
|
||||
{children ? (
|
||||
<div className="TagTile-children">
|
||||
{children.map((child) => [<Link href={app.route.tag(child)}>{child.name()}</Link>, ' '])}
|
||||
</div>
|
||||
) : (
|
||||
<span className="TagTile-lastPostedDiscussion"/>
|
||||
''
|
||||
)}
|
||||
</Link>
|
||||
{lastPostedDiscussion ? (
|
||||
<Link
|
||||
className="TagTile-lastPostedDiscussion"
|
||||
href={app.route.discussion(lastPostedDiscussion, lastPostedDiscussion.lastPostNumber())}
|
||||
>
|
||||
<span className="TagTile-lastPostedDiscussion-title">{lastPostedDiscussion.title()}</span>
|
||||
{humanTime(lastPostedDiscussion.lastPostedAt())}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="TagTile-lastPostedDiscussion" />
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
{cloud.length ? (
|
||||
<div className="TagCloud">
|
||||
{cloud.map(tag => [
|
||||
tagLabel(tag, {link: true}),
|
||||
' ',
|
||||
])}
|
||||
</div>
|
||||
) : ''}
|
||||
{cloud.length ? <div className="TagCloud">{cloud.map((tag) => [tagLabel(tag, { link: true }), ' '])}</div> : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,11 +15,11 @@ import addTagLabels from './addTagLabels';
|
||||
import addTagControl from './addTagControl';
|
||||
import addTagComposer from './addTagComposer';
|
||||
|
||||
app.initializers.add('flarum-tags', function() {
|
||||
app.routes.tags = {path: '/tags', component: TagsPage };
|
||||
app.routes.tag = {path: '/t/:tags', component: IndexPage };
|
||||
app.initializers.add('flarum-tags', function () {
|
||||
app.routes.tags = { path: '/tags', component: TagsPage };
|
||||
app.routes.tag = { path: '/t/:tags', component: IndexPage };
|
||||
|
||||
app.route.tag = (tag: Tag) => app.route('tag', {tags: tag.slug()});
|
||||
app.route.tag = (tag: Tag) => app.route('tag', { tags: tag.slug() });
|
||||
|
||||
app.postComponents.discussionTagged = DiscussionTaggedPost;
|
||||
|
||||
@ -37,7 +37,6 @@ app.initializers.add('flarum-tags', function() {
|
||||
addTagComposer();
|
||||
});
|
||||
|
||||
|
||||
// Expose compat API
|
||||
import tagsCompat from './compat';
|
||||
import { compat } from '@flarum/core/forum';
|
||||
|
@ -1,23 +1,19 @@
|
||||
import app from "flarum/forum/app";
|
||||
import type Tag from "../../common/models/Tag";
|
||||
import app from 'flarum/forum/app';
|
||||
import type Tag from '../../common/models/Tag';
|
||||
|
||||
export default class TagListState {
|
||||
loadedIncludes = new Set();
|
||||
|
||||
async load(includes: string[] = []): Promise<Tag[]> {
|
||||
const unloadedIncludes = includes.filter(
|
||||
(include) => !this.loadedIncludes.has(include)
|
||||
);
|
||||
const unloadedIncludes = includes.filter((include) => !this.loadedIncludes.has(include));
|
||||
|
||||
if (unloadedIncludes.length === 0) {
|
||||
return Promise.resolve(app.store.all<Tag>("tags"));
|
||||
return Promise.resolve(app.store.all<Tag>('tags'));
|
||||
}
|
||||
|
||||
return app.store
|
||||
.find<Tag[]>("tags", { include: unloadedIncludes.join(",") })
|
||||
.then((val) => {
|
||||
unloadedIncludes.forEach((include) => this.loadedIncludes.add(include));
|
||||
return val;
|
||||
});
|
||||
return app.store.find<Tag[]>('tags', { include: unloadedIncludes.join(',') }).then((val) => {
|
||||
unloadedIncludes.forEach((include) => this.loadedIncludes.add(include));
|
||||
return val;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ export default function getSelectableTags(discussion) {
|
||||
let tags = app.store.all('tags');
|
||||
|
||||
if (discussion) {
|
||||
tags = tags.filter(tag => tag.canAddToDiscussion() || discussion.tags().indexOf(tag) !== -1);
|
||||
tags = tags.filter((tag) => tag.canAddToDiscussion() || discussion.tags().indexOf(tag) !== -1);
|
||||
} else {
|
||||
tags = tags.filter(tag => tag.canStartDiscussion());
|
||||
tags = tags.filter((tag) => tag.canStartDiscussion());
|
||||
}
|
||||
|
||||
return tags;
|
||||
|
@ -2526,6 +2526,11 @@ prettier@^2.4.1, prettier@^2.5.1:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
|
||||
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
|
||||
|
||||
prettier@^2.7.1:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64"
|
||||
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
|
||||
|
||||
prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
|
Loading…
x
Reference in New Issue
Block a user