Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ on: [workflow_dispatch, push, pull_request]

jobs:
run:
uses: flarum/framework/.github/workflows/REUSABLE_backend.yml@1.x
uses: flarum/framework/.github/workflows/REUSABLE_backend.yml@2.x
with:
enable_backend_testing: true
enable_phpstan: true
php_versions: '["8.0", "8.1", "8.2", "8.3", "8.4]'

backend_directory: .
4 changes: 2 additions & 2 deletions .github/workflows/frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on: [workflow_dispatch, push, pull_request]

jobs:
run:
uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@1.x
uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@2.x
with:
enable_bundlewatch: false
enable_prettier: true
Expand All @@ -13,7 +13,7 @@ jobs:
frontend_directory: ./js
backend_directory: .
js_package_manager: yarn
main_git_branch: 1.x
main_git_branch: 2.x

secrets:
bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
18 changes: 10 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
}
],
"require": {
"flarum/core": "^1.8.3",
"fof/follow-tags": "^1.1.4"
"flarum/core": "^2.0.0",
"fof/follow-tags": "^2.0.0"
},
"authors": [
{
Expand Down Expand Up @@ -60,11 +60,11 @@
}
},
"require-dev": {
"flarum/phpstan": "*",
"flarum/testing": "*",
"flarum/approval": "*",
"fof/user-directory": "*",
"flarum/gdpr": "dev-main"
"flarum/phpstan": "^2.0.0",
"flarum/testing": "^2.0.0",
"flarum/approval": "^2.0.0",
"fof/user-directory": "^2.0.0",
"flarum/gdpr": "^2.0.0"
},
"autoload-dev": {
"psr-4": {
Expand All @@ -88,5 +88,7 @@
"test:integration": "Runs all integration tests.",
"test:setup": "Sets up a database for use with integration tests. Execute this only once.",
"analyse:phpstan": "Run static analysis"
}
},
"minimum-stability": "beta",
"prefer-stable": true
}
69 changes: 22 additions & 47 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,11 @@

namespace IanM\FollowUsers;

use Flarum\Api\Controller\ListUsersController;
use Flarum\Api\Controller\ShowForumController;
use Flarum\Api\Controller\ShowUserController;
use Flarum\Api\Serializer\BasicUserSerializer;
use Flarum\Api\Serializer\CurrentUserSerializer;
use Flarum\Api\Serializer\DiscussionSerializer;
use Flarum\Api\Serializer\UserSerializer;
use Flarum\Api\Endpoint;
use Flarum\Api\Resource;
use Flarum\Discussion\Event as DiscussionEvent;
use Flarum\Discussion\Filter\DiscussionFilterer;
use Flarum\Extend;
use Flarum\Gdpr\Extend\UserData;
use Flarum\Http\RequestUtil;
use Flarum\User\Event\Saving;
use Flarum\User\Filter\UserFilterer;
use Flarum\User\Search\UserSearcher;
use Flarum\User\User;

Expand All @@ -47,57 +38,37 @@
->namespace('ianm-follow-users', __DIR__.'/resources/views'),

(new Extend\Notification())
->type(Notifications\NewFollowerBlueprint::class, BasicUserSerializer::class, ['alert'])
->type(Notifications\NewUnfollowerBlueprint::class, BasicUserSerializer::class, ['alert'])
->type(Notifications\NewDiscussionBlueprint::class, DiscussionSerializer::class, ['alert', 'email'])
->type(Notifications\NewPostByUserBlueprint::class, DiscussionSerializer::class, ['alert', 'email']),
->type(Notifications\NewFollowerBlueprint::class, ['alert'])
->type(Notifications\NewUnfollowerBlueprint::class, ['alert'])
->type(Notifications\NewDiscussionBlueprint::class, ['alert', 'email'])
->type(Notifications\NewPostByUserBlueprint::class, ['alert', 'email']),

