breezy 0.12.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/lib/breezy.rb +6 -9
  3. data/lib/breezy/helpers.rb +541 -11
  4. data/lib/breezy/redirection.rb +30 -0
  5. data/lib/breezy/xhr_headers.rb +0 -10
  6. data/lib/generators/rails/breezy_generator.rb +13 -6
  7. data/lib/generators/rails/templates/_form.json.props +13 -0
  8. data/lib/generators/rails/templates/controller.rb.tt +0 -3
  9. data/lib/generators/rails/templates/edit.json.props +14 -0
  10. data/lib/generators/rails/templates/{index.js.props → index.json.props} +1 -2
  11. data/lib/generators/rails/templates/new.json.props +15 -0
  12. data/lib/generators/rails/templates/{show.js.props → show.json.props} +0 -2
  13. data/lib/generators/rails/templates/web/edit.html.erb +7 -0
  14. data/lib/generators/rails/templates/web/edit.jsx +21 -28
  15. data/lib/generators/rails/templates/web/index.html.erb +7 -0
  16. data/lib/generators/rails/templates/web/index.jsx +10 -7
  17. data/lib/generators/rails/templates/web/new.html.erb +7 -0
  18. data/lib/generators/rails/templates/web/new.jsx +18 -25
  19. data/lib/generators/rails/templates/web/show.html.erb +7 -0
  20. data/lib/generators/rails/templates/web/show.jsx +3 -4
  21. data/lib/install/templates/web/action_creators.js +14 -12
  22. data/lib/install/templates/web/actions.js +4 -1
  23. data/lib/install/templates/web/application.js +173 -47
  24. data/lib/install/templates/web/application.json.props +26 -0
  25. data/lib/install/templates/web/application_visit.js +65 -0
  26. data/lib/install/templates/web/initializer.rb +1 -15
  27. data/lib/install/templates/web/reducer.js +62 -9
  28. data/lib/install/web.rb +23 -61
  29. data/lib/tasks/install.rake +11 -1
  30. data/test/helpers_test.rb +9 -17
  31. data/test/render_test.rb +19 -95
  32. data/test/test_helper.rb +1 -1
  33. metadata +25 -28
  34. data/app/views/breezy/response.html.erb +0 -0
  35. data/lib/breezy/configuration.rb +0 -35
  36. data/lib/breezy/render.rb +0 -37
  37. data/lib/generators/rails/templates/edit.js.props +0 -16
  38. data/lib/generators/rails/templates/new.js.props +0 -4
  39. data/lib/generators/rails/templates/web/base.jsx +0 -11
  40. data/lib/generators/rails/templates/web/form.jsx +0 -31
  41. data/lib/install/templates/web/babelrc +0 -35
  42. data/test/breezy_test.rb +0 -125
  43. data/test/configuration_test.rb +0 -36
@@ -0,0 +1,30 @@
1
+ module Breezy
2
+ module Redirection
3
+ def _compute_redirect_to_location(request, options)
4
+ computed_location = URI.parse(super)
5
+ next_param = Rack::Utils
6
+ .parse_nested_query(computed_location.query)
7
+
8
+ if request.params[:__] == "0"
9
+ computed_location.query = next_param.merge({__: "0"}).to_query
10
+ end
11
+
12
+ computed_location.to_s
13
+ end
14
+
15
+ def redirect_back_with_bzq(opts)
16
+ if request.referrer && params[:bzq]
17
+ referrer_url = URI.parse(request.referrer)
18
+ referrer_url.query = Rack::Utils
19
+ .parse_nested_query(referrer_url.query)
20
+ .merge({bzq: params[:bzq]})
21
+ .to_query
22
+
23
+ redirect_to referrer_url.to_s, opts
24
+ else
25
+ redirect_back(opts)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -17,17 +17,7 @@ module Breezy
17
17
  end
18
18
  end
19
19
 
