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

feat: new, simpler voting layout #100

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@
"name": "fas fa-thumbs-up",
"backgroundColor": "#e74c3c",
"color": "#fff"
}
},
"optional-dependencies": [
"flarum/likes"
]
},
"flagrow": {
"discuss": "https://discuss.flarum.org/d/20671"
Expand Down
1 change: 0 additions & 1 deletion extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
'useAlternateLayout',
'upVotesOnly',
'iconNameAlt',
'altPostVotingUi',
]),

(new Extend\Routes('api'))
Expand Down
9 changes: 0 additions & 9 deletions js/src/admin/components/SettingsPage.js
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export default class SettingsPage extends ExtensionPage {
'rateLimit',
'showVotesOnDiscussionPage',
'useAlternateLayout',
'altPostVotingUi',
'upVotesOnly',
'firstPostOnly',
'allowSelfVotes',
Expand Down Expand Up @@ -364,14 +363,6 @@ export default class SettingsPage extends ExtensionPage {
50
);

items.add(
'altPostLayout',
<Switch state={this.values.altPostVotingUi() || false} onchange={this.values.altPostVotingUi} className="votes-switch">
{app.translator.trans('fof-gamification.admin.page.votes.alternate_post_layout')}
</Switch>,
40
);

items.add(
'upvotesOnly',
<Switch state={this.values.upVotesOnly() || false} onchange={this.values.upVotesOnly} className="votes-switch">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export default function addUpvoteTabToUserProfile() {
app.routes['user.votes'] = { path: '/u/:username/votes', component: VotesUserPage };
extend(UserPage.prototype, 'navItems', function (items) {
const user = this.user;

if (!user) return;

const icon = setting('iconName') || 'thumbs';
items.add(
'votes',
Expand Down
75 changes: 12 additions & 63 deletions js/src/forum/addVoteButtons.js
Original file line number Diff line number Diff line change
@@ -1,75 +1,24 @@
import app from 'flarum/forum/app';
import { extend } from 'flarum/common/extend';
import Button from 'flarum/common/components/Button';
import CommentPost from 'flarum/forum/components/CommentPost';
import classList from 'flarum/common/utils/classList';
import PostControls from 'flarum/forum/utils/PostControls';

import VotesModal from './components/VotesModal';
import setting from './helpers/setting';
import saveVote from './helpers/saveVote';

export default function () {
extend(PostControls, 'moderationControls', function (items, post) {
if (post.seeVoters()) {
items.add('viewVotes', [
m(
Button,
{
icon: 'fas fa-thumbs-up',
onclick: () => {
app.modal.show(VotesModal, { post });
},
},
app.translator.trans('fof-gamification.forum.mod_item')
),
]);
if (post.contentType() === 'comment' && post.seeVoters()) {
items.add(
'viewVotes',
<Button
icon="fas fa-thumbs-up"
onclick={() => {
app.modal.show(VotesModal, { post });
}}
>
{app.translator.trans('fof-gamification.forum.mod_item')}
</Button>
);
}
});

extend(CommentPost.prototype, 'actionItems', function (items) {
const post = this.attrs.post;

//if (!post.canVote()) 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',
<div className={classList('CommentPost-votes', setting('useAlternateLayout', true) && 'alternateLayout')}>
{Button.component({
icon: this.voteLoading ? undefined : `fas fa-fw fa-${icon}-up`,
className: classList('Post-vote Post-upvote', hasUpvoted && 'Post-vote--active'),
loading: this.voteLoading,
disabled: this.voteLoading || !canVote || !canSeeVotes,
onclick: () => onclick(!hasUpvoted, false),
'aria-label': app.translator.trans('fof-gamification.forum.post.upvote_button'),
})}

<label className="Post-points">{post.votes()}</label>

{!upVotesOnly &&
Button.component({
icon: this.voteLoading ? undefined : `fas fa-fw fa-${icon}-down`,
className: classList('Post-vote Post-downvote', hasDownvoted && 'Post-vote--active'),
loading: this.voteLoading,
disabled: !canVote || !canSeeVotes,
onclick: () => onclick(false, !hasDownvoted),
'aria-label': app.translator.trans('fof-gamification.forum.post.downvote_button'),
})}
</div>,
10
);
});
}
22 changes: 0 additions & 22 deletions js/src/forum/addVotersToDiscussionPageSideBar.tsx

This file was deleted.

19 changes: 19 additions & 0 deletions js/src/forum/addVotersToEligiblePosts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { extend } from 'flarum/common/extend';
import type ItemList from 'flarum/common/utils/ItemList';
import CommentPost from 'flarum/forum/components/CommentPost';

import type Mithril from 'mithril';
import VotingWidget from './components/VotingWidget';

/**
* Adds our custom {@link VotingWidget} component to the post footer.
*/
export default function addVotersToEligiblePosts() {
extend(CommentPost.prototype, 'footerItems', function (items: ItemList<Mithril.Children>) {
const post = this.attrs.post;

if (post?.canSeeVotes() && post?.seeVoters()) {
items.add('post-voters', <VotingWidget post={post} />, -7);
}
});
}
136 changes: 95 additions & 41 deletions js/src/forum/components/Voters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ import icon from 'flarum/common/helpers/icon';
import SubtreeRetainer from 'flarum/common/utils/SubtreeRetainer';

import type Mithril from 'mithril';
import Post from 'flarum/common/models/Post';
import User from 'flarum/common/models/User';

export default class Voters extends Component {
interface IAttrs {
post: Post;
}

export default class Voters extends Component<IAttrs> {
subtreeRetainer!: SubtreeRetainer;
lastRenderVotes: number = -1;
loading: boolean = false;
Expand Down Expand Up @@ -46,24 +52,54 @@ export default class Voters extends Component {
}
}

viewnew() {
const max = 15;
const votes = this.attrs.post.votes?.();
const upvotes = this.attrs.post.upvotes?.();
const downvotes = this.attrs.post.downvotes?.();
const downvotesEnabled = false;

return (
<div className="Voters-info">
<div className="Votrs-info--title">
{icon('fas fa-users')}
<span className="FoFGamification-voters-title-label">{app.translator.trans('fof-gamification.forum.voters.label')}</span>
</div>
<div className="Voters-info--sections">
{votes && upvotes ? (
<div className="Upvotes">
<span>Upvoters</span>
{this.buildVoters(upvotes, max)}
</div>
) : (
<LoadingIndicator display="inline" />
)}
{votes && downvotes && downvotesEnabled ? (
<div className="Downvotes">
<span>Downvoters</span>
{this.buildVoters(upvotes, max)}
</div>
) : null}
</div>
</div>
);
}

view() {
// if (this.loading) {
if (this.attrs.post.votes() === false || this.attrs.post.upvotes() === false) {
return (
<div className="VotingContainer">
<div className="FoFGamification-voters">
<div className="FoFGamification-voters-title">
<span className="FoFGamification-voters-title-icon">
{icon('fas fa-users')}
<span className="FoFGamification-voters-title-label">{app.translator.trans('fof-gamification.forum.voters.label')}</span>
<span className="FoFGamification-voters-title-label FoFGamification-voters-title-label--mobile">
{app.translator.trans('fof-gamification.forum.voters.label')}
</span>
<div className="Upvoters">
<div className="FoFGamification-voters-title">
<span className="FoFGamification-voters-title-icon">
{icon('fas fa-users')}
<span className="FoFGamification-voters-title-label">{app.translator.trans('fof-gamification.forum.voters.label')}</span>
<span className="FoFGamification-voters-title-label FoFGamification-voters-title-label--mobile">
{app.translator.trans('fof-gamification.forum.voters.label')}
</span>
</div>

<LoadingIndicator display="inline" />
</span>
</div>

<LoadingIndicator display="inline" />
</div>
);
}
Expand All @@ -72,35 +108,53 @@ export default class Voters extends Component {
const voters = this.attrs.post.upvotes();

return (
<div className="VotingContainer">
<div className="FoFGamification-voters">
<div className="FoFGamification-voters-title">
<span className="FoFGamification-voters-title-icon">
{icon('fas fa-users')}
<span className="FoFGamification-voters-title-label">{app.translator.trans('fof-gamification.forum.voters.label')}</span>
<span className="FoFGamification-voters-title-label FoFGamification-voters-title-label--mobile">
{voters.length === 0
? app.translator.trans('fof-gamification.forum.voters.label_none')
: app.translator.trans('fof-gamification.forum.voters.label')}
</span>
<div className="Upvoters">
<div className="FoFGamification-voters-title">
<span className="FoFGamification-voters-title-icon">
{icon('fas fa-users')}
<span className="FoFGamification-voters-title-label">{app.translator.trans('fof-gamification.forum.voters.label')}</span>
<span className="FoFGamification-voters-title-label FoFGamification-voters-title-label--mobile">
{voters.length === 0
? app.translator.trans('fof-gamification.forum.voters.label_none')
: app.translator.trans('fof-gamification.forum.voters.label')}
</span>
</div>
<div className="FoFGamification-voters-message">
{voters.length === 0 ? app.translator.trans('fof-gamification.forum.voters.none') : null}
</div>
<div className="FoFGamification-voters-list">
{voters.slice(0, max).map((user: any) => (
<Link href={app.route('user', { username: user.slug() })} className="FoFGamification-voters-item">
<Tooltip text={user.displayName()}>{avatar(user)}</Tooltip>
</Link>
))}
{voters.length > max ? (
<span className="FoFGamification-voters-item FoFGamification-voters-item--plus">
<span className="Avatar">{`+${voters.length - max}`}</span>
</span>
) : null}
</div>
</span>
</div>
<div className="FoFGamification-voters-message">
{voters.length === 0 ? app.translator.trans('fof-gamification.forum.voters.none') : null}
</div>
<div className="FoFGamification-voters-list">
{voters.slice(0, max).map((user: any) => (
<Link href={app.route('user', { username: user.slug() })} className="FoFGamification-voters-item">
<Tooltip text={user.displayName()}>{avatar(user)}</Tooltip>
</Link>
))}
{voters.length > max ? (
<span className="FoFGamification-voters-item FoFGamification-voters-item--plus">
<span className="Avatar">{`+${voters.length - max}`}</span>
</span>
) : null}
</div>
</div>
);
}

buildVoters(voters: User[], max: number) {
if (voters.length === 0) {
return <div className="FoFGamification-voters-message">{app.translator.trans('fof-gamification.forum.voters.none')}</div>;
}
return (
<div className="FoFGamification-voters-list">
{voters.slice(0, max).map((user: User) => (
<Link href={app.route('user', { username: user.slug() })} className="FoFGamification-voters-item">
<Tooltip text={user.displayName()}>{avatar(user)}</Tooltip>
</Link>
))}
{voters.length > max ? (
<span className="FoFGamification-voters-item FoFGamification-voters-item--plus">
<span className="Avatar">{`+${voters.length - max}`}</span>
</span>
) : null}
</div>
);
}
Expand Down
Loading