Skip to content

Commit

Permalink
chore: work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
DavideIadeluca committed Jan 30, 2024
1 parent 9b404b9 commit 1bcfee8
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 10 deletions.
51 changes: 50 additions & 1 deletion js/src/common/models/Poll.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,55 @@
import app from 'flarum/forum/app';
import Model from 'flarum/common/Model';
import User from 'flarum/common/Model';
import computed from 'flarum/common/utils/computed';

export default class Poll extends Model {
//TMP
title() {
return Model.attribute<string>('title').call(this);
}
slug() {
return Model.attribute<string>('slug').call(this);
}

createdAt() {
return Model.attribute<Date | undefined, string | undefined>('createdAt', Model.transformDate).call(this);
}
user() {
return Model.hasOne<User | null>('user').call(this);
}

voteCount() {
return Model.attribute<number>('voteCount').call(this);
}

// TODO: These two don't make sense as of now
isUnread() {
return computed<boolean, this>('unreadCount', (unreadCount) => !!unreadCount).call(this);
}
isRead() {
return computed<boolean, this>('unreadCount', (unreadCount) => !!(app.session.user && !unreadCount)).call(this);
}

hiddenAt() {
return Model.attribute('hiddenAt', Model.transformDate).call(this);
}
hiddenUser() {
return Model.hasOne<User | null>('hiddenUser').call(this);
}
isHidden() {
return computed<boolean, this>('hiddenAt', (hiddenAt) => !!hiddenAt).call(this);
}

canVote() {
return Model.attribute<boolean | undefined>('canVote').call(this);
}
canRename() {
return Model.attribute<boolean | undefined>('canRename').call(this);
}
canHide() {
return Model.attribute<boolean | undefined>('canHide').call(this);
}
canDelete() {
return Model.attribute<boolean | undefined>('canDelete').call(this);
}
}
64 changes: 64 additions & 0 deletions js/src/forum/components/Poll/PollList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import app from 'flarum/forum/app';
import Component from 'flarum/common/Component';
import PollListItem from './PollListItem';
import Button from 'flarum/common/components/Button';
import LoadingIndicator from 'flarum/common/components/LoadingIndicator';
import Placeholder from 'flarum/common/components/Placeholder';
import classList from 'flarum/common/utils/classList';

/**
* The `PollList` component displays a list of polls.
*/
export default class PollList extends Component {
view() {
/**
* @type {import('../../states/PollListState').default}
*/
const state = this.attrs.state;

const params = state.getParams();
const isLoading = state.isInitialLoading() || state.isLoadingNext();

let loading;

if (isLoading) {
loading = <LoadingIndicator />;
} else if (state.hasNext()) {
loading = (
<Button className="Button" onclick={state.loadNext.bind(state)}>
{/* TODO: IS THIS THE RIGHT KEY? MAYBE WE SHOULD MAKE A NEW ONE? */}
{app.translator.trans('core.forum.discussion_list.load_more_button')}
</Button>
);
}

if (state.isEmpty()) {
{
/* TODO: IS THIS THE RIGHT KEY? MAYBE WE SHOULD MAKE A NEW ONE? */
}
const text = app.translator.trans('core.forum.discussion_list.empty_text');
return (
<div className="PollList">
<Placeholder text={text} />
</div>
);
}

const pageSize = state.pageSize;

return (
<div className={classList('PollList', { 'PollList--searchResults': state.isSearchResults() })}>
<ul aria-busy={isLoading} className="PollList-polls">
{state.getPages().map((pg) => {
return pg.items.map((poll) => (
<li key={poll.id()} data-id={poll.id()}>
<PollListItem poll={poll} params={params} />
</li>
));
})}
</ul>
<div className="PollList-loadMore">{loading}</div>
</div>
);
}
}
193 changes: 193 additions & 0 deletions js/src/forum/components/Poll/PollListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import * as Mithril from 'mithril';
import app from 'flarum/forum/app';
import Component, { ComponentAttrs } from 'flarum/common/Component';
import type Poll from '../../../common/models/Poll';
import type { PollListParams } from '../../states/PollListState';
import SubtreeRetainer from 'flarum/common/utils/SubtreeRetainer';
import classList from 'flarum/common/utils/classList';
import Dropdown from 'flarum/common/components/Dropdown';
import Link from 'flarum/common/components/Link';
import highlight from 'flarum/common/helpers/highlight';
import slidable from 'flarum/forum/utils/slidable';
import icon from 'flarum/common/helpers/icon';
import PollPage from './PollPage';
import abbreviateNumber from 'flarum/common/utils/abbreviateNumber';

export interface IPollListItemAttrs extends ComponentAttrs {
poll: Poll;
params: PollListParams;
}

/**
* The `PollListItem` component shows a single poll in the
* poll list.
*/
export default class PollListItem<CustomAttrs extends IPollListItemAttrs = IPollListItemAttrs> extends Component<CustomAttrs> {
/**
* Ensures that the poll will not be redrawn
* unless new data comes in.
*/
subtree!: SubtreeRetainer;

highlightRegExp?: RegExp;

oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode);

this.subtree = new SubtreeRetainer(
() => this.attrs.poll.freshness,
() => {
const time = app.session.user && app.session.user.markedAllAsReadAt();
return time && time.getTime();
},
() => this.active()
);
}

elementAttrs() {
return {
className: classList('PollListItem', {
active: this.active(),
'PollListItem--hidden': this.attrs.poll.isHidden(),
Slidable: 'ontouchstart' in window,
}),
};
}

