foo : Msg -> Model -> ( Model, Cmd Msg ) | |
foo msg model = | |
case msg of | |
Bar -> | |
( model, Cmd msg ) |
import axios from 'axios'; | |
import * as L from 'partial.lenses'; | |
import * as U from 'karet.util'; | |
import Kefir from 'kefir'; | |
const baseURL = 'http://localhost:3004'; | |
const client = axios.create({ baseURL }); | |
export const request$ = req => Kefir.fromPromise(client(req)); |
import React from 'karet'; | |
import Kefir from 'kefir'; | |
const tick = Kefir.interval(1000, 1); | |
const Timer = ({ start = 0 }) => | |
<div> | |
Seconds: {tick.scan((total, i) => total + i, start)} | |
</div>; |
In Kancolle, we have [Fleet
s][masayards fleet], which contain information on the Fleet
composition, its name, its state indicating whether it's on a missionm with a timestamp indicating when the Fleet
will be back from its mission. A wise guy on Twitter already said that timers can be created with setTimeout
(or more specifically setInterval
), but since sanity might be scarce nowadays, manually managing these periodic effects is something you rather delegate to somebody/something else.
Because calmm
encourages streams for data and application state, we can make a simple React component that will be used to display a constantly updating countdown to a set point in the future.
In these examples, I will be using [karet
][karet] for making React play nice with streams, [karet.util
][karet.util] and [partial.lenses
][partial.lenses] for the rest.
If you're not familiar with lenses, take a look at the tutorial in the [partial.lenses
][partial.lenses tutorial
Due to the KCS API being a clusterfuck of all kinds of madness, we can't use a single, simple lens that will normalize the data straight up. Good thing is: this state is always specified as an array of either numbers, or objects.
Luckily, the partial.lenses
library offers the L.choose
function, that can be used to
create optics based on the underlying view.
The result we want is something like this:
/** | |
* @param {string} name | |
* @param {string} defaultMessage | |
* @returns {Function} | |
* @private | |
*/ | |
function createError(name, defaultMessage) { | |
function NewError(message) { | |
this.name = name; | |
this.message = message || defaultMessage; |
/** | |
* @fileoverview FuseJS module definitions for Typescript | |
* @version 0.1.0 | |
*/ | |
declare namespace FuseJS { | |
export class Observable<T> { | |
constructor(...values: T[]); | |
_CompareFn: (oldValue: T, newValue: T) => boolean; | |
_UpdateFn: (oldValue: T, newValue: T) => void; | |
_MapFn: (newValue: T) => T; |
/** | |
* FuseJS module definitions for Typescript | |
* | |
* @version 0.1.0 | |
*/ | |
declare type ArrayLike<T> = Array<T> | { length: number;[index: number]: T }; | |
declare type ArrayOrIterable<T> = ArrayLike<T>; |
var Storage = require('FuseJS/Storage'); | |
function FileNotFoundError(message) { | |
this.name = this.constructor.name; | |
this.message = message || 'file could not be found'; | |
this.stack = (new Error()).stack; | |
} | |
FileNotFoundError.prototype = Object.create(Error.prototype); | |
FileNotFoundError.prototype.constructor = FileNotFoundError; |