(new Extend\Event())
->listen(Saving::class, Listeners\SaveFollowedToDatabase::class)
->listen(DiscussionEvent\Deleted::class, Listeners\DeleteNotificationWhenDiscussionIsHiddenOrDeleted::class)
->listen(DiscussionEvent\Hidden::class, Listeners\DeleteNotificationWhenDiscussionIsHiddenOrDeleted::class)
->listen(DiscussionEvent\Restored::class, Listeners\RestoreNotificationWhenDiscussionIsRestored::class)
->subscribe(Listeners\QueueNotificationJobs::class),

(new Extend\Filter(DiscussionFilterer::class))
->addFilter(Query\FollowUsersDiscussionFilter::class),

(new Extend\Filter(UserFilterer::class))
->addFilter(Query\FollowedUsersFilterGambit::class),

(new Extend\SimpleFlarumSearch(UserSearcher::class))
->addGambit(Query\FollowedUsersFilterGambit::class),

(new Extend\User())
->registerPreference('blocksFollow', 'boolval', false),

(new Extend\Policy())
->modelPolicy(User::class, Access\UserPolicy::class),

(new Extend\ApiSerializer(CurrentUserSerializer::class))
->hasMany('followedUsers', UserSerializer::class),

(new Extend\ApiSerializer(BasicUserSerializer::class))
->attributes(Api\AddBasicUserAttributes::class),

(new Extend\ApiSerializer(UserSerializer::class))
->attributes(Api\AddUserAttributes::class),

(new Extend\ApiController(ListUsersController::class))
->prepareDataForSerialization(function (ListUsersController $controller, $data, $request) {
$actor = RequestUtil::getActor($request);
$actor->load('followedUsers');

return $data;
// API Resource extensions (Flarum 2.x)
(new Extend\ApiResource(Resource\UserResource::class))
->fields(Api\UserResourceFields::class)
->endpoint(Endpoint\Index::class, function (Endpoint\Index $endpoint) {
return $endpoint->addDefaultInclude(['followedUsers', 'followedBy']);
})
->addInclude(['followedUsers', 'followedBy']),
->endpoint(Endpoint\Show::class, function (Endpoint\Show $endpoint) {
return $endpoint->addDefaultInclude(['followedUsers', 'followedBy']);
}),

(new Extend\ApiController(ShowUserController::class))
->prepareDataForSerialization(Api\LoadRelations::class)
->addInclude(['followedUsers', 'followedBy']),

(new Extend\ApiController(ShowForumController::class))
->addInclude('actor.followedUsers'),
(new Extend\ApiResource(Resource\ForumResource::class))
->endpoint(Endpoint\Show::class, function (Endpoint\Show $endpoint) {
return $endpoint->addDefaultInclude(['actor.followedUsers']);
}),

(new Extend\Settings())
->default('ianm-follow-users.button-on-profile', false)
Expand All @@ -110,4 +81,8 @@
(new UserData())
->addType(Data\FollowUser::class),
]),

(new Extend\SearchDriver(\Flarum\Search\Database\DatabaseSearchDriver::class))
->addFilter(\Flarum\Discussion\Search\DiscussionSearcher::class, Query\FollowUsersDiscussionFilter::class)
->addFilter(UserSearcher::class, Query\FollowedUsersFilter::class),
];
25 changes: 16 additions & 9 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,27 @@
"private": true,
"prettier": "@flarum/prettier-config",
"dependencies": {
"flarum-webpack-config": "^2.0.0",
"css-what": "^5.1.0"
},
"devDependencies": {
"prettier": "^3.0.2",
"@flarum/prettier-config": "^1.0.0",
"flarum-tsconfig": "^1.0.2",
"css-what": "^5.1.0",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4"
"flarum-tsconfig": "^2.0.0",
"flarum-webpack-config": "^3.0.0",
"webpack": "^5.65.0",
"webpack-cli": "^5.0",
"typescript-coverage-report": "^0.6.1"
},
"scripts": {
"dev": "webpack --mode development --watch",
"build": "webpack --mode production",
"analyze": "cross-env ANALYZER=true npm run build",
"format": "prettier --write src",
"format-check": "prettier --check src"
},
"devDependencies": {
"prettier": "^3.2.4"
"format-check": "prettier --check src",
"clean-typings": "npx rimraf dist-typings && mkdir dist-typings",
"build-typings": "npm run clean-typings && ([ -e src/@types ] && cp -r src/@types dist-typings/@types || true) && tsc && npm 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"
}
}
2 changes: 2 additions & 0 deletions js/src/@types/shims.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import User from 'flarum/common/models/User';

