recoil#Snapshot TypeScript Examples

The following examples show how to use recoil#Snapshot. 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: useExportResults.ts    From nextclade with MIT License 6 votes vote down vote up
function useResultsExport(exportFn: (filename: string, snapshot: Snapshot, worker: ExportWorker) => Promise<void>) {
  return useRecoilCallback(
    ({ set, snapshot }) => {
      const snapshotRelease = snapshot.retain()
      return (filename: string) => {
        void ExportWorker.get()
          .then((worker) => exportFn(filename, snapshot, worker))
          .catch((error) => {
            set(globalErrorAtom, error)
          })
          .finally(() => {
            snapshotRelease()
          })
      }
    },
    [exportFn],
  )
}
Example #2
Source File: useExportResults.ts    From nextclade with MIT License 6 votes vote down vote up
async function mapGoodResults<T>(snapshot: Snapshot, mapFn: (result: AnalysisOutput) => T) {
  const results = await snapshot.getPromise(analysisResultsAtom)

  return results
    .filter((result) => notUndefined(result.result))
    .map((result) => {
      if (!result.result) {
        throw new ErrorInternal('When preparing analysis results for export: expected result to be non-nil')
      }
      return mapFn(result.result)
    })
}
Example #3
Source File: useExportResults.ts    From nextclade with MIT License 6 votes vote down vote up
async function prepareErrorsCsv(snapshot: Snapshot, worker: ExportWorker) {
  const results = await snapshot.getPromise(analysisResultsAtom)

  const errors: ErrorsFromWeb[] = results.map(({ seqName, result, error }) => {
    if (result) {
      return {
        seqName,
        errors: '',
        failedGenes: result.analysisResult.missingGenes,
        warnings: result.analysisResult.warnings,
      }
    }

    if (error) {
      return {
        seqName,
        errors: error,
        failedGenes: [],
        warnings: [],
      }
    }

    throw new ErrorInternal('When preparing errors for export: Expected either result or error to be non-nil')
  })

  return worker.serializeErrorsCsv(errors)
}
Example #4
Source File: useExportResults.ts    From nextclade with MIT License 6 votes vote down vote up
async function preparePeptideFiles(snapshot: Snapshot) {
  const peptides = await mapGoodResults(snapshot, ({ queryPeptides, analysisResult: { seqName } }) => ({
    seqName,
    queryPeptides,
  }))

  const filesMap = new Map<string, ZipFileDescription>()

  for (const { seqName, queryPeptides } of peptides) {
    for (const { geneName, seq } of queryPeptides) {
      const file = filesMap.get(geneName)
      const fastaEntry = `>${seqName}\n${seq}\n`
      if (file) {
        file.data = `${file.data}${fastaEntry}`
      } else {
        let filename = DEFAULT_EXPORT_PARAMS.filenamePeptidesTemplate
        filename = filename.replace('{{GENE}}', geneName)
        filesMap.set(geneName, { filename, data: fastaEntry })
      }
    }
  }

  return Array.from(filesMap.values())
}
Example #5
Source File: useExportResults.ts    From nextclade with MIT License 5 votes vote down vote up
async function prepareOutputFasta(snapshot: Snapshot) {
  let fastaStr = (
    await mapGoodResults(snapshot, (result) => `>${result.analysisResult.seqName}\n${result.query}`)
  ).join('\n')
  fastaStr += '\n'
  return fastaStr
}
Example #6
Source File: useExportResults.ts    From nextclade with MIT License 5 votes vote down vote up
async function prepareResultsCsv(snapshot: Snapshot, worker: ExportWorker, delimiter: string) {
  const results = await mapGoodResults(snapshot, (result) => result.analysisResult)
  const cladeNodeAttrDescs = await snapshot.getPromise(cladeNodeAttrDescsAtom)
  return worker.serializeResultsCsv(results, cladeNodeAttrDescs, delimiter)
}
Example #7
Source File: useExportResults.ts    From nextclade with MIT License 5 votes vote down vote up
async function prepareResultsJson(snapshot: Snapshot, worker: ExportWorker) {
  const results = await mapGoodResults(snapshot, (result) => result.analysisResult)
  const cladeNodeAttrDescs = await snapshot.getPromise(cladeNodeAttrDescsAtom)
  return worker.serializeResultsJson(results, cladeNodeAttrDescs, PACKAGE_VERSION)
}
Example #8
Source File: useExportResults.ts    From nextclade with MIT License 5 votes vote down vote up
async function prepareResultsNdjson(snapshot: Snapshot, worker: ExportWorker) {
  const results = await mapGoodResults(snapshot, (result) => result.analysisResult)
  return worker.serializeResultsNdjson(results)
}
Example #9
Source File: useExportResults.ts    From nextclade with MIT License 5 votes vote down vote up
async function prepareOutputTree(snapshot: Snapshot) {
  const tree = await snapshot.getPromise(treeAtom)
  if (!tree) {
    throw new ErrorInternal('When exporting tree: the tree data is not ready')
  }
  return JSON.stringify(tree, null, 2)
}
Example #10
Source File: useExportResults.ts    From nextclade with MIT License 5 votes vote down vote up
async function prepareInsertionsCsv(snapshot: Snapshot, worker: ExportWorker) {
  const results = await mapGoodResults(snapshot, (result) => result.analysisResult)
  return worker.serializeInsertionsCsv(results)
}
Example #11
Source File: ChromogenObserver.tsx    From Chromogen with MIT License 4 votes vote down vote up
ChromogenObserver: React.FC<{ store?: Array<object> | object }> = ({ store }) => {
  // Initializing as undefined over null to match React typing for AnchorHTML attributes
  const [file, setFile] = useState<undefined | string>(undefined);
  const [storeMap, setStoreMap] = useState<Map<string, string>>(new Map());
  const [recording, setRecording] = useRecoilState<boolean>(recordingState);
  const [devtool, setDevtool] = useState<boolean>(false);
  const [, setEditFile] =useState<undefined | string>(undefined);

  // DevTool message handling
  const receiveMessage = (message: any) => {
    switch (message.data.action) {
      case 'connectChromogen':
        setDevtool(true);
        window.postMessage({ action: 'moduleConnected' }, '*');
        break;
      case 'downloadFile':
        generateFile(setFile, storeMap);
        break;
      case 'editFile':
        const array = generateFile(setEditFile, storeMap);
        window.postMessage({ action: 'editFileReceived', data: array }, '*');
        break;       
      case 'toggleRecord':
        setRecording(!recording);
        window.postMessage({ action: 'setStatus' }, '*');
        break;
      default:
      // Do nothing
    }
  };

  // Add/remove DevTool event listeners
  useEffect(() => {
    window.addEventListener('message', receiveMessage);

    return () => window.removeEventListener('message', receiveMessage);
  });

  // Auto-click download link when a new file is generated (via button click)
  useEffect(() => document.getElementById('chromogen-download')!.click(), [file]);
  // ! to get around strict null check in tsconfig

  // Update storeMap with src variable names if store prop passed
  useEffect(() => {
    if (store !== undefined) {
      const storeArr = Array.isArray(store) ? store : [store];
      const newStore: Map<string, string> = new Map();

      storeArr.forEach((storeModule) => {
        Object.entries(storeModule).forEach(([variable, imported]) => {
          let key;
          /** Relevant imports will be either an object (for vanilla atoms or selectors)
           * or functions (for atom or selector families). If we are examining a family function,
           * we will need to invoke it to create an atom/selector in order to pull the
           * original family key out from the generated atom or selector's individual key.
           * */
          if (typeof imported === 'function') {
            // Extended atom fam key will follow format of `[key]__"chromogenDummyParam"__withFallback`
            // Extended selector fam key will follow format of `[key]__selectorFamily/"chromogenDummyParam"/1`
            const extendedKey = imported(dummyParam).key;
            key = extendedKey.includes('selectorFamily')
              ? extendedKey.substring(0, extendedKey.indexOf('selectorFamily') - 2)
              : extendedKey.substring(0, extendedKey.indexOf(`"${dummyParam}"`) - 2);
          } else {
            key = imported.key;
          }
          newStore.set(key, variable);
        });
      });
      setStoreMap(newStore);
    }
  }, []);

  useRecoilTransactionObserver_UNSTABLE(
    ({ previousSnapshot, snapshot }: { previousSnapshot: Snapshot; snapshot: Snapshot }): void => {
      // Map current snapshot to array of atom states
      // Can't directly check recording hook b/c TransactionObserver runs before state update
      if (snapshot.getLoadable(recordingState).contents) {
        const { transactions, setTransactions, atoms, atomFamilies } = ledger;

        const state = atoms.map((item) => {
          const { key } = item;
          const value = snapshot.getLoadable(item).contents;
          const previous = previousSnapshot.getLoadable(item).contents;
          const updated = value !== previous;
          return { key, value, previous, updated };
        });

        const atomFamilyState: AtomFamilyState[] = [];

        /* eslint-disable */
        // TODO: refactor out of for-in syntax b/c for-in tracks up the prototype chain x_x
        for (const family in atomFamilies) {
          const familyMembers = atomFamilies[family];
          for (const member in familyMembers) {
            const memberRecoilState = familyMembers[member];
            let { key } = memberRecoilState;
            /* Key will be auto-generated by recoil in the format of
             * [atomFamilyName] + "__" + [params] + "__withFallback".
             * Removing the "__withFallback" suffix to enhance readability
             */
            key = key.substring(0, key.length - 14);
            const value = snapshot.getLoadable(memberRecoilState).contents;
            const previous = previousSnapshot.getLoadable(memberRecoilState).contents;
            const updated = value !== previous;
            // Don't track dummy atom generated by onload useEffect hook
            if (!key.includes(dummyParam)) atomFamilyState.push({ family, key, value, updated });
          }
        }
        /* eslint-enable */

        transactions.push({ state, updates: [], atomFamilyState, familyUpdates: [] });
        setTransactions.push({ state, setter: null });
      }
    },
  );

const [pauseColor, setPauseColor] = useState('#90d1f0');
const pauseBorderStyle = {
  borderColor: `${pauseColor}`,
};

const [playColor, setPlayColor] = useState('transparent transparent transparent #90d1f0')
const playBorderStyle = {
  borderColor: `${playColor}`,
};

  return (
    <>
      {
        // Render button div only if DevTool not connected
        !devtool && (
          <div>
            <div style={styles.divStyle}>
              <button
                aria-label={recording ? 'pause' : 'record'}
                id="chromogen-toggle-record"
                style={{ ...styles.buttonStyle, backgroundColor: '#7f7f7f' }}
                type="button"
                onClick={() => {
                  setRecording(!recording);
                  // if (!recording) return true;
                  // return false;
                }}
                onMouseEnter={() => recording ? setPauseColor('#f6f071') : setPlayColor('transparent transparent transparent #f6f071')}
                onMouseLeave={() => recording ? setPauseColor('#90d1f0') : setPlayColor('transparent transparent transparent #90d1f0')}
              ><a>{recording ? 
                <div style={{...styles.pauseStyle, ...pauseBorderStyle}}></div>
                 : <div style={{...styles.playStyle, ...playBorderStyle}}></div>
                 }</a>
              </button>
              <button
                aria-label="capture test"
                id="chromogen-generate-file"
                style={{ ...styles.buttonStyle, backgroundColor: '#7f7f7f', marginLeft: '-2px', marginRight: '13px' }}
                type="button"
                onClick={() => generateFile(setFile, storeMap)}
                onMouseEnter={() => document.getElementById("chromogen-generate-file")!.style.color = '#f6f071'}
                onMouseLeave={() => document.getElementById("chromogen-generate-file")!.style.color = '#90d1f0'}
                ><a>{'Download'}</a>
              </button>
            </div>
          </div>
        )
      }
      <a
        download="chromogen.test.js"
        href={file}
        id="chromogen-download"
        style={{ display: 'none' }}
      >
        Download Test
      </a>
    </>
  );
}