import React, { useEffect, useState } from 'react';

import Table from 'src/components/Table/Table';

import { toListOfObjects } from 'src/services/CozoDb/utils';
import { saveAs } from 'file-saver';

import { Pane, Text } from '@cybercongress/gravity';
import { Button as CybButton, Dots, Loading, Select } from 'src/components';
import FileInputButton from './FileInputButton';
import { useAppSelector } from 'src/redux/hooks';
import Display from 'src/components/containerGradient/Display/Display';

import { useBackend } from 'src/contexts/backend/backend';

import { Link } from 'react-router-dom';
import { Colors } from 'src/components/containerGradient/types';
import classNames from 'classnames';
import BackendStatus from './BackendStatus';
import cozoPresets from './cozo_presets.json';

import styles from './drive.scss';

const DEFAULT_PRESET_NAME = '๐Ÿ’ก defaul commands...';

const presetsAsSelectOptions = [
  { text: DEFAULT_PRESET_NAME, value: '' },
  ...Object.entries(cozoPresets).map(([key, value]) => ({
    text: key,
    value: Array.isArray(value) ? value.join('\r\n') : value,
  })),
];

const diffMs = (t0: number, t1: number) => `${(t1 - t0).toFixed(1)}ms`;

function Drive() {
  const [queryText, setQueryText] = useState('');
  const [isLoaded, setIsLoaded] = useState(true);
  const [inProgress, setInProgress] = useState(false);
  const [statusMessage, setStatusMessage] = useState('');
  const [errorMessage, setErrorMessage] = useState<string | undefined>();
  const [queryResults, setQueryResults] = useState<{ rows: []; cols: [] }>();
  const { cozoDbRemote, isReady } = useBackend();
  const { syncState, dbPendingWrites, services } = useAppSelector(
    (store) => store.backend
  );

  // console.log('-----syncStatus', syncState, dbPendingWrites);

  function runQuery(queryArg?: string) {
    const query = queryArg || queryText.trim();
    if (query) {
      setInProgress(true);
      setErrorMessage('');
      setStatusMessage('');
      setQueryResults(undefined);
      requestAnimationFrame(() => {
        setTimeout(async () => {
          try {
            const t0 = performance.now();
            const result = await cozoDbRemote!.runCommand(query);
            const t1 = performance.now();

            try {
              setStatusMessage(
                `finished with ${result.rows.length} rows in ${diffMs(t0, t1)}`
              );
              if (!result.headers) {
                result.headers =
                  result.rows[0].map((_, i) => i.toString()) || [];
              }
              const rows = toListOfObjects(result);
              const cols = result.headers.map((n) => ({
                // header: n,
                accessorKey: n,
                header: () => n,
                cell: (item) => {
                  const value = item.getValue();
                  if (['cid'].indexOf(n) > -1) {
                    return (
                      <a
                        href={`/ipfs/${value}`}
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        {`${value.slice(0, 10)}...${value.slice(-10)}`}
                      </a>
                    );
                  }

                  return value;
                },
              }));

              // parse object-type values to string to be able to display them in the table
              const rowsNormalized = rows.map((row) => {
                const updatedRow = {};
                for (const [key, value] of Object.entries(row)) {
                  if (typeof value === 'object') {
                    updatedRow[key] = JSON.stringify(value);
                  } else if (typeof value === 'boolean') {
                    updatedRow[key] = value.toString();
                  } else {
                    updatedRow[key] = value;
                  }
                }
                return updatedRow;
              });

              setQueryResults({ rows: rowsNormalized, cols });
            } catch (e: DBResultError | any) {
              console.error('Query failed', e);
              setStatusMessage(`finished with errors`);
              if (e.display) {
                setErrorMessage(e.display);
              }
            }
          } catch (e) {
            setStatusMessage(`query failed`);
            setErrorMessage(e.message);
            console.log(e);
          } finally {
            setInProgress(false);
          }
        }, 0);
      });
    }
  }

  const exportReations = async () => {
    const result = await cozoDbRemote!.exportRelations([
      'pin',
      'particle',
      'link',
    ]);
    console.log('---export data', result);
    try {
      const blob = new Blob([JSON.stringify(result.data)], {
        type: 'text/plain;charset=utf-8',
      });
      saveAs(blob, 'export.json');
    } catch (e) {
      console.log('CozoDb: Failed to import', e);
    }
  };

  const importReations = async (file: any) => {
    const content = await file.text();

    const res = await cozoDbRemote!.importRelations(content);
    console.log('----import result', res);
  };

  const runExampleScript = async (value: string) => {
    setQueryText(value);
    runQuery(value);
  };

  return (
    <>
      <div className={styles.main}>
        <Display color={Colors.ORANGE}>
          <p>
            this is tech preview of cyb brain. it does not adds any new
            functionality across the app, yet
          </p>
          <p>features:</p>
          <div>- log you links while you surf</div>
          <div>- connect external ipfs node for performance</div>
          <div>
            - sync your <Link to="/search/ipfs">ipfs</Link> pins
          </div>
          <div>
            - import your transactions and cybergraph from{' '}
            <Link to="/search/bostrom">bostrom</Link>
          </div>
          <div>
            - query using ai oriented{' '}
            <a href="https://www.cozodb.org/" target="_blank">
              datalog
            </a>
          </div>
          <p>
            link your feedback{' '}
            <Link to="/search/brain%20feedback">brain feedback</Link>
          </p>
        </Display>
        <BackendStatus />

        <Pane width="100%">
          <textarea
            placeholder="Enter your query here..."
            onChange={(e) => setQueryText(e.target.value)}
            value={queryText}
            className="resize-none"
            className={styles.queryInput}
            rows={10}
          />
          <div className={styles.commandPanel}>
            <div className={styles.subPanel}>
              <CybButton
                disabled={!isLoaded || inProgress}
                onClick={() => runQuery()}
                small
              >
                {isLoaded
                  ? inProgress
                    ? 'Query is running'
                    : '๐ŸŸง Run script'
                  : 'Loading WASM ...'}
              </CybButton>
              <Select
                width="250px"
                valueSelect=""
                small
                // textSelectValue="select preset..."
                onChangeSelect={runExampleScript}
                options={presetsAsSelectOptions}
                // disabled={pending}
              />
            </div>
            <div className={styles.subPanel}>
              <CybButton
                disabled={!isLoaded || !isReady}
                onClick={exportReations}
                small
              >
                export
              </CybButton>
              <FileInputButton caption="import" processFile={importReations} />
            </div>
          </div>
        </Pane>
        {statusMessage && (
          <Pane width="100%" marginTop={10}>
            <div className={styles.statusMessage}>{statusMessage}</div>
          </Pane>
        )}
      </div>
      {queryResults ? (
        queryResults.cols.length > 0 ? (
          <div className={classNames('bp5-dark', styles.results)}>
            <Table columns={queryResults.cols} data={queryResults.rows} />
          </div>
        ) : null
      ) : (
        <div className={styles.errorMessage}>{errorMessage}</div>
      )}
    </>
  );
}

