mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-17 23:45:59 +00:00
add to favorites basic functionality
This commit is contained in:
@@ -66,6 +66,25 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
favorites_meta: 'get',
|
||||
async favorites() {
|
||||
if (!hasPermission(`files/favorites/read`)) return [];
|
||||
const dir = path.join(filesdir(), 'favorites');
|
||||
if (!(await fs.exists(dir))) return [];
|
||||
const files = await fs.readdir(dir);
|
||||
const res = [];
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dir, file);
|
||||
const text = await fs.readFile(filePath, { encoding: 'utf-8' });
|
||||
res.push({
|
||||
file,
|
||||
folder: 'favorites',
|
||||
...JSON.parse(text),
|
||||
});
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
markdownManifest_meta: 'get',
|
||||
async markdownManifest() {
|
||||
if (!hasPermission(`files/markdown/read`)) return [];
|
||||
|
||||
@@ -44,7 +44,7 @@ function Menu({ data, menuExt = null }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function SavedFileAppObjectBase({ data, commonProps, format, icon, onLoad, menuExt = null }) {
|
||||
export function SavedFileAppObjectBase({ data, commonProps, format, icon, onLoad, title = undefined, menuExt = null }) {
|
||||
const { file, folder } = data;
|
||||
|
||||
const onClick = async () => {
|
||||
@@ -56,7 +56,7 @@ export function SavedFileAppObjectBase({ data, commonProps, format, icon, onLoad
|
||||
<AppObjectCore
|
||||
{...commonProps}
|
||||
data={data}
|
||||
title={file}
|
||||
title={title || file}
|
||||
icon={icon}
|
||||
onClick={onClick}
|
||||
Menu={menuExt ? (props) => <Menu {...props} menuExt={menuExt} /> : Menu}
|
||||
@@ -90,7 +90,7 @@ export function SavedSqlFileAppObject({ data, commonProps }) {
|
||||
icon: 'img shell',
|
||||
tabComponent: 'ShellTab',
|
||||
},
|
||||
script.getScript()
|
||||
{ editor: script.getScript() }
|
||||
);
|
||||
};
|
||||
|
||||
@@ -111,6 +111,8 @@ export function SavedSqlFileAppObject({ data, commonProps }) {
|
||||
initialData: data,
|
||||
// @ts-ignore
|
||||
savedFile: file,
|
||||
savedFolder: 'sql',
|
||||
savedFormat: 'text',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -135,9 +137,11 @@ export function SavedShellFileAppObject({ data, commonProps }) {
|
||||
tabComponent: 'ShellTab',
|
||||
props: {
|
||||
savedFile: file,
|
||||
savedFolder: 'shell',
|
||||
savedFormat: 'text',
|
||||
},
|
||||
},
|
||||
data
|
||||
{ editor: data }
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -171,10 +175,12 @@ export function SavedChartFileAppObject({ data, commonProps }) {
|
||||
conid: connection._id,
|
||||
database,
|
||||
savedFile: file,
|
||||
savedFolder: 'charts',
|
||||
savedFormat: 'json',
|
||||
},
|
||||
tabComponent: 'ChartTab',
|
||||
},
|
||||
data
|
||||
{ editor: data }
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -191,7 +197,9 @@ export function SavedMarkdownFileAppObject({ data, commonProps }) {
|
||||
icon: 'img markdown',
|
||||
tabComponent: 'MarkdownViewTab',
|
||||
props: {
|
||||
file,
|
||||
savedFile: file,
|
||||
savedFolder: 'markdown',
|
||||
savedFormat: 'text',
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -209,9 +217,11 @@ export function SavedMarkdownFileAppObject({ data, commonProps }) {
|
||||
tabComponent: 'MarkdownEditorTab',
|
||||
props: {
|
||||
savedFile: file,
|
||||
savedFolder: 'markdown',
|
||||
savedFormat: 'text',
|
||||
},
|
||||
},
|
||||
data
|
||||
{ editor: data }
|
||||
);
|
||||
}}
|
||||
menuExt={<DropDownMenuItem onClick={showPage}>Show page</DropDownMenuItem>}
|
||||
@@ -219,7 +229,51 @@ export function SavedMarkdownFileAppObject({ data, commonProps }) {
|
||||
);
|
||||
}
|
||||
|
||||
[SavedSqlFileAppObject, SavedShellFileAppObject, SavedChartFileAppObject, SavedMarkdownFileAppObject].forEach((fn) => {
|
||||
export function FavoriteFileAppObject({ data, commonProps }) {
|
||||
const { file, folder, icon, tabComponent, title, props, tabdata } = data;
|
||||
const openNewTab = useOpenNewTab();
|
||||
|
||||
return (
|
||||
<SavedFileAppObjectBase
|
||||
data={data}
|
||||
commonProps={commonProps}
|
||||
format="json"
|
||||
icon={icon || 'img favorite'}
|
||||
title={title}
|
||||
onLoad={async (data) => {
|
||||
let tabdataNew = tabdata;
|
||||
if (props.savedFile) {
|
||||
const resp = await axios.post('files/load', {
|
||||
folder: props.savedFolder,
|
||||
file: props.savedFile,
|
||||
format: props.savedFormat,
|
||||
});
|
||||
tabdataNew = {
|
||||
...tabdata,
|
||||
editor: resp.data,
|
||||
};
|
||||
}
|
||||
openNewTab(
|
||||
{
|
||||
title,
|
||||
icon: icon || 'img favorite',
|
||||
props,
|
||||
tabComponent,
|
||||
},
|
||||
tabdataNew
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
[
|
||||
SavedSqlFileAppObject,
|
||||
SavedShellFileAppObject,
|
||||
SavedChartFileAppObject,
|
||||
SavedMarkdownFileAppObject,
|
||||
FavoriteFileAppObject,
|
||||
].forEach((fn) => {
|
||||
// @ts-ignore
|
||||
fn.extractKey = (data) => data.file;
|
||||
});
|
||||
|
||||
@@ -341,7 +341,7 @@ export default function DataGridCore(props) {
|
||||
tabComponent: 'FreeTableTab',
|
||||
props: {},
|
||||
},
|
||||
getSelectedFreeData()
|
||||
{ editor: getSelectedFreeData() }
|
||||
);
|
||||
};
|
||||
|
||||
@@ -354,8 +354,10 @@ export default function DataGridCore(props) {
|
||||
props: {},
|
||||
},
|
||||
{
|
||||
data: getSelectedFreeData(),
|
||||
config: { chartType: 'bar' },
|
||||
editor: {
|
||||
data: getSelectedFreeData(),
|
||||
config: { chartType: 'bar' },
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -92,10 +92,12 @@ export default function SqlDataGridCore(props) {
|
||||
},
|
||||
},
|
||||
{
|
||||
config: { chartType: 'bar' },
|
||||
sql: display.getExportQuery((select) => {
|
||||
select.orderBy = null;
|
||||
}),
|
||||
editor: {
|
||||
config: { chartType: 'bar' },
|
||||
sql: display.getExportQuery((select) => {
|
||||
select.orderBy = null;
|
||||
}),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ const iconNames = {
|
||||
'icon export': 'mdi mdi-application-export',
|
||||
'icon new-connection': 'mdi mdi-database-plus',
|
||||
'icon tables': 'mdi mdi-table-multiple',
|
||||
'icon favorite': 'mdi mdi-star',
|
||||
|
||||
'icon database': 'mdi mdi-database',
|
||||
'icon server': 'mdi mdi-server',
|
||||
@@ -65,6 +66,7 @@ const iconNames = {
|
||||
'img chart': 'mdi mdi-chart-bar color-magenta-7',
|
||||
'img markdown': 'mdi mdi-application color-red-7',
|
||||
'img preview': 'mdi mdi-file-find color-red-7',
|
||||
'img favorite': 'mdi mdi-star color-yellow-7',
|
||||
|
||||
'img free-table': 'mdi mdi-table color-green-7',
|
||||
'img macro': 'mdi mdi-hammer-wrench',
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function OpenChartLink({ file, children }) {
|
||||
savedFile: file,
|
||||
},
|
||||
},
|
||||
resp.data
|
||||
{ editor: resp.data }
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ function GenerateSctriptButton({ modalState }) {
|
||||
icon: 'img shell',
|
||||
tabComponent: 'ShellTab',
|
||||
},
|
||||
code
|
||||
{ editor: code }
|
||||
);
|
||||
modalState.close();
|
||||
};
|
||||
|
||||
@@ -13,7 +13,12 @@ export default function SaveTabModal({ data, folder, format, modalState, tabid,
|
||||
changeTab(tabid, setOpenedTabs, (tab) => ({
|
||||
...tab,
|
||||
title: name,
|
||||
props: { ...tab.props, savedFile: name },
|
||||
props: {
|
||||
...tab.props,
|
||||
savedFile: name,
|
||||
savedFolder: folder,
|
||||
savedFormat: format,
|
||||
},
|
||||
}));
|
||||
|
||||
const handleKeyboard = React.useCallback(
|
||||
|
||||
@@ -24,6 +24,6 @@ export default function useNewQuery() {
|
||||
database,
|
||||
},
|
||||
},
|
||||
initialData
|
||||
{ editor: initialData }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,3 +74,5 @@ export default function ChartTab({ tabVisible, toolbarPortalRef, conid, database
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ChartTab.allowAddToFavorites = (props) => props.savedFile;
|
||||
|
||||
@@ -3,13 +3,17 @@ import axios from '../utility/axios';
|
||||
import LoadingInfo from '../widgets/LoadingInfo';
|
||||
import MarkdownExtendedView from '../markdown/MarkdownExtendedView';
|
||||
|
||||
export default function MarkdownViewTab({ file }) {
|
||||
export default function MarkdownViewTab({ savedFile }) {
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [text, setText] = React.useState(null);
|
||||
|
||||
const handleLoad = async () => {
|
||||
setIsLoading(true);
|
||||
const resp = await axios.post('files/load', { folder: 'markdown', file, format: 'text' });
|
||||
const resp = await axios.post('files/load', {
|
||||
folder: 'markdown',
|
||||
file: savedFile,
|
||||
format: 'text',
|
||||
});
|
||||
setText(resp.data);
|
||||
setIsLoading(false);
|
||||
};
|
||||
@@ -28,3 +32,5 @@ export default function MarkdownViewTab({ file }) {
|
||||
|
||||
return <MarkdownExtendedView>{text || ''}</MarkdownExtendedView>;
|
||||
}
|
||||
|
||||
MarkdownViewTab.allowAddToFavorites = (props) => true;
|
||||
|
||||
@@ -176,3 +176,5 @@ export default function QueryTab({ tabid, conid, database, initialArgs, tabVisib
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
QueryTab.allowAddToFavorites = (props) => props.savedFile;
|
||||
|
||||
@@ -139,3 +139,5 @@ export default function ShellTab({ tabid, tabVisible, toolbarPortalRef, ...other
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ShellTab.allowAddToFavorites = (props) => props.savedFile;
|
||||
|
||||
@@ -28,3 +28,4 @@ export default function TableDataTab({ conid, database, schemaName, pureName, ta
|
||||
}
|
||||
|
||||
TableDataTab.matchingProps = ['conid', 'database', 'schemaName', 'pureName'];
|
||||
TableDataTab.allowAddToFavorites = (props) => true;
|
||||
|
||||
@@ -55,4 +55,5 @@ export default function ViewDataTab({ conid, database, schemaName, pureName, tab
|
||||
);
|
||||
}
|
||||
|
||||
ViewDataTab.matchingProps = ['conid', 'database', 'schemaName', 'pureName'];
|
||||
ViewDataTab.matchingProps = ['conid', 'database', 'schemaName', 'pureName'];
|
||||
ViewDataTab.allowAddToFavorites = (props) => true;
|
||||
|
||||
@@ -46,6 +46,12 @@ const configLoader = () => ({
|
||||
reloadTrigger: 'config-changed',
|
||||
});
|
||||
|
||||
const favoritesLoader = () => ({
|
||||
url: 'files/favorites',
|
||||
params: {},
|
||||
reloadTrigger: 'files-changed-favorites',
|
||||
});
|
||||
|
||||
const markdownManifestLoader = () => ({
|
||||
url: 'files/markdown-manifest',
|
||||
params: {},
|
||||
@@ -282,3 +288,10 @@ export function getMarkdownManifest(args) {
|
||||
export function useMarkdownManifest(args) {
|
||||
return useCore(markdownManifestLoader, args);
|
||||
}
|
||||
|
||||
export function getFavorites(args) {
|
||||
return getCore(favoritesLoader, args);
|
||||
}
|
||||
export function useFavorites(args) {
|
||||
return useCore(favoritesLoader, args);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { changeTab } from './common';
|
||||
import { useSetOpenedTabs } from './globalState';
|
||||
|
||||
export default function useEditorData({ tabid, reloadToken = 0, loadFromArgs = null }) {
|
||||
const localStorageKey = `tabdata_${tabid}`;
|
||||
const localStorageKey = `tabdata_editor_${tabid}`;
|
||||
const setOpenedTabs = useSetOpenedTabs();
|
||||
const changeCounterRef = React.useRef(0);
|
||||
const savedCounterRef = React.useRef(0);
|
||||
|
||||
@@ -46,7 +46,13 @@ export default function useOpenNewTab() {
|
||||
|
||||
const tabid = uuidv1();
|
||||
if (initialData) {
|
||||
await localforage.setItem(`tabdata_${tabid}`, initialData);
|
||||
for (const key of _.keys(initialData)) {
|
||||
if (key == 'editor') {
|
||||
await localforage.setItem(`tabdata_${key}_${tabid}`, initialData[key]);
|
||||
} else {
|
||||
localStorage.setItem(`tabdata_${key}_${tabid}`, JSON.stringify(initialData[key]));
|
||||
}
|
||||
}
|
||||
}
|
||||
setOpenedTabs((files) => [
|
||||
...(files || []).map((x) => ({ ...x, selected: false })),
|
||||
|
||||
57
packages/web/src/widgets/FavoritesWidget.js
Normal file
57
packages/web/src/widgets/FavoritesWidget.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { AppObjectList } from '../appobj/AppObjectList';
|
||||
import { useOpenedTabs } from '../utility/globalState';
|
||||
import ClosedTabAppObject from '../appobj/ClosedTabAppObject';
|
||||
import { WidgetsInnerContainer } from './WidgetStyles';
|
||||
import { FavoriteFileAppObject } from '../appobj/SavedFileAppObject';
|
||||
import WidgetColumnBar, { WidgetColumnBarItem } from './WidgetColumnBar';
|
||||
import { useFavorites, useFiles } from '../utility/metadataLoaders';
|
||||
import useHasPermission from '../utility/useHasPermission';
|
||||
|
||||
function ClosedTabsList() {
|
||||
const tabs = useOpenedTabs();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WidgetsInnerContainer>
|
||||
<AppObjectList
|
||||
list={_.sortBy(
|
||||
tabs.filter((x) => x.closedTime),
|
||||
(x) => -x.closedTime
|
||||
)}
|
||||
AppObjectComponent={ClosedTabAppObject}
|
||||
/>
|
||||
</WidgetsInnerContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function FavoritesList() {
|
||||
const favorites = useFavorites();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WidgetsInnerContainer>
|
||||
<AppObjectList list={favorites} AppObjectComponent={FavoriteFileAppObject} />
|
||||
</WidgetsInnerContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FavoritesWidget() {
|
||||
const hasPermission = useHasPermission();
|
||||
return (
|
||||
<WidgetColumnBar>
|
||||
<WidgetColumnBarItem title="Recently closed tabs" name="closedTabs" height="20%">
|
||||
<ClosedTabsList />
|
||||
</WidgetColumnBarItem>
|
||||
{hasPermission('files/favorites/read') && (
|
||||
<WidgetColumnBarItem title="Favorites" name="favorites" height="15%">
|
||||
<FavoritesList />
|
||||
</WidgetColumnBarItem>
|
||||
)}
|
||||
</WidgetColumnBar>
|
||||
);
|
||||
}
|
||||
@@ -15,23 +15,6 @@ import WidgetColumnBar, { WidgetColumnBarItem } from './WidgetColumnBar';
|
||||
import { useFiles } from '../utility/metadataLoaders';
|
||||
import useHasPermission from '../utility/useHasPermission';
|
||||
|
||||
function ClosedTabsList() {
|
||||
const tabs = useOpenedTabs();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WidgetsInnerContainer>
|
||||
<AppObjectList
|
||||
list={_.sortBy(
|
||||
tabs.filter((x) => x.closedTime),
|
||||
(x) => -x.closedTime
|
||||
)}
|
||||
AppObjectComponent={ClosedTabAppObject}
|
||||
/>
|
||||
</WidgetsInnerContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SavedSqlFilesList() {
|
||||
const files = useFiles({ folder: 'sql' });
|
||||
@@ -85,9 +68,6 @@ export default function FilesWidget() {
|
||||
const hasPermission = useHasPermission();
|
||||
return (
|
||||
<WidgetColumnBar>
|
||||
<WidgetColumnBarItem title="Recently closed tabs" name="closedTabs" height="20%">
|
||||
<ClosedTabsList />
|
||||
</WidgetColumnBarItem>
|
||||
{hasPermission('files/sql/read') && (
|
||||
<WidgetColumnBarItem title="Saved SQL files" name="sqlFiles" height="15%">
|
||||
<SavedSqlFilesList />
|
||||
|
||||
@@ -14,6 +14,9 @@ import { getDefaultFileFormat } from '../utility/fileformats';
|
||||
import getElectron from '../utility/getElectron';
|
||||
import AboutModal from '../modals/AboutModal';
|
||||
import useOpenNewTab from '../utility/useOpenNewTab';
|
||||
import axios from '../utility/axios';
|
||||
import tabs from '../tabs';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
|
||||
const ToolbarContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -36,6 +39,8 @@ export default function ToolBar({ toolbarPortalRef }) {
|
||||
const electron = getElectron();
|
||||
const markdownManifest = useMarkdownManifest();
|
||||
|
||||
const currentTab = openedTabs.find((x) => x.selected);
|
||||
|
||||
React.useEffect(() => {
|
||||
window['dbgate_createNewConnection'] = modalState.open;
|
||||
window['dbgate_newQuery'] = newQuery;
|
||||
@@ -76,6 +81,31 @@ export default function ToolBar({ toolbarPortalRef }) {
|
||||
});
|
||||
};
|
||||
|
||||
const addToFavorite = () => {
|
||||
const tabdata = {};
|
||||
|
||||
const re = new RegExp(`tabdata_(.*)_${currentTab.tabid}`);
|
||||
for (const key in localStorage) {
|
||||
const match = key.match(re);
|
||||
if (!match) continue;
|
||||
if (match[1] == 'editor') continue;
|
||||
tabdata[match[1]] = JSON.parse(localStorage.getItem(key));
|
||||
}
|
||||
|
||||
axios.post('files/save', {
|
||||
folder: 'favorites',
|
||||
file: uuidv1(),
|
||||
format: 'json',
|
||||
data: {
|
||||
title: currentTab.title,
|
||||
icon: currentTab.icon,
|
||||
props: currentTab.props,
|
||||
tabComponent: currentTab.tabComponent,
|
||||
tabdata,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function openTabFromButton(page) {
|
||||
if (
|
||||
openedTabs.find(
|
||||
@@ -134,6 +164,14 @@ export default function ToolBar({ toolbarPortalRef }) {
|
||||
<ToolbarButton onClick={showImport} icon="icon import">
|
||||
Import data
|
||||
</ToolbarButton>
|
||||
{!!currentTab &&
|
||||
tabs[currentTab.tabComponent] &&
|
||||
tabs[currentTab.tabComponent].allowAddToFavorites &&
|
||||
tabs[currentTab.tabComponent].allowAddToFavorites(currentTab.props) && (
|
||||
<ToolbarButton onClick={addToFavorite} icon="icon favorite">
|
||||
Add to favorites
|
||||
</ToolbarButton>
|
||||
)}
|
||||
<ToolbarButton onClick={switchTheme} icon="icon theme">
|
||||
{currentTheme == 'dark' ? 'Light mode' : 'Dark mode'}
|
||||
</ToolbarButton>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { useCurrentWidget } from '../utility/globalState';
|
||||
import ArchiveWidget from './ArchiveWidget';
|
||||
import DatabaseWidget from './DatabaseWidget';
|
||||
import FavoritesWidget from './FavoritesWidget';
|
||||
import FilesWidget from './FilesWidget';
|
||||
import PluginsWidget from './PluginsWidget';
|
||||
|
||||
@@ -11,5 +12,6 @@ export default function WidgetContainer() {
|
||||
if (currentWidget === 'file') return <FilesWidget />;
|
||||
if (currentWidget === 'archive') return <ArchiveWidget />;
|
||||
if (currentWidget === 'plugins') return <PluginsWidget />;
|
||||
if (currentWidget === 'favorites') return <FavoritesWidget />;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,11 @@ export default function WidgetIconPanel() {
|
||||
name: 'plugins',
|
||||
title: 'Extensions & Plugins',
|
||||
},
|
||||
{
|
||||
icon: 'icon favorite',
|
||||
name: 'favorites',
|
||||
title: 'Favorites',
|
||||
},
|
||||
// {
|
||||
// icon: 'fa-cog',
|
||||
// name: 'settings',
|
||||
|
||||
Reference in New Issue
Block a user