Skip to content

Commit

Permalink
feat: handle pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
invm committed Oct 14, 2023
1 parent d6e41e5 commit 849ef04
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 113 deletions.
6 changes: 4 additions & 2 deletions src-tauri/src/handlers/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::{
state::{AsyncState, ServiceAccess},
utils::{
crypto::md5_hash,
error::{CommandResult, Error}, fs::paginate_file,
error::{CommandResult, Error},
fs::paginate_file,
},
};
use anyhow::anyhow;
Expand Down Expand Up @@ -76,7 +77,7 @@ pub async fn enqueue_query(
}
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug)]
pub struct QueryResultParams {
pub path: String,
pub page: usize,
Expand Down Expand Up @@ -106,6 +107,7 @@ pub async fn query_results(
_app_handle: AppHandle,
params: QueryResultParams,
) -> CommandResult<Value> {
info!(?params, "query_results");
let data = paginate_file(&params.path, params.page, params.page_size);
Ok(Value::from(data))
}
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/queues/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub async fn async_process_model(
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
while let Some(input) = input_rx.recv().await {
let task = input;
// tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
match task.conn.execute_query(&task.query).await {
Ok(result_set) => match write_query(&task.id, result_set) {
Ok(path) => {
Expand Down
14 changes: 11 additions & 3 deletions src-tauri/src/utils/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,13 @@ pub fn create_app_config(app_path: &String) -> Result<()> {

pub fn paginate_file(path: &str, page: usize, limit: usize) -> Vec<String> {
let file = fs::read_to_string(path).expect("Error reading file");
let lines = file.lines().skip(page * limit).take(limit);
return lines.into_iter().map(|s| s.to_string()).collect();
let lines = file
.lines()
.skip(page * limit)
.take(limit)
.map(|s| s.to_string())
.collect();
return lines;
}

pub fn write_file(path: &PathBuf, content: &str) -> Result<()> {
Expand All @@ -64,7 +69,10 @@ pub fn write_file(path: &PathBuf, content: &str) -> Result<()> {
}

pub fn write_query(id: &str, result_set: ResultSet) -> Result<String> {
let rows = json!(result_set.rows).to_string();
let mut rows = String::from("");
result_set.rows.iter().for_each(|row| {
rows += &(row.to_string() + "\n");
});
let metadata = json!({
"count": result_set.rows.len(),
"affected_rows": result_set.affected_rows,
Expand Down
32 changes: 13 additions & 19 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import { Loader } from 'components/UI';
import { createEffect, createSignal, onMount } from 'solid-js';
import { useAppSelector } from 'services/Context';
import { listen } from '@tauri-apps/api/event';
import { Events, QueryMetadataResult, QueryTaskResult, Row } from 'interfaces';
import { Events, QueryTaskResult } from 'interfaces';
import { log } from 'utils/utils';
import { invoke } from '@tauri-apps/api';

function App() {
const [loading, setLoading] = createSignal(true);
Expand All @@ -22,6 +21,7 @@ function App() {
queryIdx,
},
app: { restoreAppStore },
backend: { getQueryMetadata },
} = useAppSelector();

createEffect(() => {
Expand All @@ -30,31 +30,25 @@ function App() {
}, 500);
});

const getQueryMetadata = (path: string) =>
invoke<string>('get_query_metadata', { path });

const getInitialQueryResults = (path: string) => {
// TODO: move page size to global store
return invoke<string>('query_results', {
params: { path, page: 0, page_size: 20 },
});
};

const compareAndAssign = async (event: QueryTaskResult) => {
if (
getConnection().id === event.conn_id &&
idx === event.tab_idx &&
queryIdx() === event.query_idx
) {
// TODO: should get 0 page of results
if (event.status === 'Completed') {
const res = await getInitialQueryResults(event.path);
const md_res = await getQueryMetadata(event.path);
const rows = JSON.parse(res) as Row[];
const metadata = JSON.parse(md_res) as QueryMetadataResult;
updateResultSet(event.tab_idx, event.query_idx, { rows, ...metadata });
const md = await getQueryMetadata(event.path);
const metadata = {
...md,
path: event.path,
status: event.status,
};
updateResultSet(event.tab_idx, event.query_idx, metadata);
} else if (event.status === 'Error') {
updateResultSet(event.tab_idx, event.query_idx, { info: event.error });
updateResultSet(event.tab_idx, event.query_idx, {
status: event.status,
info: event.error,
});
}
}
// setContentStore('tabs', idx ?? contentStore.idx, key, data);
Expand Down
17 changes: 10 additions & 7 deletions src/components/Screens/Console/Content/QueryTab/QueryTextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import {
highlightWhitespace,
highlightActiveLine,
} from '@codemirror/view';
import { MySQL, sql } from '@codemirror/lang-sql';
import { MySQL, sql, SQLite, PostgreSQL } from '@codemirror/lang-sql';
import { dracula } from '@uiw/codemirror-theme-dracula';
import { vim } from '@replit/codemirror-vim';
import { format } from 'sql-formatter';
import { invoke } from '@tauri-apps/api';
import { Copy, EditIcon, FireIcon, VimIcon } from 'components/UI/Icons';
import { useAppSelector } from 'services/Context';
import { QueryTaskEnqueueResult } from 'interfaces';
import { QueryTaskEnqueueResult, Dialect } from 'interfaces';
import { t } from 'utils/i18n';
import { Alert } from 'components/UI';
import { basicSetup } from 'codemirror';
Expand All @@ -26,7 +26,11 @@ import { search } from '@codemirror/search';
import { createStore } from 'solid-js/store';
import { ActionRowButton } from './components/ActionRowButton';

import { log } from 'utils/utils';
const SQLDialects = {
[Dialect.Mysql]: MySQL,
[Dialect.Postgres]: PostgreSQL,
[Dialect.Sqlite]: SQLite,
};

export const QueryTextArea = () => {
const {
Expand Down Expand Up @@ -60,8 +64,9 @@ export const QueryTextArea = () => {
createExtension(search);
createExtension(() => basicSetup);
createExtension(() => (vimModeOn() ? vim() : []));
// TODO: add dialect and schema
createExtension(() => sql({ dialect: MySQL, schema }));
createExtension(() =>
sql({ dialect: SQLDialects[getConnection().connection.dialect], schema })
);
const { setFocused } = createEditorFocus(editorView);
// TODO: add option to scroll inside autocompletion list with c-l and c-k
// defaultKeymap.push({ keys: "gq", type: "operator", operator: "hardWrap" });
Expand Down Expand Up @@ -106,12 +111,10 @@ export const QueryTextArea = () => {
);
updateContentTab('data', {
query: code(),
executed: true,
result_sets: results_sets.map((id) => ({
id,
})),
});
log({ results_sets });
} catch (error) {
updateContentTab('error', String(error));
} finally {
Expand Down
54 changes: 41 additions & 13 deletions src/components/Screens/Console/Content/QueryTab/ResultesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createEffect, createSignal } from 'solid-js';
import { createEffect, createSignal, on } from 'solid-js';
import { TabulatorFull as Tabulator } from 'tabulator-tables';
import { dracula } from '@uiw/codemirror-theme-dracula';
import { search } from '@codemirror/search';
Expand All @@ -10,6 +10,7 @@ import {
} from 'solid-codemirror';
import { Pagination } from './components/Pagination';
import { useAppSelector } from 'services/Context';
import { Row } from 'interfaces';

type TableColumn = { title: string; field: string; resizeable: boolean };

Expand Down Expand Up @@ -37,37 +38,51 @@ const parseObjRecursive = (

export const ResultsTable = () => {
const {
connections: { queryIdx, getContentData, pageSize },
connections: { queryIdx, getContentData },
backend: { pageSize, getQueryResults },
} = useAppSelector();
const [code, setCode] = createSignal('');
const { ref, editorView, createExtension } = createCodeMirror({
onValueChange: setCode,
});
const [table, setTable] = createSignal<Tabulator | null>(null);
createEditorControlledValue(editorView, code);
createExtension(() => search());
createExtension(dracula);
createExtension(basicSetup);
createExtension(json);

const lineWrapping = EditorView.lineWrapping;
createExtension(lineWrapping);

createEffect(() => {
const data = getContentData('Query');
const rows = data.result_sets[queryIdx()]?.rows ?? [];
const [rows, setRows] = createSignal<Row[]>([]);
const [page, setPage] = createSignal(0);

const updateRows = async () => {
const result_set = getContentData('Query').result_sets[queryIdx()];
const _rows =
result_set?.status === 'Completed'
? await getQueryResults(result_set.path!)
: [];

setRows(_rows);
};

createEffect(on(queryIdx, updateRows));
createEffect(on(page, updateRows));
createEffect(updateRows);

createEffect(async () => {
let columns: TableColumn[] = [];
if (length) {
columns = Object.keys(rows[0]).map((k) => ({
if (rows().length) {
columns = Object.keys(rows()[0]).map((k) => ({
title: k,
field: k,
resizeable: true,
// editor: "input" as const, // this will make the whole table navigable
}));
}

const _table = new Tabulator('#results-table', {
data: rows,
new Tabulator('#results-table', {
data: rows(),
columns,
columnDefaults: {
title: '',
Expand Down Expand Up @@ -115,9 +130,22 @@ export const ResultsTable = () => {
// navRight: ["ctrl + shift + l", 39],
// },
});
setTable(_table);
});

const onNextPage = async () => {
setPage(page() + 1);
const result_set = getContentData('Query').result_sets[queryIdx()];
const res = await getQueryResults(result_set.path!, page());
setRows(res);
};

const onPrevPage = async () => {
setPage(page() - 1);
const result_set = getContentData('Query').result_sets[queryIdx()];
const res = await getQueryResults(result_set.path!, page());
setRows(res);
};

return (
<div class="flex flex-col h-full overflow-hidden">
<dialog id="my_modal_1" class="modal">
Expand All @@ -130,7 +158,7 @@ export const ResultsTable = () => {
<button>close</button>
</form>
</dialog>
<Pagination table={table()} />
<Pagination {...{ page, onNextPage, onPrevPage }} />
<div id="results-table"></div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
import { TabulatorFull as Tabulator } from "tabulator-tables";
import { Alert } from "components/UI";
import { useAppSelector } from "services/Context";
import { createShortcut } from "@solid-primitives/keyboard";
import { ChevronLeft, ChevronRight } from "components/UI/Icons";
import { t } from "utils/i18n";
import { Alert } from 'components/UI';
import { useAppSelector } from 'services/Context';
import { createShortcut } from '@solid-primitives/keyboard';
import { ChevronLeft, ChevronRight } from 'components/UI/Icons';
import { t } from 'utils/i18n';
import { Accessor, createEffect, on, Show } from 'solid-js';
import { createStore } from 'solid-js/store';
import { ResultSet } from 'interfaces';

type PaginationProps = {
table: Tabulator | null;
page: Accessor<number>;
onPrevPage: () => void;
onNextPage: () => void;
};

export const Pagination = (_props: PaginationProps) => {
export const Pagination = (props: PaginationProps) => {
const {
connections: {
selectNextQuery,
selectPrevQuery,
queryIdx
},
connections: { selectNextQuery, selectPrevQuery, queryIdx, getContentData },
backend: { pageSize },
} = useAppSelector();

createShortcut(["Control", "Shift", "N"], selectNextQuery);
createShortcut(["Control", "Shift", "P"], selectPrevQuery);
createShortcut(['Control', 'Shift', 'N'], selectNextQuery);
createShortcut(['Control', 'Shift', 'P'], selectPrevQuery);

const [resultSet, setResultSet] = createStore<ResultSet>({});

createEffect(
on(queryIdx, () => {
const rs = getContentData('Query').result_sets[queryIdx()];
if (rs) setResultSet(rs);
})
);

createEffect(() => {
console.log('query idx', queryIdx());
});

return (
<div class="container flex justify-between items-top p-1 my-1 gap-2 bg-base-200">
Expand All @@ -30,26 +44,43 @@ export const Pagination = (_props: PaginationProps) => {
</button>
<button class="join-item btn btn-sm btn-disabled !text-info">
<span class="mt-1">
{t("console.result_set")} {queryIdx() + 1}
{t('console.result_set')} {queryIdx() + 1}
</span>
</button>
<button class="join-item btn btn-sm" onClick={selectNextQuery}>
<ChevronRight />
</button>
</div>
<div class="flex-1">
<Alert color="info">
Lorem ipsum dolor sit amet, qui minim labore adipisicing minim
</Alert>
<Show when={resultSet?.info}>
<Alert color="info">{resultSet?.info}</Alert>
</Show>
</div>
</div>
<div class="join">
<button class="join-item btn btn-sm">1</button>
<button class="join-item btn btn-sm">2</button>
<button class="join-item btn btn-sm btn-disabled">...</button>
<button class="join-item btn btn-sm">99</button>
<button class="join-item btn btn-sm">100</button>
</div>

<Show when={!resultSet?.info}>
<div class="join">
<button
class="join-item btn btn-sm"
disabled={!props.page()}
onClick={props.onPrevPage}
>
<ChevronLeft />
</button>
<button class="join-item btn btn-sm btn-disabled !text-base-content">
{props.page() + 1}
</button>
<button
class="join-item btn btn-sm"
disabled={
props.page() * pageSize() + pageSize() >= (resultSet?.count ?? 0)
}
onClick={props.onNextPage}
>
<ChevronRight />
</button>
</div>
</Show>
</div>
);
};
Loading

0 comments on commit 849ef04

Please sign in to comment.