immer#Draft TypeScript Examples

The following examples show how to use immer#Draft. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: MessageValueViewReducer.ts    From Protoman with MIT License 6 votes vote down vote up
// Change Value Helpers

function changeValue(v: Draft<ProtobufValue>, segments: string[], value: string): void {
  switch (v.type.tag) {
    case 'message':
      return changeMessageValue(v as Draft<MessageValue>, segments, value);
    case 'primitive':
      return changePrimitiveValue(v as Draft<PrimitiveValue>, segments, value);
    case 'enum':
      return changeEnumValue(v as Draft<EnumValue>, segments, value);
  }
}
Example #2
Source File: panelReducer.ts    From diagram-maker with Apache License 2.0 6 votes vote down vote up
anchorPanel = (
  draftState: Draft<DiagramMakerPanels>,
  action: DragPanelAction,
) => {
  const containerSize = action.payload.viewContainerSize;
  const currentPanel = draftState[action.payload.id];
  const { x, y } = currentPanel.position;

  const rightDistance = Math.abs(containerSize.width - (x + currentPanel.size.width));
  const bottomDistance = Math.abs(containerSize.height - (y + currentPanel.size.height));

  if (x < ANCHOR_DISTANCE && y < ANCHOR_DISTANCE) {
    currentPanel.positionAnchor = PositionAnchor.TOP_LEFT;
    currentPanel.position = { x: 0, y: 0 };
  } else if (rightDistance < ANCHOR_DISTANCE && y < ANCHOR_DISTANCE) {
    currentPanel.positionAnchor = PositionAnchor.TOP_RIGHT;
    currentPanel.position = { x: 0, y: 0 };
  } else if (x < ANCHOR_DISTANCE && bottomDistance < ANCHOR_DISTANCE) {
    currentPanel.positionAnchor = PositionAnchor.BOTTOM_LEFT;
    currentPanel.position = { x: 0, y: 0 };
  } else if (rightDistance < ANCHOR_DISTANCE && bottomDistance < ANCHOR_DISTANCE) {
    currentPanel.positionAnchor = PositionAnchor.BOTTOM_RIGHT;
    currentPanel.position = { x: 0, y: 0 };
  }
}
Example #3
Source File: index.tsx    From mutik with MIT License 6 votes vote down vote up
export function createStore<S>(initialState: S): Store<S> {
  let listeners: Listener[] = [];
  let currentState = initialState;
  return {
    get() {
      return currentState;
    },
    set(nextState: S | UpdaterFn<S>) {
      currentState =
        typeof nextState === 'function'
          ? (nextState as UpdaterFn<S>)(currentState)
          : nextState;
      listeners.forEach(listener => listener());
    },
    on(listener: Listener) {
      listeners.push(listener);
      return () => this.off(listener);
    },
    off(listener: Listener) {
      listeners = listeners.filter(fn => fn !== listener);
    },
    reset() {
      this.set(initialState);
    },
    mutate(updater: (draft: Draft<S>) => void | S) {
      let currState = this.get();
      let nextState = immer(currState, updater);
      if (nextState !== currState) this.set(nextState as S);
    },
  };
}
Example #4
Source File: BulkReducer.ts    From Protoman with MIT License 6 votes vote down vote up
export default function BulkReducer(s: AppState, action: AnyAction): AppState {
  if (BulkActionTypes.includes(action.type)) {
    const a = action as BulkAction;

    switch (a.type) {
      case 'IMPORT_COLLECTION':
        return produce(s, draft => {
          const names = s.collections.map(([name]) => name);
          const prefix = 'Imported';
          let idx = 1;
          while (names.includes(prefix + idx)) idx++;
          console.log(a.collection);
          draft.collections.push([prefix + idx, a.collection as Draft<Collection>]);
        });
      default:
        return s;
    }
  }

  return s;
}
Example #5
Source File: workspaceReducer.ts    From diagram-maker with Apache License 2.0 6 votes vote down vote up
resizeReducer = (draftState: Draft<DiagramMakerWorkspace>, action: ResizeWorkspaceAction) => {
  const currentWorkspace = draftState;
  currentWorkspace.viewContainerSize = action.payload.containerSize;

  currentWorkspace.scale = clampZoom(
    currentWorkspace.scale,
    currentWorkspace.canvasSize,
    currentWorkspace.viewContainerSize,
  );

  currentWorkspace.position = clampPosition(
    currentWorkspace.position,
    currentWorkspace.canvasSize,
    currentWorkspace.scale,
    currentWorkspace.viewContainerSize,
  );
}
Example #6
Source File: MessageValueViewReducer.ts    From Protoman with MIT License 6 votes vote down vote up
function changeMessageValue(msg: Draft<MessageValue>, segments: string[], value: string): void {
  const fieldName = segments[0];
  const [field, repeatedField, oneOfField, mapField] = findField(msg, fieldName);

  if (field) {
    changeValue(field, segments.slice(1), value);
  } else if (repeatedField) {
    const idx = parseInt(segments[1]);
    changeValue(repeatedField[idx], segments.slice(2), value);
  } else if (oneOfField) {
    const [, selectedValue] = oneOfField;
    // although segments[1](subfieldName) isn't used,
    // it's included in the path just in case we later decide that we need it.
    // i.e. rather than keeping just the selected value,
    // we might keep all values to preserve user inputs.
    changeValue(selectedValue, segments.slice(2), value);
  } else if (mapField) {
    const idx = parseInt(segments[1]);
    const isKey = parseInt(segments[2]) === 0;
    if (isKey) {
      mapField[idx][0] = value;
    } else {
      changeValue(mapField[idx][1], segments.slice(3), value);
    }
  }
}
Example #7
Source File: slot.ts    From textbus with GNU General Public License v3.0 6 votes vote down vote up
/**
   * 更新插槽状态的方法
   * @param fn
   */
  updateState(fn: (draft: Draft<T>) => void): T {
    let changes!: Patch[]
    let inverseChanges!: Patch[]
    const oldState = this.state
    const newState = produce(oldState, fn, (p, ip) => {
      changes = p
      inverseChanges = ip
    })
    this.state = newState
    const applyAction: ApplyAction = {
      type: 'apply',
      patches: changes,
      value: newState
    }
    this.changeMarker.markAsDirtied({
      path: [],
      apply: [applyAction],
      unApply: [{
        type: 'apply',
        patches: inverseChanges,
        value: oldState
      }]
    })
    this.stateChangeEvent.next([applyAction])
    return newState!
  }