20
- if request.xhr? && request.headers["X-BREEZY-REQUEST"]
21
- self.status = 200
22
- response.headers["X-BREEZY-LOCATION"] = url
23
- end
24
-
25
20
  url
26
21
  end
27
-
28
- private
29
- def set_response_url
30
- response.headers['X-RESPONSE-URL'] = request.fullpath
31
- end
32
22
  end
33
23
  end
@@ -21,6 +21,7 @@ module Rails
21
21
  filename = filename_with_extensions(view)
22
22
  template filename, File.join('app/views', controller_file_path, filename)
23
23
  end
24
+ template '_form.json.props', File.join('app/views', controller_file_path, '_form.json.props')
24
25
 
25
26
  %w(index show new edit).each do |view|
26
27
  @action_name = view
@@ -28,9 +29,11 @@ module Rails
28
29
  template 'web/' + filename, File.join('app/views', controller_file_path, filename)
29
30
  end
30
31
 
31
- js_filename = [plural_table_name, 'form.jsx'].map(&:camelcase).join
32
- template 'web/form.jsx', File.join('app/components', js_filename)
33
- template 'web/base.jsx', File.join('app/components', 'BaseScreen.jsx')
32
+ %w(index show new edit).each do |view|
33
+ @action_name = view
34
+ filename = filename_with_html_extensions(view)
35
+ template 'web/' + filename, File.join('app/views', controller_file_path, filename)
36
+ end
34
37
 
35
38
  %w(index show new edit).each do |view|
36
39
  append_mapping(view)
@@ -48,8 +51,8 @@ module Rails
48
51
  "\nimport #{component_name} from 'views/#{controller_file_path}/#{action}'"
49
52
  end
50
53
 
51
- inject_into_file app_js, after: 'const screenToComponentMapping = {' do
52
- "\n '#{[controller_file_path, action].join('/')}': #{component_name},"
54
+ inject_into_file app_js, after: 'identifierToComponentMapping = {' do
55
+ "\n '#{[controller_file_path, action].join('/')}': #{component_name},"
53
56
  end
54
57
  end
55
58
 
@@ -62,13 +65,17 @@ module Rails
62
65
  end
63
66
 
64
67
  def filename_with_extensions(name)
65
- [name, :js, :props] * '.'
68
+ [name, :json, :props] * '.'
66
69
  end
67
70
 
68
71
  def filename_with_jsx_extensions(name)
69
72
  [name, :jsx] * '.'
70
73
  end
71
74
 
75
+ def filename_with_html_extensions(name)
76
+ [name, :html, :erb] * '.'
77
+ end
78
+
72
79
  def attributes_list_with_timestamps
73
80
  attributes_list(attributes_names + %w(created_at updated_at))
74
81
  end
@@ -0,0 +1,13 @@
1
+ html = form_with(model: @<%= model_resource_name %>, local: true) do |form|
2
+ inner = "".html_safe
3
+
4
+ <%- attributes.each do |attr| -%>
5
+ inner << form.label(:<%= attr.column_name %>)
6
+ inner << form.<%= attr.field_type %>(:<%= attr.column_name %>)
7
+ <%- end -%>
8
+ inner << form.submit
9
+
10
+ inner
11
+ end
12
+
13
+ json.html html
@@ -5,9 +5,6 @@ require_dependency "<%= namespaced_path %>/application_controller"
5
5
  <% module_namespacing do -%>
6
6
  class <%= controller_class_name %>Controller < ApplicationController
7
7
  before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
8
- # `use_breezy` enables breezy functionality
9
- # on application.html.erb
10
- before_action :use_breezy
11
8
 
12
9
  # GET <%= route_url %>
13
10
  def index