declare module 'flarum/common/models/User' {
export default interface User {
followed(): boolean;
Expand Down
28 changes: 28 additions & 0 deletions js/src/admin/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import app from 'flarum/admin/app';
import Extend from 'flarum/common/extenders';
import commonExtend from '../common/extend';

export default [
...commonExtend,

new Extend.Admin() //
.permission(
() => ({
icon: 'fas fa-user-friends',
label: app.translator.trans('ianm-follow-users.admin.permissions.be_followed_label'),
permission: 'user.beFollowed',
}),
'reply',
95
)
.setting(() => ({
label: app.translator.trans('ianm-follow-users.admin.settings.button-on-profile-label'),
type: 'bool',
setting: 'ianm-follow-users.button-on-profile',
}))
.setting(() => ({
label: app.translator.trans('ianm-follow-users.admin.settings.stats-on-profile-label'),
type: 'bool',
setting: 'ianm-follow-users.stats-on-profile',
})),
];
34 changes: 0 additions & 34 deletions js/src/admin/index.js

This file was deleted.

17 changes: 17 additions & 0 deletions js/src/admin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import app from 'flarum/admin/app';
import addFollowingPageOption from 'ext:fof/follow-tags/common/utils/addFollowingPageOption';

export { default as extend } from './extend';

app.initializers.add(
'ianm-follow-users',
() => {
if ('fof-follow-tags' in flarum.extensions) {
// Register our "users" option with fof-follow-tags
addFollowingPageOption(() => ({
users: app.translator.trans('ianm-follow-users.lib.following_link'),
}));
}
},
-10 // Run before fof-follow-tags so our option is registered before the cache is populated
);
21 changes: 0 additions & 21 deletions js/src/common/FollowLevels.js

This file was deleted.

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

type TranslatorParameters = Record<string, unknown>;
type TranslationFunction = (opts?: TranslatorParameters) => any;

const trans =
(key: string): TranslationFunction =>
(opts) =>
app.translator.trans(`ianm-follow-users.lib.follow_levels.${key}`, opts ?? {});

export interface FollowLevel {
value: string;
name: TranslationFunction;
description: TranslationFunction;
}

export const FollowLevels: readonly FollowLevel[] = Object.freeze([
{
value: 'unfollow',
name: trans('unfollow.name'),
description: trans('unfollow.description'),
},
{
value: 'follow',
name: trans('follow.name'),
description: trans('follow.description'),
},
{
value: 'lurk',
name: trans('lurk.name'),
description: trans('lurk.description'),
},
]);
7 changes: 7 additions & 0 deletions js/src/common/extend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Extend from 'flarum/common/extenders';
import FollowedUsersGambit from './gambits/FollowedUsersGambit';

export default [
new Extend.Search() //
.gambit('users', FollowedUsersGambit),
];
12 changes: 12 additions & 0 deletions js/src/common/gambits/FollowedUsersGambit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import app from 'flarum/common/app';
import { BooleanGambit } from 'flarum/common/query/IGambit';

export default class FollowedUsersGambit extends BooleanGambit {
key() {
return app.translator.trans('ianm-follow-users.lib.gambits.followeduser.key', {}, true);
}

filterKey() {
return 'followeduser';
}
}
Loading
Loading