Skip to content

Commit

Permalink
wip: add polls route, begin to setup state, controls, etc
Browse files Browse the repository at this point in the history
  • Loading branch information
imorland committed Jan 30, 2024
1 parent adf5393 commit 4b461a5
Show file tree
Hide file tree
Showing 6 changed files with 394 additions and 6 deletions.
15 changes: 10 additions & 5 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@

return [
(new Extend\Frontend('forum'))
->js(__DIR__.'/js/dist/forum.js')
->css(__DIR__.'/resources/less/forum.less'),
->js(__DIR__ . '/js/dist/forum.js')
->css(__DIR__ . '/resources/less/forum.less')
->route('/polls', 'fof_polls_directory', Content\PollsDirectory::class),

(new Extend\Frontend('admin'))
->js(__DIR__.'/js/dist/admin.js')
->css(__DIR__.'/resources/less/admin.less'),
->js(__DIR__ . '/js/dist/admin.js')
->css(__DIR__ . '/resources/less/admin.less'),

new Extend\Locales(__DIR__.'/resources/locale'),
new Extend\Locales(__DIR__ . '/resources/locale'),

(new Extend\Routes('api'))
->post('/fof/polls', 'fof.polls.create', Controllers\CreatePollController::class)
Expand Down Expand Up @@ -94,6 +95,7 @@
(new Extend\Settings())
->default('fof-polls.maxOptions', 10)
->default('fof-polls.optionsColorBlend', true)
->default('fof-polls.directory-default-sort', 'default')
->serializeToForum('allowPollOptionImage', 'fof-polls.allowOptionImage', 'boolval')
->serializeToForum('pollMaxOptions', 'fof-polls.maxOptions', 'intval')
->registerLessConfigVar('fof-polls-options-color-blend', 'fof-polls.optionsColorBlend', function ($value) {
Expand All @@ -102,4 +104,7 @@

(new Extend\ModelVisibility(Poll::class))
->scope(Access\ScopePollVisibility::class),

(new Extend\View())
->namespace('fof-polls', __DIR__ . '/resources/views'),
];
148 changes: 148 additions & 0 deletions js/src/forum/components/PollsDirectory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import app from 'flarum/forum/app';
import Page from 'flarum/common/components/Page';
import extractText from 'flarum/common/utils/extractText';
import IndexPage from 'flarum/forum/components/IndexPage';
import ItemList from 'flarum/common/utils/ItemList';
import listItems from 'flarum/common/helpers/listItems';
import SelectDropdown from 'flarum/common/components/SelectDropdown';
import LinkButton from 'flarum/common/components/LinkButton';
import Select from 'flarum/common/components/Select';
import Button from 'flarum/common/components/Button';
import Mithril from 'mithril';

export default class PollsDirectory extends Page {
oncreate(vnode: Mithril.Vnode) {
super.oncreate(vnode);

app.setTitle(extractText(app.translator.trans('fof-polls.forum.page.nav')));
}

view() {
return (
<div className="IndexPage">
{IndexPage.prototype.hero()}
<div className="container">
<div className="sideNavContainer">
<nav className="IndexPage-nav sideNav">
<ul>{listItems(this.sidebarItems().toArray())}</ul>
</nav>
<div className="IndexPage-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>
{/* <UserDirectoryList state={this.state} /> */}
</div>
</div>
</div>
</div>
);
}

/**
* Our own sidebar. Re-uses Index.sidebarItems as the base
* Elements added here will only show up on the user directory page
*/
sidebarItems(): ItemList<Mithril.Children> {
const items = IndexPage.prototype.sidebarItems();

items.setContent(
'nav',
SelectDropdown.component(
{
buttonClassName: 'Button',
className: 'App-titleControl',
},
this.navItems().toArray()
)
);

return items;
}

/**
* Our own sidebar navigation. Re-uses Index.navItems as the base
* Elements added here will only show up on the user directory page
*/
navItems(): ItemList<Mithril.Children> {
const items = IndexPage.prototype.navItems();
const params = this.stickyParams();

items.setContent(
'fof-polls-directory',
LinkButton.component(
{
href: app.route('fof_polls_directory', params),
icon: 'fas fa-poll',
},
app.translator.trans('fof-polls.forum.page.nav')
),
);

return items;
}

stickyParams() {
return {
sort: m.route.param('sort'),
q: m.route.param('q'),
};
}

changeParams(sort: string) {
const params = this.params();

if (sort === app.forum.attribute('pollsDirectoryDefaultSort')) {
delete params.sort;
} else {
params.sort = sort;
}

this.state.refreshParams(params);

const routeParams = { ...params };
delete routeParams.qBuilder;

m.route.set(app.route('fof_polls_directory', routeParams));
}

viewItems() {
const items = new ItemList();
const sortMap = this.state.sortMap();

const sortOptions = {};
for (const i in sortMap) {
sortOptions[i] = app.translator.trans('fof-polls.lib.sort.' + i);
}

items.add(
'sort',
Select.component({
options: sortOptions,
value: this.state.getParams().sort || app.forum.attribute('pollsDirectoryDefaultSort'),
onchange: this.changeParams.bind(this),
}),
100
);

return items;
}

actionItems(): ItemList<Mithril.Children> {
const items = new ItemList<Mithril.Children>();

items.add(
'refresh',
Button.component({
title: app.translator.trans('fof-polls.forum.page.refresh_tooltip'),
icon: 'fas fa-sync',
className: 'Button Button--icon',
onclick: () => {
this.state.refresh();
},
})
);

return items;
}
}
3 changes: 2 additions & 1 deletion js/src/forum/extend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import Discussion from 'flarum/common/models/Discussion';
import Poll from './models/Poll';
import PollOption from './models/PollOption';
import PollVote from './models/PollVote';
import PollsDirectory from './components/PollsDirectory';

export default [
new Extend.Routes() //
.add('fof_polls_directory', '/polls', 'polls'),
.add('fof_polls_directory', '/polls', PollsDirectory),

new Extend.Store() //
.add('polls', Poll)
Expand Down
135 changes: 135 additions & 0 deletions js/src/forum/states/PollDirectoryState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import app from 'flarum/forum/app';

/**
* Based on Flarum's DiscussionListState
*/
import SortMap from '../../common/utils/SortMap';

export default class UserDirectoryState {
constructor(params = {}, app = window.app) {
this.params = params;

this.app = app;

this.users = [];

this.moreResults = false;

this.loading = false;

this.qBuilder = {};
}

requestParams() {
const params = { include: [], filter: {} };

const sortKey = this.params.sort || app.forum.attribute('userDirectoryDefaultSort');

// sort might be set to null if no sort params has been passed
params.sort = this.sortMap()[sortKey];

if (this.params.q) {
params.filter.q = this.params.q;
}

return params;
}

sortMap() {
return {
default: '',
...new SortMap().sortMap(),
};
}

getParams() {
return this.params;
}

clear() {
this.users = [];
m.redraw();
}

refreshParams(newParams) {
if (!this.hasUsers() || Object.keys(newParams).some((key) => this.getParams()[key] !== newParams[key])) {
const q = '';
this.params = newParams;

if (newParams.qBuilder) {
Object.assign(this.qBuilder, newParams.qBuilder || {});
this.params.q = Object.values(this.qBuilder).join(' ').trim();
}

if (!this.params.q && q) {
this.params.q = q;
}

this.refresh();
}
}

refresh() {
this.loading = true;

this.clear();

return this.loadResults().then(
(results) => {
this.users = [];
this.parseResults(results);
},
() => {
this.loading = false;
m.redraw();
}
);
}

loadResults(offset) {
const preloadedUsers = this.app.preloadedApiDocument();

if (preloadedUsers) {
return Promise.resolve(preloadedUsers);
}

const params = this.requestParams();
params.page = { offset };
params.include = params.include.join(',');

return this.app.store.find('users', params);
}

loadMore() {
this.loading = true;

this.loadResults(this.users.length).then(this.parseResults.bind(this));
}

parseResults(results) {
this.users.push(...results);

this.loading = false;
this.moreResults = !!results.payload.links && !!results.payload.links.next;

m.redraw();

return results;
}

hasUsers() {
return this.users.length > 0;
}

isLoading() {
return this.loading;
}

isSearchResults() {
return !!this.params.q;
}

empty() {
return !this.hasUsers() && !this.isLoading();
}
}
16 changes: 16 additions & 0 deletions resources/views/directory/index.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
$url = resolve('Flarum\Http\UrlGenerator');
?>
<div class="container">
<h2>{{ $translator->trans('fof-polls.forum.page.nav') }}</h2>

<ul>
@foreach ($apiDocument->data as $user)
<li>
{{ $user->attributes->username }}
</li>
@endforeach
</ul>

<a href="{{ $url->to('forum')->route('fof_polls_directory') }}?page={{ $page + 1 }}">{{ $translator->trans('core.views.index.next_page_button') }} &raquo;</a>
</div>
Loading

0 comments on commit 4b461a5

Please sign in to comment.