ObjectStateMutations.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /**
  2. * Copyright (c) 2015-present, Parse, LLC.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. * @flow
  10. */
  11. import encode from './encode';
  12. import ParseFile from './ParseFile';
  13. import ParseObject from './ParseObject';
  14. import ParseRelation from './ParseRelation';
  15. import TaskQueue from './TaskQueue';
  16. import { RelationOp } from './ParseOp';
  17. /*:: import type { Op } from './ParseOp';*/
  18. /*:: export type AttributeMap = { [attr: string]: any };*/
  19. /*:: export type OpsMap = { [attr: string]: Op };*/
  20. /*:: export type ObjectCache = { [attr: string]: string };*/
  21. /*:: export type State = {
  22. serverData: AttributeMap;
  23. pendingOps: Array<OpsMap>;
  24. objectCache: ObjectCache;
  25. tasks: TaskQueue;
  26. existed: boolean
  27. };*/
  28. export function defaultState()
  29. /*: State*/
  30. {
  31. return {
  32. serverData: {},
  33. pendingOps: [{}],
  34. objectCache: {},
  35. tasks: new TaskQueue(),
  36. existed: false
  37. };
  38. }
  39. export function setServerData(serverData
  40. /*: AttributeMap*/
  41. , attributes
  42. /*: AttributeMap*/
  43. ) {
  44. for (const attr in attributes) {
  45. if (typeof attributes[attr] !== 'undefined') {
  46. serverData[attr] = attributes[attr];
  47. } else {
  48. delete serverData[attr];
  49. }
  50. }
  51. }
  52. export function setPendingOp(pendingOps
  53. /*: Array<OpsMap>*/
  54. , attr
  55. /*: string*/
  56. , op
  57. /*: ?Op*/
  58. ) {
  59. const last = pendingOps.length - 1;
  60. if (op) {
  61. pendingOps[last][attr] = op;
  62. } else {
  63. delete pendingOps[last][attr];
  64. }
  65. }
  66. export function pushPendingState(pendingOps
  67. /*: Array<OpsMap>*/
  68. ) {
  69. pendingOps.push({});
  70. }
  71. export function popPendingState(pendingOps
  72. /*: Array<OpsMap>*/
  73. )
  74. /*: OpsMap*/
  75. {
  76. const first = pendingOps.shift();
  77. if (!pendingOps.length) {
  78. pendingOps[0] = {};
  79. }
  80. return first;
  81. }
  82. export function mergeFirstPendingState(pendingOps
  83. /*: Array<OpsMap>*/
  84. ) {
  85. const first = popPendingState(pendingOps);
  86. const next = pendingOps[0];
  87. for (const attr in first) {
  88. if (next[attr] && first[attr]) {
  89. const merged = next[attr].mergeWith(first[attr]);
  90. if (merged) {
  91. next[attr] = merged;
  92. }
  93. } else {
  94. next[attr] = first[attr];
  95. }
  96. }
  97. }
  98. export function estimateAttribute(serverData
  99. /*: AttributeMap*/
  100. , pendingOps
  101. /*: Array<OpsMap>*/
  102. , className
  103. /*: string*/
  104. , id
  105. /*: ?string*/
  106. , attr
  107. /*: string*/
  108. )
  109. /*: mixed*/
  110. {
  111. let value = serverData[attr];
  112. for (let i = 0; i < pendingOps.length; i++) {
  113. if (pendingOps[i][attr]) {
  114. if (pendingOps[i][attr] instanceof RelationOp) {
  115. if (id) {
  116. value = pendingOps[i][attr].applyTo(value, {
  117. className: className,
  118. id: id
  119. }, attr);
  120. }
  121. } else {
  122. value = pendingOps[i][attr].applyTo(value);
  123. }
  124. }
  125. }
  126. return value;
  127. }
  128. export function estimateAttributes(serverData
  129. /*: AttributeMap*/
  130. , pendingOps
  131. /*: Array<OpsMap>*/
  132. , className
  133. /*: string*/
  134. , id
  135. /*: ?string*/
  136. )
  137. /*: AttributeMap*/
  138. {
  139. const data = {};
  140. for (var attr in serverData) {
  141. data[attr] = serverData[attr];
  142. }
  143. for (let i = 0; i < pendingOps.length; i++) {
  144. for (attr in pendingOps[i]) {
  145. if (pendingOps[i][attr] instanceof RelationOp) {
  146. if (id) {
  147. data[attr] = pendingOps[i][attr].applyTo(data[attr], {
  148. className: className,
  149. id: id
  150. }, attr);
  151. }
  152. } else {
  153. if (attr.includes('.')) {
  154. // convert a.b.c into { a: { b: { c: value } } }
  155. const fields = attr.split('.');
  156. const last = fields[fields.length - 1];
  157. let object = Object.assign({}, data);
  158. for (let i = 0; i < fields.length - 1; i++) {
  159. object = object[fields[i]];
  160. }
  161. object[last] = pendingOps[i][attr].applyTo(object[last]);
  162. } else {
  163. data[attr] = pendingOps[i][attr].applyTo(data[attr]);
  164. }
  165. }
  166. }
  167. }
  168. return data;
  169. }
  170. export function commitServerChanges(serverData
  171. /*: AttributeMap*/
  172. , objectCache
  173. /*: ObjectCache*/
  174. , changes
  175. /*: AttributeMap*/
  176. ) {
  177. for (const attr in changes) {
  178. const val = changes[attr];
  179. serverData[attr] = val;
  180. if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof ParseFile) && !(val instanceof ParseRelation)) {
  181. const json = encode(val, false, true);
  182. objectCache[attr] = JSON.stringify(json);
  183. }
  184. }
  185. }