<commit_before>/* @flow */

import React from 'react'
import ReactDOM from 'react-dom'
import { asyncResult, emit, reduce, update } from '../sunshine'
import * as Sunshine from '../react'
import { get, over, set } from 'safety-lens'
import { prop } from 'safety-lens/es2015'
import { assert } from 'chai'

import type { Lens_ } from 'safety-lens'
import type { Reducers } from '../sunshine'

// State

export type AppState = {
  todos: Todo[],
}

export type Todo = {
  title: string,
  completed: boolean,
}

const initialState: AppState = { todos: [] }


// Basic component

type DefaultProps = {}
type Props = {
  app: Sunshine.App<AppState>,
  pageSize: number,
}
type ComponentState = { todos: Todo[] }

class TodoApp extends Sunshine.Component<DefaultProps,Props,ComponentState> {
  getState(appState: AppState): ComponentState {
    return {
      todos: todosPage(this.props.pageSize, appState)
    }
  }

  render(): React.Element {
    const todos = this.state.todos.map((todo, idx) => (
      <li key={idx}>{todo.title}</li>
    ))
    return (
      <div>
        <form onSubmit={this.addTodo.bind(this)}>
          <input type="text" ref="title"/>
          <input type="submit" value="create todo"/>
        </form>
        <ul>
          {todos}
        </ul>
      </div>
    )
  }

  addTodo(event: Event) {
    event.preventDefault()
    const title = this.refs.title.value
    this.emit(new AddTodoEvent(title))
  }
}


// Lenses

const todosLens: Lens_<AppState,Todo[]> = prop('todos')

function todosPage(pageSize: number, state: AppState): Todo[] {
  const todos = get(todosLens, state)
  return todos.filter(todo => !todo.completed).slice(0, pageSize)
}


// Demonstrating lenses

// reading todos
const todos = get(todosLens, initialState)
assert.deepEqual(todos, [])

// setting todos;
// `newState` is a new object with the new todos list. `initialState` is not modified.
const newState = set(todosLens, [
  { title: 'add todo', completed: true }
], initialState)

assert.deepEqual(newState, {
  todos: [
    { title: 'add todo', completed: true }
  ]
})

// modifying todos without replacing the whole list
const newnewState = over(todosLens, todos => todos.concat({
  title: 'append a todo',
  completed: false,
}), newState)

assert.deepEqual(newnewState, {
  todos: [
    { title: 'add todo', completed: true },
    { title: 'append a todo', completed: false },
  ]
})


// The Event type

class AddTodoEvent {
  title: string;
  constructor(title: string) {
    this.title = title
  }
}


// Event reducers

const reducers: Reducers = [
  reduce(AddTodoEvent, (state, { title }) => update(
    over(todosLens, todos => todos.concat({
      title: title,
      completed: false,
    }), state)
  ))
]




/* Other possible events */

// import {} from 'whatwg-fetch'
// Tells Flow that `fetch` is a global function.
declare var fetch: Function;


class AddTodoWithAuthor {
  title: string;
  authorId: number;
  constructor(title: string, authorId: number) {
    this.title = title
    this.authorId = authorId
  }
}

class AppendTodo {
  todo: Todo;
  constructor(todo: Todo) {
    this.todo = todo
  }
}

const otherPossibleReducers = [
  reduce(AddTodoWithAuthor, (state, { title, authorId }) => asyncResult(
    fetch(`/users/${authorId}`).then(response => {
      return response.json().then(author => {
        const todo = {
          title,
          author,
          completed: false,
        }
        return emit(new AppendTodo(todo))
      })
    })
  )),

  reduce(AppendTodo, (state, { todo }) => update(
    over(todosLens, todos => todos.concat(todo), state)
  )),
]


// Initialize App

const app = new Sunshine.App(initialState, reducers)


// Render the app

function toRender() {
  ReactDOM.render(
    <TodoApp pageSize={10} app={app} />,
    document.getElementById('app')
  )
}

export {
  TodoApp,
  initialState,
  toRender,
  app,
}
<commit_msg>Add missing type parameter to `Reducers` annotation in simple-todo example<commit_after>/* @flow */

