-
-
- {icon('fas fa-users')}
- {app.translator.trans('fof-gamification.forum.voters.label')}
-
- {voters.length === 0
- ? app.translator.trans('fof-gamification.forum.voters.label_none')
- : app.translator.trans('fof-gamification.forum.voters.label')}
-
+
+
+
+ {icon('fas fa-users')}
+ {app.translator.trans('fof-gamification.forum.voters.label')}
+
+ {voters.length === 0
+ ? app.translator.trans('fof-gamification.forum.voters.label_none')
+ : app.translator.trans('fof-gamification.forum.voters.label')}
-
-
- {voters.length === 0 ? app.translator.trans('fof-gamification.forum.voters.none') : null}
-
-
- {voters.slice(0, max).map((user: any) => (
-
- {avatar(user)}
-
- ))}
- {voters.length > max ? (
-
- {`+${voters.length - max}`}
-
- ) : null}
-
+
+
+
+ {voters.length === 0 ? app.translator.trans('fof-gamification.forum.voters.none') : null}
+
+ {voters.slice(0, max).map((user: any) => (
+
+ {avatar(user)}
+
+ ))}
+ {voters.length > max ? (
+
+ {`+${voters.length - max}`}
+
+ ) : null}
+
+
+ );
+ }
+
+ buildVoters(voters: User[], max: number) {
+ if (voters.length === 0) {
+ return
{app.translator.trans('fof-gamification.forum.voters.none')}
;
+ }
+ return (
+
+ {voters.slice(0, max).map((user: User) => (
+
+ {avatar(user)}
+
+ ))}
+ {voters.length > max ? (
+
+ {`+${voters.length - max}`}
+
+ ) : null}
);
}
diff --git a/js/src/forum/components/VotingControl.tsx b/js/src/forum/components/VotingControl.tsx
new file mode 100644
index 0000000..f76654c
--- /dev/null
+++ b/js/src/forum/components/VotingControl.tsx
@@ -0,0 +1,68 @@
+import app from 'flarum/forum/app';
+import Component from 'flarum/common/Component';
+import Post from 'flarum/common/models/Post';
+import Button from 'flarum/common/components/Button';
+import abbreviateNumber from 'flarum/common/utils/abbreviateNumber';
+import LoadingIndicator from 'flarum/common/components/LoadingIndicator';
+import setting from '../helpers/setting';
+import saveVote from '../helpers/saveVote';
+import Tooltip from 'flarum/common/components/Tooltip';
+
+interface IAttrs {
+ post: Post;
+ voteLoading: boolean;
+}
+
+export default class VotingControl extends Component
{
+ view() {
+ const post = this.attrs.post;
+
+ const hasDownvoted = post.hasDownvoted();
+ const hasUpvoted = post.hasUpvoted();
+
+ const icon = setting('iconName') || 'thumbs';
+ const upvotesOnly = setting('upVotesOnly', true);
+
+ const canSeeVotes = post.canSeeVotes();
+
+ // We set canVote to true for guest users so that they can access the login by clicking the button
+ const canVote = !app.session.user || post.canVote();
+
+ const onclick = (upvoted: boolean, downvoted: boolean) =>
+ saveVote(post, upvoted, downvoted, (val: boolean) => {
+ this.attrs.voteLoading = val;
+ });
+
+ return (
+
+
+
+
+ {abbreviateNumber(post.votes() || 0)}
+
+ {!upvotesOnly && (
+
+
+ )}
+
+ {this.attrs.voteLoading && }
+
+ );
+ }
+}
diff --git a/js/src/forum/components/VotingWidget.tsx b/js/src/forum/components/VotingWidget.tsx
new file mode 100755
index 0000000..e97f9c3
--- /dev/null
+++ b/js/src/forum/components/VotingWidget.tsx
@@ -0,0 +1,48 @@
+import Component from 'flarum/common/Component';
+import Post from 'flarum/common/models/Post';
+import ItemList from 'flarum/common/utils/ItemList';
+import type Mithril from 'mithril';
+import Voters from './Voters';
+import VotingControl from './VotingControl';
+
+interface IAttrs {
+ post: Post;
+}
+
+export default class VotingWidget extends Component {
+ loading: boolean = false;
+
+ view() {
+ return (
+
+
{this.headerItems().toArray()}
+
+
{this.voteActionItems().toArray()}
+
{this.votersItems().toArray()}
+
+
+ );
+ }
+
+ headerItems() {
+ const items = new ItemList();
+
+ return items;
+ }
+
+ voteActionItems() {
+ const items = new ItemList();
+
+ items.add('vote-control', , 100);
+
+ return items;
+ }
+
+ votersItems() {
+ const items = new ItemList();
+
+ items.add('upvotes', , 100);
+
+ return items;
+ }
+}
diff --git a/js/src/forum/components/index.js b/js/src/forum/components/index.ts
similarity index 67%
rename from js/src/forum/components/index.js
rename to js/src/forum/components/index.ts
index 23244ad..cf498b5 100644
--- a/js/src/forum/components/index.js
+++ b/js/src/forum/components/index.ts
@@ -2,10 +2,14 @@ import RankingsPage from './RankingsPage';
import VoteNotification from './VoteNotification';
import VotesModal from './VotesModal';
import Voters from './Voters';
+import VotingControl from './VotingControl';
+import VotingWidget from './VotingWidget';
export const components = {
RankingsPage,
VoteNotification,
VotesModal,
Voters,
+ VotingControl,
+ VotingWidget,
};
diff --git a/js/src/forum/helpers/index.js b/js/src/forum/helpers/index.ts
similarity index 100%
rename from js/src/forum/helpers/index.js
rename to js/src/forum/helpers/index.ts
diff --git a/js/src/forum/index.js b/js/src/forum/index.js
old mode 100755
new mode 100644
index 71df7a4..0b1f4e0
--- a/js/src/forum/index.js
+++ b/js/src/forum/index.js
@@ -17,9 +17,8 @@ import addAlternateLayout from './addAlternateLayout';
import setting from './helpers/setting';
import addVotesSort from './addVotesSort';
-import useAlternatePostVoteLayout from './useAlternatePostVoteLayout';
import addNotifications from './addNotifications';
-import addVotersToDiscussionPageSideBar from './addVotersToDiscussionPageSideBar';
+import addVotersToEligiblePosts from './addVotersToEligiblePosts';
import addUpvoteTabToUserProfile from './addUpvoteTabToUserProfile';
app.initializers.add('fof-gamification', () => {
@@ -54,16 +53,12 @@ app.initializers.add('fof-gamification', () => {
addUpvotesToDiscussion();
addPusher();
addNotifications();
- addVotersToDiscussionPageSideBar();
+ addVotersToEligiblePosts();
addUpvoteTabToUserProfile();
if (setting('useAlternateLayout', true)) {
addAlternateLayout();
}
-
- if (setting('altPostVotingUi', true)) {
- useAlternatePostVoteLayout();
- }
});
export * from './components';
diff --git a/js/src/forum/useAlternatePostVoteLayout.tsx b/js/src/forum/useAlternatePostVoteLayout.tsx
deleted file mode 100644
index c9dd6c0..0000000
--- a/js/src/forum/useAlternatePostVoteLayout.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import app from 'flarum/forum/app';
-
-import { extend } from 'flarum/common/extend';
-
-import CommentPost from 'flarum/forum/components/CommentPost';
-import Button from 'flarum/common/components/Button';
-import abbreviateNumber from 'flarum/common/utils/abbreviateNumber';
-import LoadingIndicator from 'flarum/common/components/LoadingIndicator';
-import type ItemList from 'flarum/common/utils/ItemList';
-
-import setting from './helpers/setting';
-import saveVote from './helpers/saveVote';
-
-export default function useAlternatePostVoteLayout() {
- extend(CommentPost.prototype, 'actionItems', function (this: CommentPost, items: ItemList) {
- if (this.attrs.post.isHidden()) return;
-
- items.remove('votes');
- });
-
- extend(CommentPost.prototype, 'classes', function (this: CommentPost, classes: string[]) {
- if (this.attrs.post.isHidden()) return;
-
- const upvotesOnly = setting('upVotesOnly', true);
-
- classes.push('votesAlternativeLayout');
-
- if (upvotesOnly) {
- classes.push('votesUpvotesOnly');
- }
- });
-
- extend(CommentPost.prototype, 'headerItems', function (this: CommentPost, items: ItemList) {
- const post = this.attrs.post;
-
- if (post.isHidden()) return;
- if (!post.canSeeVotes()) return;
-
- const hasDownvoted = post.hasDownvoted();
- const hasUpvoted = post.hasUpvoted();
-
- const icon = setting('iconName') || 'thumbs';
- const upvotesOnly = setting('upVotesOnly', true);
-
- const canSeeVotes = post.canSeeVotes();
-
- // We set canVote to true for guest users so that they can access the login by clicking the button
- const canVote = !app.session.user || post.canVote();
-
- const onclick = (upvoted, downvoted) =>
- saveVote(post, upvoted, downvoted, (val) => {
- this.voteLoading = val;
- });
-
- items.add(
- 'votes',
-
-
,
- 10000
- );
- });
-}
diff --git a/resources/less/forum/VotesBox.less b/resources/less/forum/VotesBox.less
index 8973a73..3fd4801 100644
--- a/resources/less/forum/VotesBox.less
+++ b/resources/less/forum/VotesBox.less
@@ -1,5 +1,5 @@
.VotingContainer {
- padding: 12px;
+ padding: 2px;
background: @control-bg;
border-radius: @border-radius;
@@ -36,7 +36,7 @@
}
&-icon {
- font-size: 0.9rem;
+ font-size: 0.7rem;
vertical-align: middle;
}
@@ -68,7 +68,8 @@
}
&-list {
- display: grid;
+ display: inline-flex;
+ flex-wrap: wrap;
grid-template-columns: repeat(auto-fill, minmax(16px, 1fr));
gap: 4px;
text-align: center;
diff --git a/resources/less/forum/VotingWidget.less b/resources/less/forum/VotingWidget.less
new file mode 100755
index 0000000..780ea17
--- /dev/null
+++ b/resources/less/forum/VotingWidget.less
@@ -0,0 +1,114 @@
+.VotingWidgetContainer {
+ padding: 12px;
+ background: @control-bg;
+ border-radius: @border-radius;
+ display: flex;
+
+ @media @phone {
+ padding: 8px;
+ }
+
+ .VotingWidget-voters {
+ display: inline-flex;
+
+ .VotingWidget-voters-list {
+ padding: 0 20px;
+ display: inline-flex;
+
+ .Voters-info {
+ .Voters-info--sections {
+ display: grid;
+ column-gap: 50px;
+ grid-template-columns: auto auto;
+ }
+ }
+ }
+ }
+}
+
+.VotingControl {
+ display: grid;
+ grid-template-columns: 1fr;
+
+ cursor: pointer;
+
+ height: 42px;
+ width: 36px;
+
+ left: 0;
+
+ background: @body-bg;
+
+ border-radius: 4px;
+ border: 1px solid fade(@primary-color, 80%);
+
+ line-height: 24px;
+
+ .LoadingIndicator-container {
+ color: @control-color;
+
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ button {
+ position: relative;
+ display: block;
+
+ width: 100%;
+
+ &[data-active] {
+ color: @primary-color !important;
+ }
+
+ &:hover,
+ &:focus,
+ &:active {
+ background: var(--vote-box-hover-color) !important;
+ }
+
+ .icon {
+ position: absolute;
+ top: 4px;
+ left: 50%;
+ margin-right: 0;
+ transform: translateX(-50%);
+
+ font-size: 1.2em !important;
+ }
+ }
+
+ &[data-upvotes-only] {
+ button {
+ height: 100%;
+ }
+
+ grid-template-rows: 1fr auto;
+
+ .DiscussionListItem-voteCount,
+ .Post-voteCount {
+ bottom: 0;
+ width: 100%;
+
+ pointer-events: none;
+ text-align: center;
+
+ display: block;
+ height: 24px;
+ }
+ }
+
+ &:not([data-upvotes-only]) {
+ height: 56px;
+
+ grid-template-rows: 1fr auto 1fr;
+
+ line-height: 1;
+
+ span {
+ text-align: center;
+ }
+ }
+}
\ No newline at end of file
diff --git a/resources/less/forum/extension.less b/resources/less/forum/extension.less
old mode 100755
new mode 100644
index 3363f27..7b3a68c
--- a/resources/less/forum/extension.less
+++ b/resources/less/forum/extension.less
@@ -1,6 +1,7 @@
@import "../lib/rankLabel.less";
-@import "./alternateLayout.less";
+//@import "./alternateLayout.less";
@import "./VotesBox.less";
+@import "./VotingWidget.less";
@media (min-width: 768px) {
.DiscussionListItem-info .item-discussion-votes + .item-tags {
@@ -8,6 +9,10 @@
}
}
+.Post-footer {
+ width: 100%;
+}
+
.item-discussion-votes {
margin-left: 5px;
position: absolute;
diff --git a/resources/locale/en.yml b/resources/locale/en.yml
old mode 100755
new mode 100644
index 48f86f5..c7f3ec8
--- a/resources/locale/en.yml
+++ b/resources/locale/en.yml
@@ -35,7 +35,9 @@ fof-gamification:
label_none: No voters
post:
upvote_button: Upvote post
+ upvote_tooltip: Upvote
downvote_button: Downvote post
+ downvote_tooltip: Downvote
admin:
permissions:
@@ -66,7 +68,6 @@ fof-gamification:
rate_limit: Enforce a vote rate limit (10 seconds)
discussion_page: Show total votes of original post on discussions list
alternate_layout: Use alternate voting layout on discussions list
- alternate_post_layout: Use alternate voting layout on posts
title: Votes
vote_color: Voted color
icon_name: Upvote/downvote icon