view() {
const poll = this.attrs.poll;

// TODO IMPLEMENT POLLCONTROLS
//const controls = PollControls.controls(poll, this).toArray();
const attrs = this.elementAttrs();

return (
<div {...attrs}>
{/* {this.controlsView(controls)} */}
{this.contentView()}
{this.slidableUnderneathView()}
</div>
);
}

controlsView(controls: Mithril.ChildArray): Mithril.Children {
return (
!!controls.length && (
<Dropdown
icon="fas fa-ellipsis-v"
className="PollListItem-controls"
buttonClassName="Button Button--icon Button--flat"
accessibleToggleLabel={app.translator.trans('fof-polls.forum.poll_controls.toggle_dropdown_accessible_label')}
>
{controls}
</Dropdown>
)
);
}

slidableUnderneathView(): Mithril.Children {
const poll = this.attrs.poll;
const isUnread = poll.isUnread();

return (
<span
className={classList('Slidable-underneath Slidable-underneath--left Slidable-underneath--elastic', { disabled: !isUnread })}
onclick={this.markAsRead.bind(this)}
>
{icon('fas fa-check')}
</span>
);
}

contentView(): Mithril.Children {
const poll = this.attrs.poll;
// const isUnread = poll.isUnread();
// const isRead = poll.isRead();

return (
// <div className={classList('PollListItem-content', 'Slidable-content', { unread: isUnread, read: isRead })}>
<div className={classList('PollListItem-content')}>
{/* {this.authorAvatarView()}
{this.badgesView()} */}
{this.mainView()}
{this.voteCountItem()}
</div>
);
}

mainView(): Mithril.Children {
const poll = this.attrs.poll;

return (
<Link href={app.route.poll(poll)} className="PollListItem-main">
<h2 className="PollListItem-title">{highlight(poll.title(), this.highlightRegExp)}</h2>
{/* <ul className="PollListItem-info">{listItems(this.infoItems().toArray())}</ul> */}
</Link>
);
}

oncreate(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
super.oncreate(vnode);

// If we're on a touch device, set up the discussion row to be slidable.
// This allows the user to drag the row to either side of the screen to
// reveal controls.
if ('ontouchstart' in window) {
const slidableInstance = slidable(this.element);

this.$('.PollListItem-controls').on('hidden.bs.dropdown', () => slidableInstance.reset());
}
}

onbeforeupdate(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
super.onbeforeupdate(vnode);

return this.subtree.needsRebuild();
}

/**
* Determine whether or not the discussion is currently being viewed.
*/
active() {
return app.current.matches(PollPage, { poll: this.attrs.poll });
}

/**
* Mark the poll as read.
*/
markAsRead() {
const poll = this.attrs.poll;

if (poll.isUnread()) {
poll.save({ lastVotedNumber: poll.voteCount() });
m.redraw();
}
}

voteCountItem() {
const poll = this.attrs.poll;
const isUnread = poll.isUnread();

if (isUnread) {
return (
<button className="Button--ua-reset PollListItem-count" onclick={this.markAsRead.bind(this)}>
<span aria-hidden="true">{abbreviateNumber(poll.voteCount())}</span>

<span className="visually-hidden">
{app.translator.trans('core.forum.discussion_list.unread_replies_a11y_label', { count: poll.voteCount() })}
</span>
</button>
);
}

return (
<span className="PollListItem-count">
<span aria-hidden="true">{abbreviateNumber(poll.voteCount())}</span>

<span className="visually-hidden">
{app.translator.trans('fof-polls.forum.poll_list.total_votes_a11y_label', { count: poll.voteCount() })}
</span>
</span>
);
}
}
9 changes: 9 additions & 0 deletions js/src/forum/components/Poll/PollPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Mithril from 'mithril';
import app from 'flarum/forum/app';
import Page from 'flarum/common/components/Page';

export default class PollPage extends Page {
view(): Mithril.Children {
return <p>PollPage</p>;
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
import * as Mithril from 'mithril';
import app from 'flarum/forum/app';
import Page from 'flarum/common/components/Page';
import ItemList from 'flarum/common/utils/ItemList';
import listItems from 'flarum/common/helpers/listItems';
import ItemList from 'flarum/common/utils/ItemList';
import Page from 'flarum/common/components/Page';
import IndexPage from 'flarum/forum/components/IndexPage';
import Poll from './Poll';
import PollList from './Poll/PollList';

export default class PollsPage extends Page {
oninit(vnode) {
oninit(vnode: Mithril.Vnode) {
super.oninit(vnode);
}

oncreate(vnode) {
oncreate(vnode: Mithril.Vnode) {
super.oncreate(vnode);
}

view() {
view(): Mithril.Children {
return (
<div className="IndexPage">
<div className="PollsPage">
{IndexPage.prototype.hero()}
<div className="container">
<div className="sideNavContainer">
<nav className="IndexPage-nav sideNav">
<nav className="PollsPage-nav sideNav">
<ul>{listItems(this.sidebarItems().toArray())}</ul>
</nav>
<div className="IndexPage-results sideNavOffset">
<Poll />
<div className="PollsPage-results sideNavOffset">
<div className="IndexPage-toolbar">
<ul className="IndexPage-toolbar-view">{listItems(this.viewItems().toArray())}</ul>
{/* <ul className="IndexPage-toolbar-action">{listItems(this.actionItems().toArray())}</ul> */}
</div>
<PollList />
</div>
</div>
</div>
Expand All @@ -36,6 +42,13 @@ export default class PollsPage extends Page {
return IndexPage.prototype.sidebarItems();
}

// actionItems() {
// return IndexPage.prototype.actionItems();
// }

viewItems() {
return IndexPage.prototype.viewItems();
}
navItems() {
return IndexPage.prototype.navItems();
}
Expand Down

0 comments on commit 1bcfee8

Please sign in to comment.