import React from 'react'
import ReactDOM from 'react-dom'
import { asyncResult, emit, reduce, update } from '../sunshine'
import * as Sunshine from '../react'
import { get, over, set } from 'safety-lens'
import { prop } from 'safety-lens/es2015'
import { assert } from 'chai'

import type { Lens_ } from 'safety-lens'
import type { Reducers } from '../sunshine'

// State

export type AppState = {
  todos: Todo[],
}

export type Todo = {
  title: string,
  completed: boolean,
}

const initialState: AppState = { todos: [] }


// Basic component

type DefaultProps = {}
type Props = {
  app: Sunshine.App<AppState>,
  pageSize: number,
}
type ComponentState = { todos: Todo[] }

class TodoApp extends Sunshine.Component<DefaultProps,Props,ComponentState> {
  getState(appState: AppState): ComponentState {
    return {
      todos: todosPage(this.props.pageSize, appState)
    }
  }

  render(): React.Element {
    const todos = this.state.todos.map((todo, idx) => (
      <li key={idx}>{todo.title}</li>
    ))
    return (
      <div>
        <form onSubmit={this.addTodo.bind(this)}>
          <input type="text" ref="title"/>
          <input type="submit" value="create todo"/>
        </form>
        <ul>
          {todos}
        </ul>
      </div>
    )
  }

  addTodo(event: Event) {
    event.preventDefault()
    const title = this.refs.title.value
    this.emit(new AddTodoEvent(title))
  }
}


// Lenses

const todosLens: Lens_<AppState,Todo[]> = prop('todos')

function todosPage(pageSize: number, state: AppState): Todo[] {
  const todos = get(todosLens, state)
  return todos.filter(todo => !todo.completed).slice(0, pageSize)
}


// Demonstrating lenses

// reading todos
const todos = get(todosLens, initialState)
assert.deepEqual(todos, [])

// setting todos;
// `newState` is a new object with the new todos list. `initialState` is not modified.
const newState = set(todosLens, [
  { title: 'add todo', completed: true }
], initialState)

assert.deepEqual(newState, {
  todos: [
    { title: 'add todo', completed: true }
  ]
})

// modifying todos without replacing the whole list
const newnewState = over(todosLens, todos => todos.concat({
  title: 'append a todo',
  completed: false,
}), newState)

assert.deepEqual(newnewState, {
  todos: [
    { title: 'add todo', completed: true },
    { title: 'append a todo', completed: false },
  ]
})


// The Event type

class AddTodoEvent {
  title: string;
  constructor(title: string) {
    this.title = title
  }
}


// Event reducers

const reducers: Reducers<AppState> = [
  reduce(AddTodoEvent, (state, { title }) => update(
    over(todosLens, todos => todos.concat({
      title: title,
      completed: false,
    }), state)
  ))
]




/* Other possible events */

// import {} from 'whatwg-fetch'
// Tells Flow that `fetch` is a global function.
declare var fetch: Function;


class AddTodoWithAuthor {
  title: string;
  authorId: number;
  constructor(title: string, authorId: number) {
    this.title = title
    this.authorId = authorId
  }
}

class AppendTodo {
  todo: Todo;
  constructor(todo: Todo) {
    this.todo = todo
  }
}

const otherPossibleReducers = [
  reduce(AddTodoWithAuthor, (state, { title, authorId }) => asyncResult(
    fetch(`/users/${authorId}`).then(response => {
      return response.json().then(author => {
        const todo = {
          title,
          author,
          completed: false,
        }
        return emit(new AppendTodo(todo))
      })
    })
  )),

  reduce(AppendTodo, (state, { todo }) => update(
    over(todosLens, todos => todos.concat(todo), state)
  )),
]


// Initialize App

const app = new Sunshine.App(initialState, reducers)


// Render the app

function toRender() {
  ReactDOM.render(
    <TodoApp pageSize={10} app={app} />,
    document.getElementById('app')
  )
}

export {
  TodoApp,
  initialState,
  toRender,
  app,
}

{'fraction_non_alphanumeric': 0.1420952380952381, 'fraction_numerical': 0.0017777777777777779, 'mean_word_length': 3.0914285714285716, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2723315', 'n_tokens_mistral': 2692, 'n_tokens_neox': 2656, 'n_words': 667}