Example #8
Source File: data-store.ts    From VIR with MIT License 6 votes vote down vote up
private removeItemReducer = (draft: Draft<DataStoreState>,
                               itemID: ItemID) => {
    const item = draft.items.get(itemID)
    if (item === undefined) {
      throw new Error(`Item with ID ${itemID} does not exist`)
    }
    this._removeAllChildrenOf(draft, item)
    if (item.parentID === undefined) {
      removeValue(draft.rootItemIDs, itemID)
    } else {
      const parent = draft.items.get(item.parentID)
      if (parent === undefined) {
        throw new Error(`Parent ID ${item.parentID} does not exist`)
      }
      removeValue(parent.childrenIDs, itemID)
    }
    draft.items.delete(itemID)
    this.invalidateItemID(draft, itemID)
  }
Example #9
Source File: store.ts    From Protoman with MIT License 5 votes vote down vote up
export function procCol(collection: Draft<Collection>): Draft<Collection> {
  collection.buildError = undefined;
  collection.buildStatus = 'default';
  collection.flows = collection.flows.map(([fn, f]) => [fn, procFlow(f)]);
  return collection;
}
Example #10
Source File: edgeReducer.ts    From diagram-maker with Apache License 2.0 5 votes vote down vote up
export default function edgeReducer<NodeType, EdgeType>(
  state: DiagramMakerEdges<EdgeType> | undefined,
  action: DiagramMakerAction<NodeType, EdgeType>,
): DiagramMakerEdges<EdgeType> {
  if (state === undefined) {
    return {};
  }
  switch (action.type) {
    case GlobalActionsType.CREATE_ITEMS:
      return produce(state, (draftState) => {
        action.payload.edges.forEach((edge) => {
          draftState[edge.id] = edge as Draft<DiagramMakerEdge<EdgeType>>;
        });
      });
    case EdgeActionsType.EDGE_DELETE:
      return produce(state, (draftState) => {
        delete draftState[action.payload.id];
      });
    case (EdgeActionsType.EDGE_CREATE):
      return produce(state, (draftState) => {
        const {
          id, src, dest, consumerData: untypedConsumerData,
        } = action.payload;
        const consumerData = untypedConsumerData as Draft<EdgeType>;
        const diagramMakerData = {};
        draftState[id] = {
          consumerData,
          dest,
          diagramMakerData,
          id,
          src,
        };
      });
    case (EdgeActionsType.EDGE_SELECT):
      return produce(state, (draftState) => {
        const edgeIds = Object.keys(draftState);
        edgeIds.forEach((edgeId) => {
          if (edgeId !== action.payload.id) {
            draftState[edgeId].diagramMakerData.selected = false;
          } else {
            draftState[edgeId].diagramMakerData.selected = true;
          }
        });
      });
    case (WorkspaceActionsType.WORKSPACE_DESELECT):
    case (NodeActionsType.NODE_SELECT):
      return produce(state, (draftState) => {
        const edgeIds = Object.keys(draftState);
        edgeIds.forEach((edgeId) => {
          draftState[edgeId].diagramMakerData.selected = false;
        });
      });
    case (GlobalActionsType.DELETE_ITEMS):
      return produce(state, (draftState) => {
        const { edgeIds } = action.payload;
        edgeIds.forEach((edgeId: string) => {
          delete draftState[edgeId];
        });
      });
    default:
      return state;
  }
}
Example #11
Source File: store.ts    From Protoman with MIT License 5 votes vote down vote up
function createDefaultEnv(): Draft<Env> {
  return {
    vars: [],
  };
}
Example #12
Source File: data-store.ts    From VIR with MIT License 5 votes vote down vote up
private removeQuotaRuleReducer = (draft: Draft<DataStoreState>,
                                    quotaRuleID: QuotaRuleID) => {
    draft.quotaRules.delete(quotaRuleID)
    removeValue(draft.quotaRuleOrder, quotaRuleID)
  }
