Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reactions revamp & profile widgets #3272

Merged
merged 65 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
6d476d8
Initial work on reactions revamp and profile widgets
tadhgboyle Mar 2, 2023
8e42301
Remove unneeded changes
tadhgboyle Mar 2, 2023
42efeaa
Remove unneeded change
tadhgboyle Mar 2, 2023
3ca3e3f
fix style
tadhgboyle Mar 2, 2023
8c052f2
Render emojis in reaction list StaffCP
tadhgboyle Mar 3, 2023
5e68508
wip minecraft account profile widget, sticky widgets on profile page
tadhgboyle Mar 3, 2023
611803d
Add server name and IP to last seen
tadhgboyle Mar 3, 2023
cb41d91
remove
tadhgboyle Mar 3, 2023
d9b5ccc
rename to reaction score to match member list
tadhgboyle Mar 3, 2023
5ee84fa
align reactions table contents center
tadhgboyle Mar 3, 2023
298d500
fix
tadhgboyle Mar 4, 2023
160b6c7
wip - nicer abstraction, widgets never query db, skin 3d viewer (unde…
tadhgboyle Mar 4, 2023
70f6188
wip - make reaction modals ajax, copy new reaction bar to profile posts
tadhgboyle Mar 4, 2023
a99a5a5
wip reaction modals
tadhgboyle Mar 4, 2023
d519967
wip proper sorting
tadhgboyle Mar 4, 2023
ea21033
wip modals
tadhgboyle Mar 4, 2023
32404c8
wip get reaction submission working on profile wall posts
tadhgboyle Mar 4, 2023
e822215
add back helpful and creative default reactions
tadhgboyle Mar 4, 2023
5faca3a
reduce query amount on view topic
tadhgboyle Mar 4, 2023
d6f5ccb
remove debug statement
tadhgboyle Mar 4, 2023
3fd4a9c
wip
tadhgboyle Mar 5, 2023
a8d7dec
wip
tadhgboyle Mar 5, 2023
9f3ac4b
simplify ProfileWidget pages
tadhgboyle Mar 5, 2023
d0f8748
Merge branch 'develop' into feat/reactions-revamp-profile-widgets
tadhgboyle Mar 13, 2023
f6eda0c
Revert forum dropdown
tadhgboyle Mar 13, 2023
366ba95
fix css syntax
tadhgboyle Mar 17, 2023
c09a891
fix emoji title and alt being strange
tadhgboyle Mar 17, 2023
99dceed
add back order input
tadhgboyle Mar 17, 2023
9b47a4b
sort widgets by order in list
tadhgboyle Mar 17, 2023
3c57958
fix settings link always showing
tadhgboyle Mar 17, 2023
7fc76bb
phpdoc
tadhgboyle Mar 19, 2023
709dd13
Merge branch 'develop' into feat/reactions-revamp-profile-widgets
tadhgboyle Mar 24, 2023
0346c55
Merge branch 'develop' into feat/reactions-revamp-profile-widgets
tadhgboyle Apr 4, 2023
58838b3
add widget name to exception
tadhgboyle Apr 4, 2023
5cd2e35
insert default widget data if not found
tadhgboyle Apr 4, 2023
48774fe
missing end if
tadhgboyle Apr 4, 2023
aac70d6
properly cache forum news per-group
tadhgboyle Apr 4, 2023
d322564
fix
tadhgboyle Apr 4, 2023
e37b529
fix cache key, news permission check
tadhgboyle Apr 4, 2023
f31c1fd
fix styling
tadhgboyle Apr 4, 2023
8971ca9
Merge branch 'develop' into feat/reactions-revamp-profile-widgets
tadhgboyle Apr 24, 2023
da2fcf7
Merge branch 'develop' into feat/reactions-revamp-profile-widgets
tadhgboyle Apr 27, 2023
5eeebd5
Merge branch 'develop' into feat/reactions-revamp-profile-widgets
tadhgboyle May 5, 2023
f27bb5b
Merge branch 'develop' into feat/reactions-revamp-profile-widgets
tadhgboyle Jun 9, 2023
88deadc
Merge branch 'develop' into feat/reactions-revamp-profile-widgets
tadhgboyle Jun 10, 2023
4066166
wip
tadhgboyle Jun 10, 2023
d472601
respect reaction ordering
tadhgboyle Jun 10, 2023
bdbf27b
wip
tadhgboyle Jun 10, 2023
63b73b3
wip
tadhgboyle Jun 10, 2023
3c8a00d
support custom reaction scores, update member list to properly calculate
tadhgboyle Jun 11, 2023
dd8b284
don't hardcode words
tadhgboyle Jun 11, 2023
b854422
wip
tadhgboyle Jun 12, 2023
810e636
allow multiple reactions
tadhgboyle Jun 13, 2023
fb9ce08
redirect to post
tadhgboyle Jun 13, 2023
e5e26ee
code style
tadhgboyle Jun 13, 2023
67d1987
phpstan
tadhgboyle Jun 13, 2023
0b51a97
Merge remote-tracking branch 'origin/develop' into feat/reactions-rev…
tadhgboyle Jun 13, 2023
b1d6f20
wip
tadhgboyle Jun 13, 2023
6acf141
remove hardcoded terms + fix copy
tadhgboyle Jun 14, 2023
6ba8619
use npm for skinview3d
tadhgboyle Jun 14, 2023
2365fb0
create ReactionContext and refactor
tadhgboyle Jun 14, 2023
111381a
fix cache
tadhgboyle Jun 15, 2023
c69a309
docblocks
tadhgboyle Jun 16, 2023
9b31ed1
fix spelling
tadhgboyle Jun 20, 2023
1f29029
pr amendments
tadhgboyle Jun 20, 2023
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
71 changes: 71 additions & 0 deletions core/classes/DTO/Reaction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
/**
* Represents a reaction.
*
* @package NamelessMC\DTO
* @author Aberdeener
* @version 2.1.0
* @license MIT
*/
class Reaction {

public const TYPE_POSITIVE = 2;
public const TYPE_NEGATIVE = 0;
public const TYPE_NEUTRAL = 1;
public const TYPE_CUSTOM = 3;
tadhgboyle marked this conversation as resolved.
Show resolved Hide resolved

public int $id;
public string $name;
public string $html;
public string $raw_html;
public bool $enabled;
public int $type;
public int $order;
public ?int $custom_score;

public function __construct(object $row) {
$this->id = $row->id;
$this->name = $row->name;
$this->html = Text::renderEmojis($row->html);
$this->raw_html = $row->html;
$this->enabled = $row->enabled;
$this->type = $row->type;
$this->custom_score = $row->custom_score;
$this->order = $row->order;
}

/**
* @return array<int, Reaction>
*/
public static function all(): array {
$rows = DB::getInstance()->query('SELECT * FROM nl2_reactions ORDER BY `order`')->results();
$fields = [];
foreach ($rows as $row) {
$fields[$row->id] = new Reaction($row);
}
return $fields;
}

/**
* @param string $value
* @param string $column
* @return array<int, Reaction>|Reaction
*/
public static function find(string $value, string $column = 'id') {
$rows = DB::getInstance()->query("SELECT * FROM nl2_reactions WHERE `$column` = ? ORDER BY `order`", [$value]);
if (!$rows->count()) {
return [];
}

if ($rows->count() === 1) {
return new Reaction($rows->first());
}

$fields = [];
foreach ($rows->results() as $row) {
$fields[$row->id] = new Reaction($row);
}

return $fields;
}
}
2 changes: 0 additions & 2 deletions core/classes/DTO/UserData.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class UserData {
public bool $active;
public ?string $signature;
public int $profile_views;
public int $reputation;
public ?string $reset_code;
public bool $has_avatar;
public bool $gravatar;
Expand Down Expand Up @@ -56,7 +55,6 @@ public function __construct(object $row) {
$this->active = $row->active;
$this->signature = $row->signature;
$this->profile_views = $row->profile_views;
$this->reputation = $row->reputation;
$this->reset_code = $row->reset_code;
$this->has_avatar = $row->has_avatar;
$this->gravatar = $row->gravatar;
Expand Down
25 changes: 21 additions & 4 deletions core/classes/Database/DB.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@ public function count(): int {
return $this->_count;
}

/**
* Get whether any results exist.
*
* @return bool Whether any results exist.
*/
public function exists(): bool {
return $this->_count > 0;
}

/**
* Get the last inserted ID
*
Expand All @@ -206,21 +215,29 @@ public function error(): bool {
* Perform a SELECT query on the database.
*
* @param string $table The table to select from.
* @param array $where The where clause.
* @param mixed $where The where clause. If not an array, it will be used for "id" column lookup.
* @return static|false This instance if successful, false otherwise.
*/
public function get(string $table, array $where = []) {
public function get(string $table, $where = []) {
if (!is_array($where)) {
$where = ['id', '=', $where];
}

return $this->action('SELECT *', $table, $where);
}

/**
* Perform a DELETE query on the database.
*
* @param string $table The table to delete from.
* @param array $where The where clause.
* @param mixed $where The where clause. If not an array, it will be used for "id" column lookup.
* @return static|false This instance if successful, false otherwise.
*/
public function delete(string $table, array $where) {
public function delete(string $table, $where) {
if (!is_array($where)) {
$where = ['id', '=', $where];
}

return $this->action('DELETE', $table, $where);
}

Expand Down
34 changes: 28 additions & 6 deletions core/classes/Database/DatabaseInitialiser.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,23 +155,45 @@ private function initialiseIntegrations(): void {
private function initialiseReactions(): void {
tadhgboyle marked this conversation as resolved.
Show resolved Hide resolved
$this->_db->insert('reactions', [
'name' => 'Like',
'html' => '<i class="fas fa-thumbs-up text-success"></i>',
'html' => '👍',
'enabled' => true,
'type' => 2
'type' => Reaction::TYPE_POSITIVE,
]);

$this->_db->insert('reactions', [
'name' => 'Dislike',
'html' => '<i class="fas fa-thumbs-down text-danger"></i>',
'html' => '👎',
'enabled' => true,
'type' => 0
'type' => Reaction::TYPE_NEGATIVE,
]);

$this->_db->insert('reactions', [
'name' => 'Meh',
'html' => '<i class="fas fa-meh text-warning"></i>',
'html' => '😐',
'enabled' => true,
'type' => 1
'type' => Reaction::TYPE_NEUTRAL,
]);

$this->_db->insert('reactions', [
'name' => 'Helpful',
'html' => '🛠️',
'enabled' => true,
'type' => Reaction::TYPE_POSITIVE,
]);

$this->_db->insert('reactions', [
'name' => 'Creative',
'html' => '🌈',
'enabled' => true,
'type' => Reaction::TYPE_POSITIVE,
]);

$this->_db->insert('reactions', [
'name' => 'Amazing',
'html' => '⭐',
'enabled' => true,
'type' => Reaction::TYPE_CUSTOM,
'custom_score' => 5,
]);
}

Expand Down
78 changes: 78 additions & 0 deletions core/classes/Misc/ProfilePostReactionContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php
/**
* Provides support for giving and receiving reactions on profile posts.
*
* @package NamelessMC\Misc
* @author Aberdeener
* @version 2.2.0
* @license MIT
* @see ReactionContext, ReactionContextsManager
*/
class ProfilePostReactionContext extends ReactionContext {

public function name(): string {
return 'profile_post';
}

public function friendlyName(Language $language): string {
return $language->get('user', 'profile_posts_score');
}

public function getUserReceived(User $user): array {
return DB::getInstance()->query('SELECT r.reaction_id FROM nl2_user_profile_wall_posts_reactions r JOIN nl2_user_profile_wall_posts w ON r.post_id = w.id WHERE w.author_id = ?', [
$user->data()->id
])->results();
}

public function getUserGiven(User $user): array {
return DB::getInstance()->get('user_profile_wall_posts_reactions', ['user_id', $user->data()->id])->results();
}

public function validateReactable(int $reactable_id, User $user) {
// TODO check blocked?
samerton marked this conversation as resolved.
Show resolved Hide resolved
$result = DB::getInstance()->get('user_profile_wall_posts', ['id', $reactable_id]);

if ($result->exists()) {
return $result->first();
}

return false;
}

public function hasReacted(User $user, Reaction $reaction, int $reactable_id) {
$result = DB::getInstance()->get('user_profile_wall_posts_reactions', [
['post_id', $reactable_id], ['user_id', $user->data()->id], ['reaction_id', $reaction->id]
]);

if ($result->exists()) {
return $result->first()->id;
}

return false;
}

public function giveReaction(User $user, User $receiver, Reaction $reaction, int $reactable_id): void {
DB::getInstance()->insert('user_profile_wall_posts_reactions', [
'post_id' => $reactable_id,
'user_id' => $user->data()->id,
'reaction_id' => $reaction->id,
'time' => date('U'),
]);
}

public function deleteReaction(int $reactable_reaction_id): void {
DB::getInstance()->delete('user_profile_wall_posts_reactions', $reactable_reaction_id);
}

public function getAllReactions(int $reactionable_id): array {
return DB::getInstance()->get('user_profile_wall_posts_reactions', ['post_id', $reactionable_id])->results();
}

public function reactionUserIdColumn(): string {
return 'user_id';
}

public function determineReceiver(object $reactable): User {
return new User(DB::getInstance()->get('user_profile_wall_posts', ['id', $reactable->id])->first()->author_id);
}
}
107 changes: 107 additions & 0 deletions core/classes/Misc/ReactionContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php
/**
* Represents a context in which reactions can be given and received.
*
* @package NamelessMC\Misc
* @author Aberdeener
* @version 2.2.0
* @license MIT
*/
abstract class ReactionContext {

/**
* @return string Name of the reaction context which should be used when calling the `/queries/reactions` endpoint as the `context` parameter.
*/
abstract public function name(): string;

/**
* @param Language $language Language to get the friendly name in.
* @return string Friendly name of the reaction context. Used when displaying scores in the Reactions profile widget
*/
abstract public function friendlyName(Language $language): string;

/**
* @return bool Whether this reaction context is enabled. If false, the reaction context will not be available for use.
*/
public function isEnabled(): bool {
return true;
}

/**
* Get all the reactions that the user has received in this context.
* This should return data from the contexts table which contains the reactionable ID,
* the reaction ID, and the user ID of the user who gave the reaction.
*
* @param User $user User to get the reactions for.
* @return array Array of reactions that the user has received in this context.
*/
abstract public function getUserReceived(User $user): array;

/**
* Get all the reactions that the user has given in this context.
* This should return data from the contexts table which contains the reactionable ID,
* the reaction ID, and the user ID of the user who gave the reaction.
*
* @param User $user User to get the reactions for.
* @return array Array of reactions that the user has given in this context.
*/
abstract public function getUserGiven(User $user): array;

/**
* Determine whether the user can react to the given reactable.
* Reactable meaning some model that can be reacted to: post, profile post, resource, etc.
*
* @return false|object Returns false if the reactable does not exist or is not viewable to the user, otherwise returns the reactable object.
*/
abstract public function validateReactable(int $reactable_id, User $user);

/**
* Determine whether the user has reacted to the given reactable.
*
* @return false|int Returns false if the user has not reacted, otherwise returns the reactable reaction ID.
*/
abstract public function hasReacted(User $user, Reaction $reaction, int $reactable_id);

/**
* Record a reaction being given for a given reactable.
*
* @param User $user User who is giving the reaction.
* @param User $receiver User who is receiving the reaction (generally the owner of the reactionable object).
* @param Reaction $reaction Reaction to give.
* @param int $reactable_id ID of the reactable that the user is reacting to.
*/
abstract public function giveReaction(User $user, User $receiver, Reaction $reaction, int $reactable_id): void;

/**
* Delete a reaction given in this context.
*
* @param int $reactable_reaction_id ID of the row in the contexts reactions table to delete.
*/
abstract public function deleteReaction(int $reactable_reaction_id): void;

/**
* Get all the reactions that have been given to the given reactionable.
*
* @param int $reactionable_id ID of the reactionable to get the reactions for.
* @return array Array of reactions that have been given to the given reactionable.
*/
abstract public function getAllReactions(int $reactionable_id): array;

/**
* Name of the column which contains the owner of the reactionable object in the reactionables reaction table.
* For example, the `profile_wall_posts_reaction` table stores the ID of the user who added the reaction as the `user_id`,
* whereas `forum_post_reactions` stores the ID of the user who added the reaction as the `user_given`.
*
* @return string Name of the column
*/
abstract public function reactionUserIdColumn(): string;

/**
* Determine which User owns the given reactable.
* Used to give them a reaction point.
*
* @param object $reactable Reactable object to get the receiver for.
* @return User User who owns the given reactable.
*/
abstract public function determineReceiver(object $reactable): User;
}
Loading