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 61 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` = $value ORDER BY `order`");
tadhgboyle marked this conversation as resolved.
Show resolved Hide resolved
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
70 changes: 70 additions & 0 deletions core/classes/Misc/ProfilePostReactionContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

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);
}
}
36 changes: 36 additions & 0 deletions core/classes/Misc/ReactionContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

abstract class ReactionContext {

abstract public function name(): string;

abstract public function friendlyName(Language $language): string;

public function isEnabled(): bool {
return true;
}

abstract public function getUserReceived(User $user): array;

abstract public function getUserGiven(User $user): array;

/**
* @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);

/**
* @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);

abstract public function giveReaction(User $user, User $receiver, Reaction $reaction, int $reactable_id): void;

abstract public function deleteReaction(int $reactable_reaction_id): void;

abstract public function getAllReactions(int $reactionable_id): array;

abstract public function reactionUserIdColumn(): string;

abstract public function determineReceiver(object $reactable): User;
}
35 changes: 35 additions & 0 deletions core/classes/Misc/ReactionContextsManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

class ReactionContextsManager extends Instanceable {

/** @var ReactionContext[] */
private array $_contexts = [];

public function provideContext(ReactionContext $context): void {
$this->_contexts[$context->name()] = $context;
}

public function getContext(string $name): ?ReactionContext {
if (!isset($this->_contexts[$name])) {
throw new InvalidArgumentException('Invalid reaction context name: ' . $name);
}

return $this->_contexts[$name];
}

public function getContexts(): array {
return $this->_contexts;
}

public function validContextNames(): array {
return array_map(static function (ReactionContext $context) {
return $context->name();
}, $this->_contexts);
}

public function validContextFriendlyNames(Language $language): array {
return array_map(static function (ReactionContext $context) use ($language) {
return $context->friendlyName($language);
}, $this->_contexts);
}
}
Loading