Example #13
Source File: workspaceReducer.ts    From diagram-maker with Apache License 2.0 5 votes vote down vote up
getNewZoomLevel = (currentState: Draft<DiagramMakerWorkspace>, action: ZoomWorkspaceAction): number => {
  const zoom = currentState.scale;
  const zoomFactor = Math.max(-1, Math.min(1, zoom)) * ZoomDefaults.SPEED;
  const containerSize = currentState.viewContainerSize;
  const newZoom = clampZoom(zoom + action.payload.zoom * zoomFactor, currentState.canvasSize, containerSize);

  return newZoom;
}
Example #14
Source File: data-store.ts    From VIR with MIT License 5 votes vote down vote up
private updateItemReducer = (draft: Draft<DataStoreState>,
                               itemDraft: ItemDraft,
                               options: UpdateItemOptions) => {
    const itemID = itemDraft.id
    const item = draft.items.get(itemID)
    if (item === undefined) {
      throw new Error(`Item with ID ${itemID} does not exist`)
    }

    const oldParentID = item.parentID
    const newParentID = itemDraft.parentID

    const oldStatus = item.status
    const newStatus = itemDraft.status

    itemDraft.applyToItem(item)

    // Apply status change to children
    if (oldStatus !== newStatus) {
      this.setDraftSubtreeStatus(draft, item, newStatus)
    }

    // Change parent
    if (newParentID !== oldParentID || options.anchor !== undefined) {
      // Remove from old
      if (oldParentID === undefined) {
        removeValue(draft.rootItemIDs, itemID)
      } else {
        const parent = draft.items.get(oldParentID)
        if (parent === undefined) {
          throw new Error(`Parent ID ${oldParentID} does not exist`)
        }
        removeValue(parent.childrenIDs, itemID)
      }

      // Add to new
      if (newParentID === undefined) {
        insertWithOptions(draft.rootItemIDs, item.id, options)
      } else {
        const parent = draft.items.get(newParentID)
        if (parent === undefined) {
          throw new Error(`Parent ID ${newParentID} does not exist`)
        }
        insertWithOptions(parent.childrenIDs, item.id, options)
      }
    }
  }
