Added some more type hinting, changed arguments for computed util

This commit is contained in:
David Sevilla Martin 2020-01-03 16:28:15 -05:00
parent b47ba94a9b
commit 49d2539aef
No known key found for this signature in database
GPG Key ID: F764F1417E16B15F
20 changed files with 58 additions and 53 deletions

View File

@ -12,6 +12,8 @@
- `app.bus` for some event hooking
* Translator
- Added `app.translator.transText`, automatically extracts text from `translator.trans` output
* Utils
- Changed `computed` util to require multiple keys to be passed as an array
#### Forum
* Forum Application

12
js/dist/admin.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8
js/dist/forum.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -65,7 +65,9 @@ export default abstract class Application {
* A local cache that can be used to store data at the application level, so
* that is persists between different routes.
*/
cache = {};
cache = {
notifications: null,
};
routes = {};

View File

@ -107,7 +107,7 @@ export default class Model {
* @param [options]
* @return {Promise}
*/
save(attributes: any, options: any = {}): Promise {
save(attributes: any, options: any = {}): Promise<Model|Model[]> {
const data = {
type: this.data.type,
id: this.data.id,
@ -162,7 +162,7 @@ export default class Model {
// old data! We'll revert to that and let others handle the error.
response => {
this.pushData(oldData);
m.lazyRedraw();
m.redraw();
throw response;
}
);
@ -171,13 +171,13 @@ export default class Model {
/**
* Send a request to delete the resource.
*
* @param {Object} data Data to send along with the DELETE request.
* @param {Object} body Data to send along with the DELETE request.
* @param {Object} [options]
* @return {Promise}
* @public
*/
delete(body, options = {}) {
if (!this.exists) return m.deferred.resolve().promise;
delete(body = {}, options = {}) {
if (!this.exists) return Promise.resolve();
return app.request(Object.assign({
method: 'DELETE',

View File

@ -24,7 +24,7 @@ export default class Session {
/**
* Attempt to log in a user.
*/
login(body: { identification: string, password: string }, options = {}) {
login(body: { identification: string, password: string, remember?: string }, options = {}) {
return app.request(Object.assign({
method: 'POST',
url: `${app.forum.attribute('baseUrl')}/login`,

View File

@ -9,7 +9,7 @@ export default class Store {
* The local data store. A tree of resource types to IDs, such that
* accessing data[type][id] will return the model for that type/ID.
*/
protected data: { [key: string]: { [key: number]: Model }} = {};
data: { [key: string]: { [key: number]: Model }} = {};
/**
* The model registry. A map of resource types to the model class that

View File

@ -1,3 +1,5 @@
import Mithril from 'mithril';
import Component, {ComponentProps} from '../Component';
import Button from './Button';
import RequestError from "../utils/RequestError";

View File

@ -17,8 +17,6 @@ export default class ModalManager extends Component {
super.oncreate(vnode);
app.modal = this;
window.vnode = vnode;
}
view() {
@ -44,7 +42,7 @@ export default class ModalManager extends Component {
// if (app.current) app.current.retain = true;
m.redraw(true);
m.redraw();
if (!$('.modal-backdrop').length) {
$('<div />').addClass('modal-backdrop')
@ -99,7 +97,7 @@ export default class ModalManager extends Component {
app.current.retain = false;
m.lazyRedraw();
m.redraw();
}
/**
@ -109,7 +107,7 @@ export default class ModalManager extends Component {
*/
onready() {
if (this.component && this.component.onready) {
this.component.onready(this.$());
this.component.onready();
}
}
}

View File

@ -30,7 +30,7 @@ export default class User extends Model {
canDelete = Model.attribute('canDelete') as () => boolean;
avatarColor = null;
color = computed('username', 'avatarUrl', 'avatarColor', function(username, avatarUrl, avatarColor) {
color = computed(['username', 'avatarUrl', 'avatarColor'], function(username, avatarUrl, avatarColor) {
// If we've already calculated and cached the dominant color of the user's
// avatar, then we can return that in RGB format. If we haven't, we'll want
// to calculate it. Unless the user doesn't have an avatar, in which case
@ -92,6 +92,6 @@ export default class User extends Model {
Object.assign(preferences, newPreferences);
return this.save({preferences});
return <Promise<User>> this.save({preferences});
}
}

View File

@ -6,9 +6,8 @@
* @param {function} compute The function which computes the value using the
* dependent values.
*/
export default function computed(...dependentKeys: string[]|Function[]): () => any {
const keys = <string[]> dependentKeys.slice(0, -1);
const compute = <Function> dependentKeys.slice(-1)[0];
export default function computed(dependentKeys: string|string[], compute: Function): () => any {
const keys = Array.from(dependentKeys);
const dependentValues = {};
let computedValue;

View File

@ -1,9 +1,10 @@
import m from 'mithril';
import prop from 'mithril/stream';
export default () => {
const mo = global.m;
const mo = window['m'];
const m = function (comp, ...args) {
const _m = function (comp, ...args) {
const node = mo.apply(this, arguments);
if (!node.attrs) node.attrs = {};
@ -22,13 +23,13 @@ export default () => {
return node;
};
Object.keys(mo).forEach(key => m[key] = mo[key]);
Object.keys(mo).forEach(key => _m[key] = mo[key]);
m.withAttr = (key: string, cb: Function) => function () {
_m.withAttr = (key: string, cb: Function) => function () {
cb(this.getAttribute(key) || this[key]);
};
m.prop = prop;
_m.prop = prop;
global.m = m;
window['m'] = _m;
}

View File

@ -4,8 +4,12 @@ import History from './utils/History';
import HeaderPrimary from './components/HeaderPrimary';
import HeaderSecondary from './components/HeaderSecondary';
import Page from './components/Page';
import IndexPage from './components/IndexPage';
import PostsUserPage from './components/PostsUserPage';
import User from "../common/models/User";
import Post from "../common/models/Post";
import Discussion from "../common/models/Discussion";
export default class Forum extends Application {
routes = {
@ -26,6 +30,9 @@ export default class Forum extends Application {
*/
history: History = new History();
previous: Page;
current: Page;
mount() {
// Get the configured default route and update that route's path to be '/'.
// Push the homepage as the first route, so that the user will always be
@ -72,7 +79,7 @@ export default class Forum extends Application {
setupRoutes() {
super.setupRoutes();
this.route.discussion = (discussion, near) => {
this.route.discussion = (discussion: Discussion, near?: number): string => {
const slug = discussion.slug();
return this.route(near && near !== 1 ? 'discussion.near' : 'discussion', {
id: discussion.id() + (slug.trim() ? '-' + slug : ''),
@ -82,21 +89,15 @@ export default class Forum extends Application {
/**
* Generate a URL to a post.
*
* @param {Post} post
* @return {String}
*/
this.route.post = post => {
this.route.post = (post: Post): string => {
return this.route.discussion(post.discussion(), post.number());
};
/**
* Generate a URL to a user.
*
* @param {User} user
* @return {String}
*/
this.route.user = user => {
this.route.user = (user: User): string => {
return this.route('user', {
username: user.username()
});

View File

@ -21,7 +21,7 @@ export default class DiscussionsSearchSource extends SearchSource {
include: 'mostRelevantPost'
};
return app.store.find('discussions', params).then(results => this.results[query] = results);
return app.store.find<Discussion>('discussions', params).then(results => this.results[query] = results);
}
view(query: string) {

View File

@ -158,7 +158,7 @@ export default class NotificationList extends Component {
const params = app.cache.notifications ? {page: {offset: app.cache.notifications.length * 10}} : null;
return app.store.find('notifications', params)
return app.store.find<Notification>('notifications', params)
.then(this.parseResults.bind(this))
.catch(() => {})
.then(() => {
@ -170,7 +170,7 @@ export default class NotificationList extends Component {
/**
* Parse results and append them to the notification list.
*/
parseResults(results: Notification[]|any): Notification[]|any {
parseResults(results: Notification[]): Notification[] {
app.cache.notifications = app.cache.notifications || [];
if (results.length) app.cache.notifications.push(results);

View File

@ -82,7 +82,7 @@ export default abstract class UserPage extends Page {
this.username = lowercaseUsername;
app.store.all('users').some(user => {
app.store.all<User>('users').some(user => {
if (user.username().toLowerCase() === lowercaseUsername && user.joinTime()) {
this.show(user);
return true;

View File

@ -27,7 +27,7 @@ export default class UsersSearchSource extends SearchSource {
query = query.toLowerCase();
const results = (this.results[query] || [])
.concat(app.store.all<User>('users').filter((user: User) => [user.username(), user.displayName()].some(value => value.toLowerCase().substr(0, query.length) === query)))
.concat(app.store.all<User>('users').filter(user => [user.username(), user.displayName()].some(value => value.toLowerCase().substr(0, query.length) === query)))
.filter((e, i, arr) => arr.lastIndexOf(e) === i)
.sort((a, b) => a.displayName().localeCompare(b.displayName()));

View File

@ -75,8 +75,8 @@ export default {
/**
* Delete the user.
*/
deleteAction() {
if (confirm(app.translator.trans('core.forum.user_controls.delete_confirmation'))) {
deleteAction(this: User) {
if (confirm(app.translator.transText('core.forum.user_controls.delete_confirmation'))) {
this.delete().then(() => {
if (app.current instanceof UserPage && app.current.user === this) {
app.history.back();
@ -90,7 +90,7 @@ export default {
/**
* Edit the user.
*/
editAction() {
editAction(this: User) {
app.modal.show(new EditUserModal({user: this}));
}
};