export default Drive;

Synonyms

cyb/src/index.tsx
pussy-ts/src/index.tsx
bostrom.network/src/pages/Index.tsx
pussy-landing/src/pages/index.tsx
pussy.meme/src/pages/index.tsx
pussy-ts/src/containers/txs/index.tsx
pussy-ts/src/containers/mint/index.tsx
cyb/src/containers/mint/index.tsx
pussy-ts/src/components/ButtonSwap/index.tsx
pussy-ts/src/components/denom/index.tsx
cyb/src/containers/Objects/index.tsx
cyb/src/components/btnGrd/index.tsx
pussy-ts/src/components/Select/index.tsx
cyb/src/components/actionBar/index.tsx
pussy-ts/src/components/BandwidthBar/index.tsx
cyb/src/components/Select/index.tsx
pussy-ts/src/containers/portal/index.tsx
pussy-ts/src/components/btnGrd/index.tsx
cyb/src/components/TextMarkdown/index.tsx
cyb/src/containers/energy/index.tsx
pussy-ts/src/components/MainContainer/index.tsx
pussy-ts/src/components/actionBar/index.tsx
cyb/src/containers/portal/index.tsx
cyb/src/components/ButtonSwap/index.tsx
cyb/src/containers/nebula/index.tsx
pussy-ts/src/components/DonutChart/index.tsx
cyb/src/components/DonutChart/index.tsx
pussy-ts/src/containers/nebula/index.tsx
cyb/src/components/BandwidthBar/index.tsx
pussy-ts/src/containers/energy/index.tsx
cyb/src/components/denom/index.tsx
cyb/src/containers/sigma/index.tsx
cyb/src/components/Input/index.tsx
pussy-ts/src/containers/sigma/index.tsx
pussy-ts/src/containers/taverna/index.tsx
pussy-ts/src/components/Input/index.tsx
cyb/src/containers/blok/index.tsx
cyb/src/components/PDF/index.tsx
cyb/src/containers/txs/index.tsx
cyb/src/containers/taverna/index.tsx
cyb/src/components/MainContainer/index.tsx
pussy-ts/src/components/TextMarkdown/index.tsx
pussy-ts/src/components/PDF/index.tsx
cyb/src/containers/portal/citizenship/index.tsx
pussy-landing/src/components/xp/btnGrd/index.tsx
cyb/src/containers/portal/pasport/index.tsx
pussy-landing/src/components/xp/stars/index.tsx
cyb/src/components/buttons/ButtonIcon/index.tsx
cyb/src/containers/wasm/codes/index.tsx
pussy-ts/src/containers/portal/gift/index.tsx
pussy-ts/src/features/ipfs/ipfsSettings/index.tsx
pussy-ts/src/components/buttons/ButtonIcon/index.tsx
pussy-ts/src/containers/portal/release/index.tsx
cyb/src/containers/portal/release/index.tsx
cyb/src/features/ipfs/Drive/index.tsx
pussy-ts/src/containers/portal/pasport/index.tsx
cyb/src/features/ipfs/ipfsSettings/index.tsx
pussy-ts/src/containers/portal/citizenship/index.tsx
cyb/src/containers/portal/gift/index.tsx
cyb/src/components/contentIpfs/component/gateway/index.tsx
pussy-ts/src/containers/portal/components/ReleaseStatus/index.tsx
pussy-ts/src/components/contentIpfs/component/gateway/index.tsx
pussy-ts/src/containers/sigma/components/CardPassport/index.tsx
cyb/src/containers/sigma/components/CardPassport/index.tsx
cyb/src/components/contentIpfs/component/img/index.tsx
pussy-ts/src/components/contentIpfs/component/img/index.tsx
cyb/src/containers/portal/components/ActionBar/index.tsx
pussy-ts/src/components/contentIpfs/component/link/index.tsx
cyb/src/containers/portal/components/ReleaseStatus/index.tsx
cyb/src/containers/portal/components/stars/index.tsx
pussy-ts/src/containers/portal/components/stars/index.tsx
cyb/src/components/contentIpfs/component/link/index.tsx
pussy-ts/src/containers/sigma/components/cardUi/TitleCard/index.tsx
cyb/src/containers/sigma/components/cardUi/TitleCard/index.tsx
cyb/src/containers/sigma/components/cardUi/RowBalancesDetails/index.tsx
pussy-ts/src/containers/sigma/components/cardUi/RowBalancesDetails/index.tsx

Neighbours