Example #15
Source File: workspaceReducer.ts    From diagram-maker with Apache License 2.0 5 votes vote down vote up
canvasResizeReducer = (draftState: Draft<DiagramMakerWorkspace>, action: ResizeWorkspaceCanvasAction) => {
  const currentWorkspace = draftState;
  currentWorkspace.canvasSize = action.payload.canvasSize;
}
Example #16
Source File: data-store.ts    From VIR with MIT License 5 votes vote down vote up
private removeItemFromQueueReducer = (draft: Draft<DataStoreState>,
                                        itemID: ItemID) => {
    removeValue(draft.queue, itemID)
  }
Example #17
Source File: index.ts    From atlas with GNU General Public License v3.0 5 votes vote down vote up
immer =
  <T extends State>(config: StateCreator<T, (fn: (state: Draft<T>) => void) => void>): StateCreator<T> =>
  (set, get, api) =>
    config((fn) => set(produce<T>(fn)), get, api)
Example #18
Source File: mutation.ts    From datart with Apache License 2.0 5 votes vote down vote up
export function updateBy<T>(base: T, updater: (draft: Draft<T>) => void) {
  return produce(base, draft => {
    updater(draft);
  });
}
Example #19
Source File: ProtofileManagerReducer.ts    From Protoman with MIT License 5 votes vote down vote up
export default function ProtofileManagerReducer(s: AppState, action: AnyAction): AppState {
  if (ProtofileManagerActionTypes.includes(action.type)) {
    const a = action as ProtofileManagerActions;

    switch (a.type) {
      case 'SET_PROTOFILES':
        return produce(s, draft => {
          const collection = getByKey(draft.collections, a.collectionName);
          if (collection) {
            collection.protoFilepaths = a.filepaths;
            collection.protoRootPath = a.rootPath;
            collection.buildError = undefined;
          }
        });
      case 'BUILD_PROTOFILES':
        return produce(s, draft => {
          const collection = getByKey(draft.collections, a.collectionName);
          if (collection) {
            collection.buildStatus = 'building';
            collection.buildError = undefined;
          }
        });
      case 'BUILD_PROTOFILES_SUCCESS':
        return produce(s, draft => {
          const collection = getByKey(draft.collections, a.collectionName);
          if (collection) {
            collection.buildStatus = 'success';
            collection.buildError = undefined;
            collection.protoCtx = a.ctx as Draft<ProtoCtx>;
            collection.messageNames = Object.values(a.ctx.types)
              .filter(t => t.tag === 'message')
              .map(t => t.name);
          }
        });
      case 'BUILD_PROTOFILES_FAILURE':
        return produce(s, draft => {
          const collection = getByKey(draft.collections, a.collectionName);
          if (collection) {
            collection.buildStatus = 'failure';
            collection.buildError = a.err;
          }
        });
      case 'RESET_PROTOFILE_STATUS':
        return produce(s, draft => {
          const collection = getByKey(draft.collections, a.collectionName);
          if (collection) {
            collection.buildStatus = 'default';
          }
        });
      default:
        return s;
    }
  }

  return s;
}
Example #20
Source File: data-store.ts    From VIR with MIT License 5 votes vote down vote up
private queueMoveReducer = (draft: Draft<DataStoreState>, itemID: ItemID,
                              index: number) => {
    const oldIndex = draft.queue.indexOf(itemID)
    if (oldIndex < 0) return
    arrayMove(draft.queue, oldIndex, index)
  }
Example #21
Source File: MessageValueViewReducer.ts    From Protoman with MIT License 5 votes vote down vote up
function extractBody(d: Draft<AppState>): Draft<MessageValue> | undefined {
  const flow = getByKey(getByKey(d.collections, d.currentCollection)?.flows, d.currentFlow);
  return flow?.requestBuilder?.bodies?.protobuf;
}
Example #22
Source File: workspaceReducer.ts    From diagram-maker with Apache License 2.0 5 votes vote down vote up
dragReducer = (draftState: Draft<DiagramMakerWorkspace>, action: DragWorkspaceAction) => {
  const currentWorkspace = draftState;
  currentWorkspace.position = getNewDragPosition(currentWorkspace, action);
}
Example #23
Source File: data-store.ts    From VIR with MIT License 5 votes vote down vote up
private quotaRuleMoveReducer = (draft: Draft<DataStoreState>,
                                  quotaRuleID: QuotaRuleID,
                                  index: number) => {
    const oldIndex = draft.quotaRuleOrder.indexOf(quotaRuleID)
    if (oldIndex < 0) return
    arrayMove(draft.quotaRuleOrder, oldIndex, index)
  }
