Skip to content

Commit

Permalink
feat: Localized notifications based on discussion language (#44)
Browse files Browse the repository at this point in the history
* feat: Allow users to recieve notifications only about the discussions in the language of their choice

* Apply fixes from StyleCI

* fix: LanguageDropdown not displaying the label correctly when a tag language is set

* fix: LanguageDropdown not updating it's state correctly, move all option entirely to the backend

* Apply fixes from StyleCI

---------

Co-authored-by: StyleCI Bot <[email protected]>
  • Loading branch information
imorland and StyleCIBot authored Sep 28, 2023
1 parent a59585d commit aa44b34
Show file tree
Hide file tree
Showing 33 changed files with 599 additions and 176 deletions.
19 changes: 14 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,22 @@
}
],
"require": {
"flarum/core": "^1.3.1",
"fof/extend": "^1.0.0",
"flarum/core": "^1.8.1",
"rinvex/countries": "^6.1.2 || ^7.0.1 || ^8.1.0",
"league/csv": "^9.7",
"ianm/iso-639": "^1.0"
"ianm/iso-639": "^1.0",
"flarum/tags": "*"
},
"authors": [
{
"name": "David Sevilla Martin",
"email": "[email protected]",
"role": "Developer"
},
{
"name": "IanM",
"email": "[email protected]",
"role": "Developer"
}
],
"autoload": {
Expand All @@ -47,12 +52,16 @@
"color": "#fff"
},
"optional-dependencies": [
"flarum/tags",
"fof/byobu"
"fof/byobu",
"fof/follow-tags"
]
},
"flagrow": {
"discuss": "https://discuss.flarum.org/d/23702"
}
},
"require-dev": {
"fof/byobu": "*",
"fof/follow-tags": "*"
}
}
23 changes: 15 additions & 8 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Flarum\Tags\Event\Creating as TagCreating;
use FoF\DiscussionLanguage\Api\Serializers\DiscussionLanguageSerializer;
use FoF\DiscussionLanguage\Api\Serializers\TagLocalizedLastDiscussionSerializer;
use FoF\FollowTags\Event\SubscriptionChanging;

return [
(new Extend\Frontend('forum'))
Expand Down Expand Up @@ -75,14 +76,8 @@
}),

(new Extend\ApiController(ShowForumController::class))
->prepareDataForSerialization(function (ShowForumController $controller, &$data) {
// Expose the complete tag list to clients by adding it as a
// relationship to the /api endpoint. Since the Forum model
// doesn't actually have a tags relationship, we will manually load and
// assign the tags data to it using an event listener.
$data['discussionLanguages'] = DiscussionLanguage::get();
})
->addInclude(['discussionLanguages']),
->addInclude(['discussionLanguages'])
->prepareDataForSerialization(LoadForumDiscussionLanguageRelationship::class),

(new Extend\ApiController(ListDiscussionsController::class))
->addInclude(['language']),
Expand All @@ -107,4 +102,16 @@

(new Extend\ApiSerializer(TagSerializer::class))
->attributes(TagLocalizedLastDiscussionSerializer::class),

(new Extend\Conditional())
->whenExtensionEnabled('fof-follow-tags', [
(new Extend\ApiSerializer(TagSerializer::class))
->attributes(AddTagSerializerAttributes::class),

(new Extend\Event())
->listen(SubscriptionChanging::class, Listener\SaveLanguageOnSubscription::class),

(new Extend\Notification())
->beforeSending(CheckNotificationRecipients::class),
]),
];
3 changes: 3 additions & 0 deletions js/src/admin/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import commonExtend from '../common/extend';

export default [...commonExtend];
23 changes: 6 additions & 17 deletions js/src/admin/index.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,27 @@
import app from 'flarum/admin/app';
import { extend } from 'flarum/common/extend';
import Forum from 'flarum/common/models/Forum';
import PermissionGrid from 'flarum/admin/components/PermissionGrid';
import SettingDropdown from 'flarum/admin/components/SettingDropdown';

import LanguagesSettingsPage from './components/LanguagesSettingsPage';
import Language from '../common/models/Language';

