From 1bcfee8e87c8eb28bb5695fcf3ff6640c40cfb89 Mon Sep 17 00:00:00 2001 From: Davide Iadeluca Date: Tue, 30 Jan 2024 09:11:31 +0100 Subject: [PATCH] chore: work in progress --- js/src/common/models/Poll.tsx | 51 ++++- js/src/forum/components/Poll/PollList.js | 64 ++++++ js/src/forum/components/Poll/PollListItem.tsx | 193 ++++++++++++++++++ js/src/forum/components/Poll/PollPage.tsx | 9 + .../{PollsPage.js => PollsPage.tsx} | 31 ++- 5 files changed, 338 insertions(+), 10 deletions(-) create mode 100644 js/src/forum/components/Poll/PollList.js create mode 100644 js/src/forum/components/Poll/PollListItem.tsx create mode 100644 js/src/forum/components/Poll/PollPage.tsx rename js/src/forum/components/{PollsPage.js => PollsPage.tsx} (51%) diff --git a/js/src/common/models/Poll.tsx b/js/src/common/models/Poll.tsx index 181827ec..79d88072 100644 --- a/js/src/common/models/Poll.tsx +++ b/js/src/common/models/Poll.tsx @@ -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('title').call(this); + } + slug() { + return Model.attribute('slug').call(this); + } + + createdAt() { + return Model.attribute('createdAt', Model.transformDate).call(this); + } + user() { + return Model.hasOne('user').call(this); + } + + voteCount() { + return Model.attribute('voteCount').call(this); + } + + // TODO: These two don't make sense as of now + isUnread() { + return computed('unreadCount', (unreadCount) => !!unreadCount).call(this); + } + isRead() { + return computed('unreadCount', (unreadCount) => !!(app.session.user && !unreadCount)).call(this); + } + + hiddenAt() { + return Model.attribute('hiddenAt', Model.transformDate).call(this); + } + hiddenUser() { + return Model.hasOne('hiddenUser').call(this); + } + isHidden() { + return computed('hiddenAt', (hiddenAt) => !!hiddenAt).call(this); + } + + canVote() { + return Model.attribute('canVote').call(this); + } + canRename() { + return Model.attribute('canRename').call(this); + } + canHide() { + return Model.attribute('canHide').call(this); + } + canDelete() { + return Model.attribute('canDelete').call(this); + } } diff --git a/js/src/forum/components/Poll/PollList.js b/js/src/forum/components/Poll/PollList.js new file mode 100644 index 00000000..02742ca2 --- /dev/null +++ b/js/src/forum/components/Poll/PollList.js @@ -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 = ; + } else if (state.hasNext()) { + loading = ( + + ); + } + + 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 ( +
+ +
+ ); + } + + const pageSize = state.pageSize; + + return ( +
+
    + {state.getPages().map((pg) => { + return pg.items.map((poll) => ( +
  • + +
  • + )); + })} +
+
{loading}
+
+ ); + } +} diff --git a/js/src/forum/components/Poll/PollListItem.tsx b/js/src/forum/components/Poll/PollListItem.tsx new file mode 100644 index 00000000..563c6176 --- /dev/null +++ b/js/src/forum/components/Poll/PollListItem.tsx @@ -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 extends Component { + /** + * Ensures that the poll will not be redrawn + * unless new data comes in. + */ + subtree!: SubtreeRetainer; + + highlightRegExp?: RegExp; + + oninit(vnode: Mithril.Vnode) { + 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 ( +
+ {/* {this.controlsView(controls)} */} + {this.contentView()} + {this.slidableUnderneathView()} +
+ ); + } + + controlsView(controls: Mithril.ChildArray): Mithril.Children { + return ( + !!controls.length && ( + + {controls} + + ) + ); + } + + slidableUnderneathView(): Mithril.Children { + const poll = this.attrs.poll; + const isUnread = poll.isUnread(); + + return ( + + {icon('fas fa-check')} + + ); + } + + contentView(): Mithril.Children { + const poll = this.attrs.poll; + // const isUnread = poll.isUnread(); + // const isRead = poll.isRead(); + + return ( + //
+
+ {/* {this.authorAvatarView()} + {this.badgesView()} */} + {this.mainView()} + {this.voteCountItem()} +
+ ); + } + + mainView(): Mithril.Children { + const poll = this.attrs.poll; + + return ( + +

{highlight(poll.title(), this.highlightRegExp)}

+ {/*
    {listItems(this.infoItems().toArray())}
*/} + + ); + } + + oncreate(vnode: Mithril.VnodeDOM) { + 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) { + 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 ( + + ); + } + + return ( + + + + + {app.translator.trans('fof-polls.forum.poll_list.total_votes_a11y_label', { count: poll.voteCount() })} + + + ); + } +} diff --git a/js/src/forum/components/Poll/PollPage.tsx b/js/src/forum/components/Poll/PollPage.tsx new file mode 100644 index 00000000..dff1ea29 --- /dev/null +++ b/js/src/forum/components/Poll/PollPage.tsx @@ -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

PollPage

; + } +} diff --git a/js/src/forum/components/PollsPage.js b/js/src/forum/components/PollsPage.tsx similarity index 51% rename from js/src/forum/components/PollsPage.js rename to js/src/forum/components/PollsPage.tsx index 105a52a4..edfd19fa 100644 --- a/js/src/forum/components/PollsPage.js +++ b/js/src/forum/components/PollsPage.tsx @@ -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 ( -
+
{IndexPage.prototype.hero()}
-
@@ -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(); }