Example #24
Source File: MessageValueViewReducer.ts    From Protoman with MIT License 5 votes vote down vote up
function changePrimitiveValue(v: Draft<PrimitiveValue>, segment: string[], value: string): void {
  console.assert(segment.length === 1 && segment[0] === '');
  v.value = value;
}
Example #25
Source File: data-store.ts    From VIR with MIT License 4 votes vote down vote up
/**
   * NOTE: This root item must be self-repeatable.
   * Returns whether the repeat is successful (or simply marked as done)
   */
  private repeatSubtreeReducer = (draft: Draft<DataStoreState>,
                                  rootItemID: ItemID,
                                  subtree: ItemID[],
                                  currentDate: DayID): boolean => {
    const item = draft.items.get(rootItemID)
    if (item === undefined) return false

    const firstTask: Task = {
      cost: item.cost,
      end: item.dueDate,
      itemID: rootItemID,
      start: item.deferDate,
    }
    if (firstTask.end === undefined) {
      item.status = ItemStatus.COMPLETED
      return false
    }

    // Move start and end to today
    let repeatOffset = 0
    if (item.repeatOnCompletion) {
      repeatOffset = this.getCurrentDayID() - firstTask.end
      firstTask.end += repeatOffset
      if (firstTask.start !== undefined) {
        firstTask.start += repeatOffset
      }
    }

    const repeat = item.repeat
    if (repeat === undefined) {
      item.status = ItemStatus.COMPLETED
      return false
    }

    const repeater = DEFAULT_REPEATER_BY_TYPE.get(repeat.type)
    if (repeater === undefined) {
      item.status = ItemStatus.COMPLETED
      return false
    }

    const iterator = repeater(
      firstTask, this.getEffectiveInfo(item), dayIDNow() + 100000000)
    let nextTask: Task | undefined = undefined
    while (true) {
      nextTask = iterator()
      if (nextTask === undefined || nextTask.end === undefined ||
        nextTask.end >= currentDate) {
        break
      }
    }

    if (nextTask === undefined) {
      item.status = ItemStatus.COMPLETED
      return false
    }

    const subtreeInfo: SubtreeRepetitionInfo[] = []

    const count = subtree.length
    for (let i = 0; i < count; i++) {
      const subtreeItemID = subtree[i]
      const subtreeItem = draft.items.get(subtreeItemID)
      if (subtreeItem === undefined) continue

      subtreeItem.status = ItemStatus.ACTIVE

      if (subtreeItemID === rootItemID) { // Is root item
        const startOffset = subtreeItem.repeatDeferOffset === undefined ?
          undefined : subtreeItem.repeatDeferOffset
        const endOffset = 0

        subtreeInfo.push({
          itemID: subtreeItemID,
          startOffset,
          endOffset,
        })
      } else {
        const startOffset = subtreeItem.deferDate === undefined ? undefined :
          firstTask.end - repeatOffset - subtreeItem.deferDate
        const endOffset = subtreeItem.dueDate === undefined ? 0 :
          firstTask.end - repeatOffset - subtreeItem.dueDate

        subtreeInfo.push({
          itemID: subtreeItemID,
          startOffset,
          endOffset,
        })
      }
    }

    const repetitionResults = generateSubtreeRepetition(
      nextTask, rootItemID, subtreeInfo)

    const numResults = repetitionResults.length
    for (let i = 0; i < numResults; ++i) {
      const result = repetitionResults[i]
      const subtreeItem = draft.items.get(result.itemID)
      if (subtreeItem === undefined) continue

      subtreeItem.deferDate = result.deferDate
      subtreeItem.dueDate = result.dueDate
    }

    return true
  }