export * from './components';
export * from './utils';
export * from '../common/models';
export { default as extend } from './extend';

app.initializers.add('fof/discussion-language', () => {
app.store.models['discussion-languages'] = Language;

Forum.prototype.discussionLanguages = Forum.hasMany('discussionLanguages');

app.extensionData
.for('fof-discussion-language')
.registerPage(LanguagesSettingsPage)
.registerPermission(
{
permission: 'discussion.changeLanguageModerate',
icon: 'fas fa-globe',
permission: 'discussion.changeLanguage',
label: app.translator.trans('fof-discussion-language.admin.permissions.allow_change_language_label'),
},
'moderate',
65
);

// This extend must remain for now, as the new admin UI in beta 15 does not currently support the custom dropdown
extend(PermissionGrid.prototype, 'startItems', (items) => {
items.add(
'allowLanguageChange',
)
.registerPermission(
{
icon: 'fas fa-globe',
label: app.translator.trans('fof-discussion-language.admin.permissions.allow_change_language_label'),
Expand All @@ -52,7 +41,7 @@ app.initializers.add('fof/discussion-language', () => {
});
},
},
90
'start',
65
);
});
});
21 changes: 21 additions & 0 deletions js/src/common/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Extend from 'flarum/common/extenders';
import Language from '../common/models/Language';
import Discussion from 'flarum/common/models/Discussion';
import Forum from 'flarum/common/models/Forum';
import Tag from 'flarum/tags/common/models/Tag';

export default [
new Extend.Store() //
.add('discussion-languages', Language),

new Extend.Model(Discussion) //
.hasOne('language')
.attribute<boolean>('canChangeLanguage'),

new Extend.Model(Forum) //
.hasMany<Language>('discussionLanguages'),

new Extend.Model(Tag) //
.attribute<string>('subscriptionLanguage')
.attribute<string>('localisedLastDiscussion'),
];
13 changes: 0 additions & 13 deletions js/src/common/models/Language.js

This file was deleted.

27 changes: 27 additions & 0 deletions js/src/common/models/Language.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Model from 'flarum/common/Model';

export default class Language extends Model {
id() {
return Model.attribute<string>('id').call(this);
}

code() {
return Model.attribute<string>('code').call(this);
}

country() {
return Model.attribute<string>('country').call(this);
}

name() {
return Model.attribute<string>('name').call(this);
}

emoji() {
return Model.attribute<string>('emoji').call(this);
}

apiEndpoint() {
return `/fof/discussion-language${this.exists ? `/${this.data.id}` : ''}`;
}
}
File renamed without changes.
2 changes: 1 addition & 1 deletion js/src/common/utils/flag.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default (language) => {
className="emoji"
draggable="false"
loading="lazy"
src={`//cdn.jsdelivr.net/gh/twitter/twemoji@13/assets/72x72/${basename(emoji)}.png`}
src={`//cdn.jsdelivr.net/gh/twitter/twemoji@14/assets/72x72/${basename(emoji)}.png`}
/>
) : (
icon('fas fa-globe')
Expand Down
6 changes: 3 additions & 3 deletions js/src/forum/addLanguageComposer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import IndexPage from 'flarum/forum/components/IndexPage';
import DiscussionComposer from 'flarum/forum/components/DiscussionComposer';

import LanguageDiscussionModal from './components/LanguageDiscussionModal';
import Language from './components/Language';
import LanguageDisplay from './components/LanguageDisplay';

const sort = (a, b) => a.code().toLowerCase() > b.code().toLowerCase();

Expand All @@ -16,7 +16,7 @@ export default () => {
promise.then((composer) => (composer.fields.language = app.store.getBy('discussion-languages', 'code', dislang)));
} else {
const localeComposer = app.forum.attribute('fof-discussion-language.composerLocaleDefault');
app.composer.fields.language = localeComposer ? app.store.getBy('discussion-languages', 'code', app.translator.formatter.locale) : '';
app.composer.fields.language = localeComposer ? app.store.getBy('discussion-languages', 'code', app.translator.getLocale()) : '';
}
});

