Source: components/traits/Queryable.js

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import Trait from './Trait';
  4. import { get, camelCase, snakeCase } from 'lodash';
  5. import { app_get, snake_case, def } from '../../helpers';
  6. import Shell from '../Shell';
  7. import ReactSync from '../../ReactSync';
  8. import collect from '../../collect.js';
  9. import axios from '../../fetchClient';
  10. import { dispatch, on } from '../../Event.js';
  11. window.Find = (dotstring) => {
  12. return get(ReactSyncAppData.page_data.state, dotstring);
  13. };
  14. /**
  15. @kind mixin
  16. @extends Trait
  17. */
  18. class Queryable extends Trait{
  19. static repo = ReactSyncAppData.page_data.state;
  20. static hasBeenKeyed = false;
  21. static setKeyed(){
  22. }
  23. /**
  24. * (STATIC) - Query the data store and return the model with the supplied id
  25. * @static
  26. */
  27. static find(id){
  28. const { plural, repo } = this;
  29. const dotstring = `${plural}.${id}`;
  30. return app_get(dotstring);
  31. }
  32. /**
  33. * (STATIC) -
  34. * @static
  35. */
  36. static where(...args){
  37. this.refresher = React.createRef();
  38. return collect(appGet(this.plural)).where(...args);
  39. }
  40. /**
  41. * (STATIC) - Return all models from the data store
  42. * @static
  43. */
  44. static list(additional_props = {}){
  45. return <Shell url={this.plural_url} Model={this} {...additional_props} />
  46. }
  47. /** */
  48. static all(additional_props = {}){
  49. debugger;
  50. this.refresher = React.createRef();
  51. const plural = this.plural;
  52. let items = app_get('state.'+plural) || app_get('state.'+ snakeCase(plural)) || app_get(plural) || app_get(snakeCase(plural));
  53. // debugger
  54. if(!items) return null;
  55. if(!('map' in items)) items = items.data;
  56. let els = items.map(e => {
  57. let props = {...e, ...additional_props};
  58. let ThisModel = this;
  59. return <ThisModel refresher={this.refresher} key={`${plural}${e.id}`} {...props} />;
  60. });
  61. return <>{els}</>
  62. }
  63. /** */
  64. static firstWhere(a, b){
  65. return null;
  66. let ThisModel = this;
  67. let props = {...this.props, ...additional_props};
  68. return <ThisModel refresher={this.refresher} key={`${this.plural}${this.props.id}`} {...props} />;
  69. }
  70. /** */
  71. static first(additional_props = {}){
  72. this.refresher = React.createRef();
  73. const plural = this.plural;
  74. let items = app_get('state.'+plural) || app_get('state.'+ snakeCase(plural)) || app_get(plural) || app_get(snakeCase(plural));
  75. if(!items.length) return null;
  76. let e = items[0];
  77. let props = {...e, ...additional_props};
  78. let ThisModel = this;
  79. return <ThisModel refresher={this.refresher} key={`${this.plural}${e.id}`} {...props} />;
  80. }
  81. /**
  82. */
  83. get save_path(){
  84. return this.api_url;
  85. }
  86. /**
  87. * Store the model's state back to the database
  88. */
  89. save(callback = false){
  90. const save_path = this.save_path || this.calculatedProperties.api_url;
  91. axios.put(save_path, this.filterMutable(this.state))
  92. .then(response => {
  93. this.refresh();
  94. react_sync_notification('Saved');
  95. if(callback) callback();
  96. })
  97. .catch(err => {
  98. console.error(err);
  99. react_sync_notification({text: 'An error occurred', level: 'danger'});
  100. });
  101. }
  102. /** */
  103. static get create_path(){
  104. return this.plural_url;
  105. }
  106. /**
  107. * Create a new model and insert into the database
  108. */
  109. static create(initialProps = {}){
  110. return new Promise((resolve, reject) => {
  111. axios.post(this.create_path, initialProps)
  112. .then(response => {
  113. const redirect_url = this.response_is_redirect(response);
  114. if(redirect_url){
  115. history.pushState(response.data, "", redirect_url);
  116. this.refresh_static();
  117. }
  118. else{
  119. this.refresh_static();
  120. }
  121. react_sync_notification('Saved');
  122. resolve(response.data);
  123. dispatch('model_created', new this(response.data));
  124. })
  125. .catch(err => {
  126. console.error(err);
  127. react_sync_notification({text: 'An error occurred', level: 'danger'});
  128. reject(err);
  129. });
  130. });
  131. }
  132. /**
  133. * Create a new model instance and store it to the database
  134. * @todo complete this method
  135. */
  136. /** */
  137. get delete_path(){
  138. return this.api_url;
  139. }
  140. /**
  141. * Delete a model
  142. */
  143. delete(callback = false){
  144. return new Promise((resolve, reject) => {
  145. axios.delete(this.delete_path)
  146. .then(response => {
  147. const redirect_url = this.constructor.response_is_redirect(response);
  148. if(redirect_url){
  149. history.pushState(response.data, "", redirect_url);
  150. this.refresh();
  151. }
  152. else{
  153. this.refresh();
  154. }
  155. react_sync_notification('Deleted');
  156. dispatch('model_deleted', response);
  157. resolve(response);
  158. if(typeof callback === 'function'){
  159. callback(response);
  160. }
  161. })
  162. .catch(err => {
  163. console.log(err);
  164. react_sync_notification({text: 'An error occurred', level: 'danger'});
  165. reject(err);
  166. });
  167. });
  168. }
  169. /** */
  170. static refresh_static(){
  171. Shell.cache = {};
  172. ReactSync.getInstance().update();
  173. }
  174. /** */
  175. refresh(){
  176. Shell.cache = {};
  177. if(this.props.refresh) this.props.refresh()
  178. else{
  179. ReactSync.getInstance().update();
  180. }
  181. }
  182. static response_is_redirect(response){
  183. const { href } = window.location;
  184. const { responseURL } = response.request;
  185. const { url } = response.config;
  186. console.log(url, responseURL);
  187. if(responseURL !== url && href !== responseURL){
  188. return responseURL;
  189. }
  190. return false;
  191. }
  192. }
  193. export default Queryable;