Example #26
Source File: nodeReducer.ts    From diagram-maker with Apache License 2.0 4 votes vote down vote up
export default function nodeReducer<NodeType, EdgeType>(
  state: DiagramMakerNodes<NodeType> | undefined,
  action: DiagramMakerAction<NodeType, EdgeType>,
): DiagramMakerNodes<NodeType> {
  if (state === undefined) {
    return {};
  }
  switch (action.type) {
    case GlobalActionsType.CREATE_ITEMS:
      return produce(state, (draftState) => {
        action.payload.nodes.forEach((node) => {
          draftState[node.id] = node as Draft<DiagramMakerNode<NodeType>>;
        });
      });
    case NodeActionsType.NODE_CREATE:
      return produce(state, (draftState) => {
        const {
          id, position, size, typeId, consumerData: untypedConsumerData,
        } = action.payload;
        const consumerData = untypedConsumerData as Draft<NodeType>;
        draftState[id] = {
          id, typeId, consumerData, diagramMakerData: { position, size },
        };
      });
    case NodeActionsType.NODE_DELETE:
      return produce(state, (draftState) => {
        delete draftState[action.payload.id];
      });
    case EditorActionsType.FOCUS_NODE:
    case NodeActionsType.NODE_SELECT:
      return produce(state, (draftState) => {
        const nodeIds = Object.keys(draftState);
        nodeIds.forEach((nodeId) => {
          if (nodeId !== action.payload.id) {
            draftState[nodeId].diagramMakerData.selected = false;
          } else {
            draftState[nodeId].diagramMakerData.selected = true;
          }
        });
      });
    case NodeActionsType.NODE_DRAG_START:
      return produce(state, (draftState) => {
        const currentNode = draftState[action.payload.id];
        if (currentNode) {
          currentNode.diagramMakerData.dragging = true;
        }
      });
    case NodeActionsType.NODE_DRAG:
      return produce(state, (draftState) => {
        const currentNode = draftState[action.payload.id];
        if (currentNode) {
          const { position } = action.payload;
          currentNode.diagramMakerData.position = position;

          // Shift all the nodes when node is dragged outside top boundary
          if (position.y < 0) {
            const offset = { x: 0, y: -position.y };
            const nodeIds = Object.keys(draftState);
            nodeIds.forEach((nodeId) => {
              const oldPos = draftState[nodeId].diagramMakerData.position;
              const newPos = {
                x: oldPos.x + offset.x,
                y: oldPos.y + offset.y,
              };
              draftState[nodeId].diagramMakerData.position = newPos;
            });
          }
          // Shift all the nodes when node is dragged outside left boundary
          if (position.x < 0) {
            const offset = { x: -position.x, y: 0 };
            const nodeIds = Object.keys(draftState);
            nodeIds.forEach((nodeId) => {
              const oldPos = draftState[nodeId].diagramMakerData.position;
              const newPos = {
                x: oldPos.x + offset.x,
                y: oldPos.y + offset.y,
              };
              draftState[nodeId].diagramMakerData.position = newPos;
            });
          }
        }
      });
    case NodeActionsType.NODE_DRAG_END:
      return produce(state, (draftState) => {
        const currentNode = draftState[action.payload.id];
        if (currentNode) {
          currentNode.diagramMakerData.dragging = false;
        }
      });
    case EditorActionsType.UPDATE_SELECTION_MARQUEE:
      return produce(state, (draftState) => {
        const nodeIds = Object.keys(draftState);
        nodeIds.forEach((nodeId) => {
          if (isInsideBoundingBox(
            draftState[nodeId].diagramMakerData.position,
            draftState[nodeId].diagramMakerData.size,
            action.payload.anchor,
            action.payload.position,
          )) {
            draftState[nodeId].diagramMakerData.selected = true;
          } else {
            draftState[nodeId].diagramMakerData.selected = false;
          }
        });
      });
    case WorkspaceActionsType.WORKSPACE_DESELECT:
    case EdgeActionsType.EDGE_SELECT:
      return produce(state, (draftState) => {
        const nodeIds = Object.keys(draftState);
        nodeIds.forEach((nodeId) => {
          draftState[nodeId].diagramMakerData.selected = false;
        });
      });
    case WorkspaceActionsType.WORKSPACE_SELECT_ALL:
      return produce(state, (draftState) => {
        const nodeIds = Object.keys(draftState);
        nodeIds.forEach((nodeId) => {
          draftState[nodeId].diagramMakerData.selected = true;
        });
      });
    case GlobalActionsType.DELETE_ITEMS:
      return produce(state, (draftState) => {
        const { nodeIds } = action.payload;
        nodeIds.forEach((nodeId: string) => {
          delete draftState[nodeId];
        });
      });
    default:
      return state;
  }
}