Expand All @@ -42,7 +42,7 @@ export default () => {
<a className="DiscussionComposer-changeTags" onclick={this.chooseLanguage.bind(this, true, null)}>
<span className={`LanguageLabel ${this.composer.fields.language ? '' : 'none'}`}>
{this.composer.fields.language
? Language.component({ language: this.composer.fields.language, uppercase: true })
? LanguageDisplay.component({ language: this.composer.fields.language, uppercase: true })
: app.translator.trans('fof-discussion-language.forum.composer_discussion.choose_language_link')}
</span>
</a>,
Expand Down
25 changes: 10 additions & 15 deletions js/src/forum/addLanguageToDiscussionList.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default () => {
}

const paramLang = app.search.params().language;
const locale = app.search.params().language ?? app.translator.formatter.locale;
const locale = app.search.params().language ?? app.translator.getLocale();
const showAnyOpt = app.forum.attribute('fof-discussion-language.showAnyLangOpt');

if (params.filter.q) {
Expand All @@ -75,29 +75,24 @@ export default () => {
// Don't add language controls to /private (fof/byobu)
if (app.current.data.routeName === 'byobuPrivate') return;

let extra, defaultSelected;
if (app.forum.attribute('fof-discussion-language.showAnyLangOpt')) {
extra = { any: app.translator.trans('fof-discussion-language.forum.index_language.any') };
defaultSelected = 'any';
} else {
defaultSelected = app.translator.formatter.locale;
}
const defaultSelected = app.forum.attribute('fof-discussion-language.showAnyLangOpt') ? 'any' : app.translator.getLocale();

items.add(
'language',
LanguageDropdown.component({
extra,
default: defaultSelected,
onclick: (key) => {
<LanguageDropdown
selected={app.search.params().language}
onclick={(key) => {
// if `key` is not a string, return early
if (typeof key !== 'string') return;

const params = app.search.params();

if (key === defaultSelected) delete params.language;
else params.language = key;

setRouteWithForcedRefresh(app.route(app.current.get('routeName'), params));
},
selected: app.search.params().language,
})
}}
/>
);
});
};
33 changes: 0 additions & 33 deletions js/src/forum/components/Language.js

This file was deleted.

4 changes: 2 additions & 2 deletions js/src/forum/components/LanguageDiscussionModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Modal from 'flarum/common/components/Modal';
import Button from 'flarum/common/components/Button';
import DiscussionPage from 'flarum/forum/components/DiscussionPage';

import Language from './Language';
import LanguageDisplay from './LanguageDisplay';

export default class LanguageDiscussionModal extends Modal {
oninit(vnode) {
Expand Down Expand Up @@ -31,7 +31,7 @@ export default class LanguageDiscussionModal extends Modal {
<div className="Form-group">
{this.languages.map((language) => (
<Button onclick={this.select.bind(this, language)} className={`Button Button--block ${this.selected === language ? 'active' : ''}`}>
<Language language={language} uppercase={true} />
<LanguageDisplay language={language} uppercase={true} />
</Button>
))}
</div>
Expand Down
33 changes: 33 additions & 0 deletions js/src/forum/components/LanguageDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import app from 'flarum/forum/app';
import Component from 'flarum/common/Component';
import flag from '../../common/utils/flag';
import Language from '../../common/models/Language';
import type Mithril from 'mithril';
import generateLanguageOptions from '../utils/generateLanguageOptions';

interface LanguageDisplayAttrs {
language: Language;
uppercase?: boolean;
extra?: Record<string, Mithril.Children>;
}

export default class LanguageDisplay extends Component<LanguageDisplayAttrs> {
options!: Record<string, Mithril.Children>;

oninit(vnode: Mithril.Vnode<LanguageDisplayAttrs, this>): void {
super.oninit(vnode);

this.options = generateLanguageOptions(this.attrs.extra);
}

view(): Mithril.Child {
const { language, uppercase } = this.attrs;
const name = language.name() || '';

return (
<span>
{flag(language)} {uppercase ? name.toUpperCase() : name}
</span>
);
}
}
Loading

0 comments on commit aa44b34

Please sign in to comment.