@@ -0,0 +1,14 @@
1
+ if @post.errors.any?
2
+ content = {
3
+ explanation: "#{pluralize(@<%= singular_table_name %>.errors.count, "error")} prohibited this post from being saved:",
4
+ messages: @<%= singular_table_name %>.errors.full_messages.map{|msg| {body: msg}}
5
+ }
6
+
7
+ flash.now[:form_error] = content
8
+ end
9
+
10
+ json.form(partial: 'form') do
11
+ end
12
+
13
+ json.<%= singular_table_name%>_path <%= singular_table_name%>_path(@<%=singular_table_name%>)
14
+ json.<%= plural_table_name %>_path <%= plural_table_name %>_path
@@ -1,5 +1,3 @@
1
- json.flash flash.to_h
2
-
3
1
  json.<%= plural_table_name %> do
4
2
  json.array! @<%= plural_table_name %> do |<%= singular_table_name %>|
5
3
  <%- attributes_list_with_timestamps.each do |attr| -%>
@@ -7,6 +5,7 @@ json.<%= plural_table_name %> do
7
5
  <%- end -%>
8
6
  json.edit_<%=singular_table_name%>_path edit_<%=singular_table_name%>_path(<%=singular_table_name%>)
9
7
  json.<%=singular_table_name%>_path <%=singular_table_name%>_path(<%=singular_table_name%>)
8
+ json.delete_<%=singular_table_name%>_path <%=singular_table_name%>_path(<%=singular_table_name%>)
10
9
  end
11
10
  end
12
11
 
@@ -0,0 +1,15 @@
1
+ if @post.errors.any?
2
+ content = {
3
+ explanation: "#{pluralize(@<%= singular_table_name %>.errors.count, "error")} prohibited this post from being saved:",
4
+ messages: @<%= singular_table_name %>.errors.full_messages.map{|msg| {body: msg}}
5
+ }
6
+
7
+ flash.now[:form_error] = content
8
+ end
9
+
10
+ json.form(partial: 'form') do
11
+ end
12
+
13
+ json.<%= plural_table_name %>_path <%= plural_table_name %>_path
14
+
15
+
@@ -1,5 +1,3 @@
1
- json.flash flash.to_h
2
-
3
1
  <%- attributes_list_with_timestamps.each do |attr|-%>
4
2
  json.<%=attr%> @<%= singular_table_name %>.<%=attr%>
5
3
  <%- end -%>
@@ -0,0 +1,7 @@
1
+ <%% initial_state = controller.render_to_string(@virtual_path ,formats: [:json], locals: local_assigns, layout: true) %>
2
+
3
+ <script type="text/javascript">
4
+ window.BREEZY_INITIAL_PAGE_STATE=<%%= initial_state.html_safe %>;
5
+ </script>
6
+
7
+ <div id="app"></div>
@@ -1,39 +1,32 @@
1
1
  import React from 'react'
2
2
  import {mapStateToProps, mapDispatchToProps} from '@jho406/breezy'
3
3
  import {connect} from 'react-redux'
4
- import BaseScreen from 'components/BaseScreen'
5
- import <%= plural_table_name.camelize %>Form from 'components/<%= plural_table_name.camelize %>Form'
4
+ import RailsTag from '@jho406/breezy/components/RailsTag'
6
5
  import * as applicationActionCreators from 'javascript/packs/action_creators'
7
6
 
