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 58 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
77 changes: 77 additions & 0 deletions core/classes/DTO/Reaction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?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 const CONTEXTS = [
'forum_post',
'profile_post',
// Allow modules to register custom contexts?
];

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
16 changes: 12 additions & 4 deletions core/classes/Database/DB.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,21 +206,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
159 changes: 159 additions & 0 deletions core/classes/Widgets/AbstractWidget.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php

abstract class AbstractWidget {

protected string $_name;
protected string $_content;
protected string $_description;
protected string $_module;
protected ?string $_settings = null;
protected bool $_requires_cookies = false;
protected Smarty $_smarty;
protected WidgetData $_data;

/**
* Get the name of this widget.
*
* @return string Name of widget.
*/
public function getName(): string {
return $this->_name;
}

/**
* Get the location (`left` or `right`) that this widget will be displayed on.
*
* @return string Location of widget.
*/
public function getLocation(): string {
return $this->getData()->location;
}

/**
* Get the path to the file for settings of this widget.
*
* @return string Widget settings URL.
*/
public function getSettings(): ?string {
return $this->_settings;
}

/**
* Get the description of this widget.
*
* @return string Description of widget.
*/
public function getDescription(): string {
return $this->_description;
}

/**
* Get the module of this widget.
*
* @return string Name of module.
*/
public function getModule(): string {
return $this->_module;
}

/**
* Get the display order of this widget.
*
* @return int Display order of widget.
*/
public function getOrder(): int {
return $this->getData()->order;
}

/**
* Get Smarty instance in use by this widget.
*
* @return Smarty Instance in use.
*/
public function getSmarty(): ?Smarty {
return $this->_smarty;
}

/**
* Render this widget to be displayed on a template page. If this widget requires cookies and cookies are not allowed, a cookie notice is displayed instead.
* Returns an empty string if this widget is not to be displayed.
*
* @throws Exception
* @throws SmartyException
*
* @return string Content/HTML of this widget.
*/
public function display(): string {
if (defined('COOKIE_CHECK') && !COOKIES_ALLOWED && $this->_requires_cookies) {
return $this->_smarty->fetch('widgets/cookie_notice.tpl');
}

return $this->_content;
}

/**
* Get pages this widget is enabled on.
*
* @return array Pages this widget is enabled on.
*/
abstract public function getPages(): array;

/**
* Clear the cache for this widget, should be called when any settings of it are changed.
*/
final public function clearCache(): void {
$cache = new Cache();
tadhgboyle marked this conversation as resolved.
Show resolved Hide resolved
$cache->setCache(
$this instanceof ProfileWidgetBase
? 'profile_widgets'
: 'widgets'
);

$cache->erase($this->getName());
}

/**
* Get widget data.
* Will use cache if available, otherwise will query the database and store the result in cache.
*
* @return WidgetData Widget data.
*/
protected function getData(): WidgetData {
if (isset($this->_data)) {
return $this->_data;
}

$cache = new Cache();
tadhgboyle marked this conversation as resolved.
Show resolved Hide resolved
$cache->setCache(
$this instanceof ProfileWidgetBase
? 'profile_widgets'
: 'widgets'
);

if ($cache->isCached($this->getName())) {
return $this->_data = new WidgetData($cache->retrieve($this->getName()));
}

$row = DB::getInstance()->get('widgets', ['name', $this->getName()]);
if ($row->count()) {
$data = new WidgetData($row->first());
$cache->store($this->getName(), $data);

return $this->_data = $data;
}

// Widget not found in database, create it
DB::getInstance()->insert('widgets', $data = [
'name' => $this->getName(),
'enabled' => true,
'location' => 'right',
'order' => 10,
'pages' => '["index","forum"]',
]);

$data = new WidgetData((object) $data);
$cache->store($this->getName(), $data);

return $this->_data = $data;
}
}
10 changes: 10 additions & 0 deletions core/classes/Widgets/ProfileWidgetBase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

abstract class ProfileWidgetBase extends AbstractWidget {

abstract public function initialise(User $user): void;

final public function getPages(): array {
return ['profile'];
}
}
Loading