8
- class <%= plural_table_name.camelize %>Edit extends BaseScreen {
9
- formRef = React.createRef()
10
-
11
- handleSubmit = (values, {setSubmitting}) => {
12
- this.props.clearFormErrors(this.props.pageKey)
13
-
14
- const options = {
15
- method:'PUT',
16
- body: JSON.stringify(values),
17
- }
18
-
19
- this.enhancedVisit(this.props.<%= singular_table_name.camelize(:lower) %>Path, options).then( rsp => {
20
- setSubmitting(false)
21
- if (this.props.errors) {
22
- this.formRef.current.setErrors(this.props.errors)
23
- }
24
- })
25
- }
26
-
7
+ class <%= plural_table_name.camelize %>Edit extends React.Component {
27
8
  render () {
9
+ const {
10
+ form,
11
+ flash,
12
+ <%= singular_table_name.camelize(:lower) %>Path,
13
+ <%= plural_table_name.camelize(:lower) %>Path,
14
+ } = this.props
15
+ const error = flash.form_error
16
+
17
+ const messagesEl = error && (
18
+ <div id="error_explanation">
19
+ <h2>{ error.explanation }</h2>
20
+ <ul>{ error.messages.map(({body})=> <li key={body}>{body}</li>) }</ul>
21
+ </div>
22
+ )
23
+
28
24
  return (
29
25
  <div>
30
- <<%= plural_table_name.camelize %>Form
31
- onSubmit={this.handleSubmit}
32
- initialValues={this.props.attributesForForm}
33
- ref={this.formRef}
34
- />
35
- <a onClick={ e => this.enhancedVisit(this.props.<%= singular_table_name.camelize(:lower) %>Path)}>Show</a>
36
- <a onClick={ e => this.enhancedVisit(this.props.<%= plural_table_name.camelize(:lower) %>Path)}>Back</a>
26
+ {messagesEl}
27
+ <RailsTag {...form} data-bz-visit={true}/>
28
+ <a href={<%= singular_table_name.camelize(:lower) %>Path} data-bz-visit={true}>Show</a>
29
+ <a href={<%= plural_table_name.camelize(:lower) %>Path} data-bz-visit={true}>Back</a>
37
30
  </div>
38
31
  )
39
32
  }
@@ -0,0 +1,7 @@
1
+ <%% initial_state = controller.render_to_string(@virtual_path ,formats: [:json], locals: local_assigns, layout: true) %>
2
+
3
+ <script type="text/javascript">
4
+ window.BREEZY_INITIAL_PAGE_STATE=<%%= initial_state.html_safe %>;
5
+ </script>
6
+
7
+ <div id="app"></div>
@@ -1,30 +1,33 @@
1
1
  import React from 'react'
2
2
  import {mapStateToProps, mapDispatchToProps} from '@jho406/breezy'
3
3
  import { connect } from 'react-redux'
4
- import BaseScreen from 'components/BaseScreen'
5
4
 
6
- class <%= plural_table_name.camelize %>Index extends BaseScreen {
5
+ class <%= plural_table_name.camelize %>Index extends React.Component {
7
6
  static defaultProps = {
8
7
  <%= plural_table_name.camelize(:lower) %>: []
9
8
  }
10
9
 
11
10
  render () {
11
+ const {
12
+ flash,
13
+ new<%= singular_table_name.camelize %>Path,
14
+ } = this.props
12
15
  const <%= singular_table_name.camelize(:lower) %>Items = this.props.<%= plural_table_name.camelize(:lower) %>.map((<%= singular_table_name.camelize(:lower) %>, key) => {
13
16
  return (
14
17
  <tr key={<%= singular_table_name.camelize(:lower) %>.id}>
15
18
  <%- attributes_list.select{|attr| attr != :id }.each do |attr| -%>
16
19
  <td>{<%=singular_table_name.camelize(:lower)%>.<%=attr.camelize(:lower)%>}</td>
17
20
  <%- end -%>
18
- <td><a onClick={ e => this.enhancedVisit(<%=singular_table_name%>.<%=singular_table_name.camelize(:lower)%>Path)}>Show</a></td>
19
- <td><a onClick={ e => this.enhancedVisit(<%=singular_table_name%>.edit<%=singular_table_name.camelize%>Path)}>Edit</a></td>
20
- <td><a onClick={ e => this.enhancedVisit(<%=singular_table_name%>.<%=singular_table_name.camelize(:lower)%>Path, {method: 'DELETE'})}>Delete</a></td>
21
+ <td><a href={ <%=singular_table_name%>.<%=singular_table_name.camelize(:lower)%>Path } data-bz-visit={true}>Show</a></td>
22
+ <td><a href={ <%=singular_table_name%>.edit<%=singular_table_name.camelize%>Path } data-bz-visit={true}>Edit</a></td>
23
+ <td><a href={ <%=singular_table_name%>.delete<%=singular_table_name.camelize%>Path }data-bz-visit={true} data-bz-method={"DELETE"}>Delete</a></td>
21
24
  </tr>
22
25
  )
23
26
  })
24
27
 
25
28
  return (
26
29
  <div>
27
- <p id="notice">{this.props.flash && this.props.flash.notice}</p>
30
+ <p id="notice">{flash.notice}</p>
28
31
 
29
32
  <h1><%= plural_table_name.capitalize %></h1>
30
33
 
@@ -43,7 +46,7 @@ class <%= plural_table_name.camelize %>Index extends BaseScreen {
43
46
  </tbody>
44
47
  </table>
45
48
  <br />
46
- <a onClick={ e => this.enhancedVisit(this.props.new<%= singular_table_name.camelize %>Path)}>New <%= singular_table_name.capitalize %></a>
49
+ <a href={new<%= singular_table_name.camelize %>Path} data-bz-visit={true}>New <%= singular_table_name.capitalize %></a>
47
50
  </div>
48
51
  )
49
52
  }
@@ -0,0 +1,7 @@
1
+ <%% initial_state = controller.render_to_string(@virtual_path ,formats: [:json], locals: local_assigns, layout: true) %>
2
+
3
+ <script type="text/javascript">
4
+ window.BREEZY_INITIAL_PAGE_STATE=<%%= initial_state.html_safe %>;
5
+ </script>
6
+
7
+ <div id="app"></div>
@@ -1,37 +1,30 @@
1
1
  import React from 'react'
2
2
  import {mapStateToProps, mapDispatchToProps} from '@jho406/breezy'
3
3
  import {connect} from 'react-redux'
4
- import BaseScreen from 'components/BaseScreen'
5
- import <%= plural_table_name.camelize %>Form from 'components/<%= plural_table_name.camelize %>Form'
4
+ import RailsTag from '@jho406/breezy/components/RailsTag'
6
5
  import * as applicationActionCreators from 'javascript/packs/action_creators'
7
6
 
8
- class <%= plural_table_name.camelize %>New extends BaseScreen {
9
- formRef = React.createRef()
10
-
11
- handleSubmit = (values, {setSubmitting}) => {
12
- this.props.clearFormErrors(this.props.pageKey)
13
-
14
- const options = {
15
- method:'POST',
16
- body: JSON.stringify(values),
17
- }
7
+ class <%= plural_table_name.camelize %>New extends React.Component {
8
+ render () {
9
+ const {
10
+ form,
11
+ flash,
12
+ <%= plural_table_name.camelize(:lower) %>Path,
13
+ } = this.props
14
+ const error = flash.form_error
18
15
 
19
- this.enhancedVisit(this.props.<%= plural_table_name.camelize(:lower) %>Path, options).then( rsp => {
20
- setSubmitting(false)
21
- if (this.props.errors) {
22
- this.formRef.current.setErrors(this.props.errors)
23
- }
24
- })
25
- }
16
+ const messagesEl = error && (
17
+ <div id="error_explanation">
18
+ <h2>{ error.explanation }</h2>
19
+ <ul>{ error.messages.map(({body})=> <li key={body}>{body}</li>) }</ul>
20
+ </div>
21
+ )
26
22
 
27
- render () {
28
23
  return (
29
24
  <div>
30
- <<%= plural_table_name.camelize %>Form
31
- onSubmit={this.handleSubmit}
32
- ref={this.formRef}
33
- />
34
- <a onClick={ e => this.enhancedVisit(this.props.<%= plural_table_name.camelize(:lower) %>Path)}>Back</a>
25
+ {messagesEl}
26
+ <RailsTag {...this.props.form} data-bz-visit={true}/>
27
+ <a href={<%= plural_table_name.camelize(:lower) %>Path} data-bz-visit={true}>Back</a>
35
28
  </div>
36
29
  )
37
30
  }
@@ -0,0 +1,7 @@
1
+ <%% initial_state = controller.render_to_string(@virtual_path ,formats: [:json], locals: local_assigns, layout: true) %>
2
+
3
+ <script type="text/javascript">
4
+ window.BREEZY_INITIAL_PAGE_STATE=<%%= initial_state.html_safe %>;
5
+ </script>
6
+
7
+ <div id="app"></div>
@@ -1,9 +1,8 @@
1
1
  import React from 'react'
2
2
  import {mapStateToProps, mapDispatchToProps} from '@jho406/breezy'
3
3
  import { connect } from 'react-redux'
4
- import BaseScreen from 'components/BaseScreen'
5
4
 
6
- class <%= plural_table_name.camelize %>Show extends BaseScreen {
5
+ class <%= plural_table_name.camelize %>Show extends React.Component {
7
6
  render () {
8
7
  return (
9
8
  <div>
@@ -14,8 +13,8 @@ class <%= plural_table_name.camelize %>Show extends BaseScreen {
14
13
  {this.props.<%=attr.camelize(:lower)%>}
15
14
  </p>
16
15
  <%- end -%>
17
- <a onClick={ e => this.enhancedVisit(this.props.edit<%= singular_table_name.camelize %>Path)}>Edit</a>
18
- <a onClick={ e => this.enhancedVisit(this.props.<%= plural_table_name.camelize(:lower) %>Path )}>Back</a>
16
+ <a href={ this.props.edit<%= singular_table_name.camelize %>Path } data-bz-visit={true}>Edit</a>
17
+ <a href={ this.props.<%= plural_table_name.camelize(:lower) %>Path } data-bz-visit={true}>Back</a>
19
18
  </div>
20
19
  )
21
20
  }
@@ -1,12 +1,14 @@
1
- import {
2
- CLEAR_FORM_ERRORS
3
- } from './actions'
4
-
5
- export function clearFormErrors(pageKey) {
6
- return {
7
- type: CLEAR_FORM_ERRORS,
8
- payload: {
9
- pageKey,
10
- }
11
- }
12
- }
1
+ // Example:
2
+ //
3
+ // import {
4
+ // CLEAR_FORM_ERRORS
5
+ // } from './actions'
6
+ //
7
+ // export function clearFormErrors(pageKey) {
8
+ // return {
9
+ // type: CLEAR_FORM_ERRORS,
10
+ // payload: {
11
+ // pageKey,
12
+ // }
13
+ // }
14
+ // }
@@ -1 +1,4 @@
1
- export const CLEAR_FORM_ERRORS = 'CLEAR_FORM_ERRORS'
1
+ // Example:
2
+ //
3
+ // export const CLEAR_FORM_ERRORS = 'CLEAR_FORM_ERRORS'
4
+ export const REHYDRATE = 'persist/REHYDRATE'
@@ -4,61 +4,187 @@ import reduceReducers from 'reduce-reducers'
4
4
  import thunk from 'redux-thunk'
5
5
  import { Provider } from 'react-redux'
6
6
  import { render } from 'react-dom'
7
- import createHistory from 'history/createBrowserHistory'
8
- import Breezy from '@jho406/breezy'
9
- import Nav from '@jho406/breezy/dist/NavComponent'
10
- import applicationReducer from './reducer'
7
+ import { createBrowserHistory, createMemoryHistory } from 'history'
8
+ import { start } from '@jho406/breezy'
9
+ import Nav from '@jho406/breezy/components/NavComponent'
10
+ import { ujsHandlers } from '@jho406/breezy/utils'
11
+ import { persistStore, persistReducer } from 'redux-persist'
12
+ import storage from 'redux-persist/lib/storage'
13
+ import { applicationRootReducer, applicationPagesReducer } from './reducer'
14
+ import { buildVisitAndRemote } from './application_visit'
11
15
 
16
+ if(typeof window !== 'undefined' ) {
17
+ document.addEventListener("DOMContentLoaded", function() {
18
+ const appEl = document.getElementById('app')
19
+ const location = window.location
12
20
 
13
- // Mapping between your props template to Component
14
- // e.g {'posts/new': PostNew}
15
- const screenToComponentMapping = {
21
+ if (appEl) {
22
+ render(
23
+ <Application
24
+ appEl={appEl}
25
+ // The base url is an optional prefix to all calls made by the `visit`
26
+ // and `remote` thunks.
27
+ baseUrl={''}
28
+ // The global var BREEZY_INITIAL_PAGE_STATE is set by your erb
29
+ // template, e.g., index.html.erb
30
+ initialPage={window.BREEZY_INITIAL_PAGE_STATE}
31
+ // The initial path of the page, e.g., /foobar
32
+ path={location.pathname + location.search + location.hash}
33
+ />, appEl)
34
+ }
35
+ })
16
36
  }
17
37
 
18
- const history = createHistory({})
19
- const initialPage = window.BREEZY_INITIAL_PAGE_STATE
20
- const baseUrl = ''
21
-
22
- //The Nav is pretty bare bones
23
- //Feel free to replace the implementation
24
- const {reducer, initialState, initialPageKey, connect} = Breezy.start({
25
- window,
26
- initialPage,
27
- baseUrl,
28
- history
29
- })
30
-
31
- const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
32
-
33
- const {
34
- breezy: breezyReducer,
35
- pages: pagesReducer,
36
- } = reducer
37
-
38
- const store = createStore(
39
- combineReducers({
40
- breezy: breezyReducer,
41
- pages: reduceReducers(pagesReducer, applicationReducer),
42
- }),
43
- initialState,
44
- composeEnhancers(applyMiddleware(thunk))
45
- )
46
-
47
- connect(store)
48
-
49
- class App extends React.Component {
38
+ export default class Application extends React.Component {
39
+ constructor(props) {
40
+ super(props)
41
+ this.hasWindow = typeof window !== 'undefined'
42
+
43
+ // Mapping between your props template to Component, you must add to this
44
+ // to register any new page level component you create. If you are using the
45
+ // scaffold, it will auto append the identifers for you.
46
+ //
47
+ // e.g {'posts/new': PostNew}
48
+ this.identifierToComponentMapping = {
49
+ }
50
+
51
+ // Create a navigator Ref for UJS attributes and to enhance the base `visit`
52
+ // and `visit` thunks
53
+ this.navigatorRef = React.createRef()
54
+
55
+ // Start Breezy and return an object to prepare the Redux store
56
+ const breezy = start({
57
+ initialPage: this.props.initialPage,
58
+ baseUrl: this.props.baseUrl,
59
+ path: this.props.path,
60
+ fetch: this.hasWindow ? window.fetch : undefined,
61
+ })
62
+ this.breezy = breezy
63
+
64
+ // Build the store and pass Breezy's provided reducer to be combined with
65
+ // your reducers located at `application_reducer.js`
66
+ const {initialState, reducer} = breezy
67
+ this.store = this.buildStore(initialState, reducer)
68
+
69
+ // Fire initial events and populate the store
70
+ breezy.prepareStore(this.store)
71
+
72
+ // Build visit and remote thunks
73
+ // Your modified `visit` and `remote` will get passed below to the
74
+ // NavComponent then to your components through the provided
75
+ // mapDispatchToProps.
76
+ //
77
+ // You can access them via `this.props.visit` or `this.props.remote`. In
78
+ // your page components
79
+ const {visit, remote} = buildVisitAndRemote(this.navigatorRef, this.store)
80
+ this.visit = visit
81
+ this.remote = remote
82
+ }
83
+
84
+ componentDidMount() {
85
+ const { appEl } = this.props
86
+ // Create the ujs event handlers. You can change the ujsAttributePrefix
87
+ // in the event the data attribute conflicts with another.
88
+ this.ujsHandlers = ujsHandlers({
89
+ visit: this.visit,
90
+ remote: this.remote,
91
+ store: this.store,
92
+ ujsAttributePrefix: 'data-bz'
93
+ })
94
+ const {onClick, onSubmit} = this.ujsHandlers
95
+
96
+ appEl.addEventListener('click', onClick)
97
+ appEl.addEventListener('submit', onSubmit)
98
+ }
99
+
100
+ componentWillUnmount() {
101
+ const { appEl } = this.props
102
+ const {onClick, onSubmit} = this.ujsHandlers
103
+
104
+ appEl.removeEventListener('click', onClick)
105
+ appEl.removeEventListener('submit', onSubmit)
106
+ this.breezy.stop()
107
+ }
108
+
109
+ buildStore(initialState, {breezy: breezyReducer, pages: pagesReducer}) {
110
+ // Create the store
111
+ // See `./reducer.js` for an explaination of the two included reducers
112
+ const composeEnhancers = (this.hasWindow && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;
113
+ const reducer = this.wrapWithPersistReducer(
114
+ reduceReducers(
115
+ combineReducers({
116
+ breezy: breezyReducer,
117
+ pages: reduceReducers(pagesReducer, applicationPagesReducer),
118
+ }),
119
+ applicationRootReducer
120
+ )
121
+ )
122
+ const store = createStore(
123
+ reducer,
124
+ initialState,
125
+ composeEnhancers(applyMiddleware(thunk))
126
+ )
127
+
128
+ if(this.hasWindow) {
129
+ // Persist the store using Redux-Persist
130
+ persistStore(store)
131
+ }
132
+
133
+ return store
134
+ }
135
+
136
+ wrapWithPersistReducer(reducers) {
137
+ // Redux Persist settings
138
+ // The key is set to the stringified JS asset path to remove the need for
139
+ // migrations when hydrating.
140
+ if (!this.hasWindow) {
141
+ return reducers
142
+ }
143
+ const prefix = 'breezy'
144
+ const persistKey = prefix + this.props.initialPage.assets.filter( asset => asset.endsWith('.js')).join(",")
145
+ const persistConfig = {
146
+ key: persistKey,
147
+ storage,
148
+ }
149
+
150
+ // Remove older storage items that were used by previous JS assets
151
+ if (this.hasWindow) {
152
+ const storedKeys = Object.keys(localStorage)
153
+ storedKeys.forEach((key) => {
154
+ if (key.startsWith(`persist:${prefix}`) && key !== persistKey) {
155
+ localStorage.removeItem(key)
156
+ }
157
+ })
158
+ }
159
+
160
+ return persistReducer(persistConfig, reducers)
161
+ }
162
+
163
+ createHistory() {
164
+ if(this.hasWindow) {
165
+ // This is used for client side rendering
166
+ return createBrowserHistory({})
167
+ } else {
168
+ // This is used for server side rendering
169
+ return createMemoryHistory({})
170
+ }
171
+ }
172
+
50
173
  render() {
51
- return <Provider store={store}>
174
+ const history = this.createHistory()
175
+
176
+ // The Nav component is pretty bare and can be inherited from for custom
177
+ // behavior or replaced with your own.
178
+ return <Provider store={this.store}>
52
179
  <Nav
53
- store={store}
54
- mapping={this.props.mapping}
180
+ store={this.store}
181
+ ref={this.navigatorRef}
182
+ visit={this.visit}
183
+ remote={this.remote}
184
+ mapping={this.identifierToComponentMapping}
55
185
  history={history}
56
- initialPageKey={initialPageKey}
186
+ initialPageKey={this.breezy.initialPageKey}
57
187
  />
58
188
  </Provider>
59
189
  }
60
190
  }
61
-
62
- document.addEventListener("DOMContentLoaded", function() {
63
- render(<App mapping={screenToComponentMapping}/>, document.getElementById('app'))
64
- })