mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
17 Commits
v0.7.26-be
...
v0.7.28
Author | SHA1 | Date | |
---|---|---|---|
c2b602785a | |||
97279742e9 | |||
6cb21ff792 | |||
72bacdeabf | |||
c434855879 | |||
ba0ffcc6f5 | |||
8e7965a0f9 | |||
4aab84fbd5 | |||
|
74e97e660d | ||
|
f5d236b521 | ||
|
e794d207ad | ||
f99a3cc054 | |||
59f7d3c670 | |||
8f2daa0f1c | |||
141d5bb69c | |||
171b6f924a | |||
f7419d8e9c |
41
CHANGELOG.md
41
CHANGELOG.md
@@ -2,6 +2,47 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [0.7.28](https://github.com/antares-sql/antares/compare/v0.7.28-beta.0...v0.7.28) (2024-08-20)
|
||||
|
||||
### [0.7.28-beta.0](https://github.com/antares-sql/antares/compare/v0.7.27...v0.7.28-beta.0) (2024-08-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add missing Dutch strings ([e794d20](https://github.com/antares-sql/antares/commit/e794d207ad75e0f5380f57df579912893e5edb6a))
|
||||
* **translation:** Add more faker translations ([74e97e6](https://github.com/antares-sql/antares/commit/74e97e660df089ed8273565942118e112f6b3220))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disabled column sort during loadings ([72bacde](https://github.com/antares-sql/antares/commit/72bacdeabf833880482a839c4735505573d33bdc))
|
||||
* html tags searching in history or saved queries, fixes [#847](https://github.com/antares-sql/antares/issues/847) ([6cb21ff](https://github.com/antares-sql/antares/commit/6cb21ff7926c74469b421c47b434612b3894b4c2))
|
||||
* **PostgreSQL:** issue exporting tables with primary keys ([c434855](https://github.com/antares-sql/antares/commit/c434855879de16f83e17784e38e931decdd94873))
|
||||
* **PostgreSQL:** wrong export formato of JSON fields ([8e7965a](https://github.com/antares-sql/antares/commit/8e7965a0f94a17ed73d5c8913f66e4e9cf0b11c7))
|
||||
* **translation:** Spelling error ([f5d236b](https://github.com/antares-sql/antares/commit/f5d236b521a3534754de0b1031513f8eb83b3cc0))
|
||||
* wrong password message importing app data ([ba0ffcc](https://github.com/antares-sql/antares/commit/ba0ffcc6f56c5506c1768c05d43bb07f7b090a68))
|
||||
|
||||
### [0.7.27](https://github.com/antares-sql/antares/compare/v0.7.26...v0.7.27) (2024-07-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* issue with new console and languages different than english, fixes [#837](https://github.com/antares-sql/antares/issues/837) ([59f7d3c](https://github.com/antares-sql/antares/commit/59f7d3c67083ac7e32bd29c9b7e6e044f2060c2f))
|
||||
|
||||
### [0.7.26](https://github.com/antares-sql/antares/compare/v0.7.26-beta.1...v0.7.26) (2024-07-15)
|
||||
|
||||
### [0.7.26-beta.1](https://github.com/antares-sql/antares/compare/v0.7.26-beta.0...v0.7.26-beta.1) (2024-07-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* custom SVG icons for connections, closes [#663](https://github.com/antares-sql/antares/issues/663) ([171b6f9](https://github.com/antares-sql/antares/commit/171b6f924acc7d7696f4f850a704af0baf616b87))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* table name in column list on export as SQL features, fixes [#822](https://github.com/antares-sql/antares/issues/822) ([f7419d8](https://github.com/antares-sql/antares/commit/f7419d8e9c4fe8ea80dbf9b2612ff44a66f50365))
|
||||
|
||||
### [0.7.26-beta.0](https://github.com/antares-sql/antares/compare/v0.7.25...v0.7.26-beta.0) (2024-07-01)
|
||||
|
||||
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "antares",
|
||||
"version": "0.7.26-beta.0",
|
||||
"version": "0.7.28",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "antares",
|
||||
"version": "0.7.26-beta.0",
|
||||
"version": "0.7.28",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "antares",
|
||||
"productName": "Antares",
|
||||
"version": "0.7.26-beta.0",
|
||||
"version": "0.7.28",
|
||||
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/antares-sql/antares.git",
|
||||
|
@@ -40,7 +40,7 @@ export const objectToGeoJSON = (val: any) => {
|
||||
export const escapeAndQuote = (val: string, client: ClientCode) => {
|
||||
const { stringsWrapper: sw } = customizations[client];
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const CHARS_TO_ESCAPE = /[\0\b\t\n\r\x1a"'\\]/g;
|
||||
const CHARS_TO_ESCAPE = sw === '"' ? /[\0\b\t\n\r\x1a"'\\]/g : /[\0\b\t\n\r\x1a'\\]/g;
|
||||
const CHARS_ESCAPE_MAP: Record<string, string> = {
|
||||
'\0': '\\0',
|
||||
'\b': '\\b',
|
||||
@@ -48,10 +48,13 @@ export const escapeAndQuote = (val: string, client: ClientCode) => {
|
||||
'\n': '\\n',
|
||||
'\r': '\\r',
|
||||
'\x1a': '\\Z',
|
||||
'"': '\\"',
|
||||
'\'': '\\\'',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
|
||||
if (sw === '"')
|
||||
CHARS_ESCAPE_MAP['"'] = '\\"';
|
||||
|
||||
let chunkIndex = CHARS_TO_ESCAPE.lastIndex = 0;
|
||||
let escapedVal = '';
|
||||
let match;
|
||||
@@ -97,10 +100,19 @@ export const valueToSqlString = (args: {
|
||||
}
|
||||
else if ('isArray' in field && field.isArray) {
|
||||
let localVal;
|
||||
if (Array.isArray(val))
|
||||
localVal = JSON.stringify(val).replaceAll('[', '{').replaceAll(']', '}');
|
||||
else
|
||||
localVal = typeof val === 'string' ? val.replaceAll('[', '{').replaceAll(']', '}') : '';
|
||||
if (Array.isArray(val)) {
|
||||
localVal = JSON
|
||||
.stringify(val)
|
||||
.replaceAll('[', '{')
|
||||
.replaceAll(']', '}');
|
||||
}
|
||||
else {
|
||||
localVal = typeof val === 'string'
|
||||
? val
|
||||
.replaceAll('[', '{')
|
||||
.replaceAll(']', '}')
|
||||
: '';
|
||||
}
|
||||
parsedValue = `'${localVal}'`;
|
||||
}
|
||||
else if (TEXT_SEARCH.includes(field.type))
|
||||
@@ -163,7 +175,7 @@ export const jsonToSqlInsert = (args: {
|
||||
const sqlInsertAfter = options && options.sqlInsertAfter ? options.sqlInsertAfter : 1;
|
||||
const sqlInsertDivider = options && options.sqlInsertDivider ? options.sqlInsertDivider : 'rows';
|
||||
const { elementsWrapper: ew } = customizations[client];
|
||||
const fieldNames = Object.keys(json[0]).map(key => `${ew}${key}${ew}`);
|
||||
const fieldNames = Object.keys(json[0]).map(key => `${ew}${key.split('.').pop()}${ew}`);
|
||||
let insertStmt = `INSERT INTO ${ew}${table}${ew} (${fieldNames.join(', ')}) VALUES `;
|
||||
let insertsString = '';
|
||||
let queryLength = 0;
|
||||
|
@@ -87,10 +87,10 @@ export default () => {
|
||||
shortCutRegister.unregister();
|
||||
});
|
||||
|
||||
ipcMain.handle('read-file', (event, filePath) => {
|
||||
ipcMain.handle('read-file', (event, { filePath, encoding }) => {
|
||||
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const content = fs.readFileSync(filePath, encoding);
|
||||
return content;
|
||||
}
|
||||
catch (error) {
|
||||
|
@@ -251,7 +251,7 @@ export default (connections: Record<string, antares.Client>) => {
|
||||
setTimeout(() => { // Ensures that writing thread has finished
|
||||
exporter?.terminate();
|
||||
exporter = null;
|
||||
}, 2000);
|
||||
}, 500);
|
||||
resolve({ status: 'success', response: payload });
|
||||
break;
|
||||
case 'cancel':
|
||||
|
@@ -591,8 +591,8 @@ export class PostgreSQLClient extends BaseClient {
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
if (schema !== 'public')
|
||||
await this.use(schema);
|
||||
// if (schema !== 'public')
|
||||
await this.use(schema);
|
||||
|
||||
const { rows } = await this.raw<antares.QueryResult<ShowIntexesResult>>(`WITH ndx_list AS (
|
||||
SELECT pg_index.indexrelid, pg_class.oid
|
||||
@@ -636,35 +636,7 @@ export class PostgreSQLClient extends BaseClient {
|
||||
}, {} as {table: string; schema: string}[]);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async getTableDll ({ schema, table }: { schema: string; table: string }) {
|
||||
// const { rows } = await this.raw<antares.QueryResult<{'ddl'?: string}>>(`
|
||||
// SELECT
|
||||
// 'CREATE TABLE ' || relname || E'\n(\n' ||
|
||||
// array_to_string(
|
||||
// array_agg(' ' || column_name || ' ' || type || ' '|| not_null)
|
||||
// , E',\n'
|
||||
// ) || E'\n);\n' AS ddl
|
||||
// FROM (
|
||||
// SELECT
|
||||
// a.attname AS column_name
|
||||
// , pg_catalog.format_type(a.atttypid, a.atttypmod) AS type
|
||||
// , CASE WHEN a.attnotnull THEN 'NOT NULL' ELSE 'NULL' END AS not_null
|
||||
// , c.relname
|
||||
// FROM pg_attribute a, pg_class c, pg_type t
|
||||
// WHERE a.attnum > 0
|
||||
// AND a.attrelid = c.oid
|
||||
// AND a.atttypid = t.oid
|
||||
// AND c.relname = '${table}'
|
||||
// ORDER BY a.attnum
|
||||
// ) AS tabledefinition
|
||||
// GROUP BY relname
|
||||
// `);
|
||||
|
||||
// if (rows.length)
|
||||
// return rows[0].ddl;
|
||||
// else return '';
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
interface SequenceRecord {
|
||||
sequence_catalog: string;
|
||||
@@ -706,6 +678,34 @@ export class PostgreSQLClient extends BaseClient {
|
||||
|
||||
if (!rows.length) return '';
|
||||
|
||||
const indexes = await this.getTableIndexes({ schema, table });
|
||||
const primaryKey = indexes
|
||||
.filter(i => i.type === 'PRIMARY')
|
||||
.reduce((acc, cur) => {
|
||||
if (!Object.keys(acc).length) {
|
||||
cur.column = `"${cur.column}"`;
|
||||
acc = cur;
|
||||
}
|
||||
else
|
||||
acc.column += `, "${cur.column}"`;
|
||||
return acc;
|
||||
}, {} as { name: string; column: string; type: string});
|
||||
|
||||
const remappedIndexes = indexes
|
||||
.filter(i => i.type !== 'PRIMARY')
|
||||
.reduce((acc, cur) => {
|
||||
const existingIndex = acc.findIndex(i => i.name === cur.name);
|
||||
|
||||
if (existingIndex >= 0)
|
||||
acc[existingIndex].column += `, "${cur.column}"`;
|
||||
else {
|
||||
cur.column = `"${cur.column}"`;
|
||||
acc.push(cur);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [] as { name: string; column: string; type: string}[]);
|
||||
|
||||
for (const column of rows) {
|
||||
let fieldType = column.data_type;
|
||||
if (fieldType === 'USER-DEFINED') fieldType = `"${schema}".${column.udt_name}`;
|
||||
@@ -733,6 +733,9 @@ export class PostgreSQLClient extends BaseClient {
|
||||
columnsSql.push(columnArr.join(' '));
|
||||
}
|
||||
|
||||
if (primaryKey)
|
||||
columnsSql.push(`CONSTRAINT "${primaryKey.name}" PRIMARY KEY (${primaryKey.column})`);
|
||||
|
||||
// Table sequences
|
||||
for (let sequence of sequences) {
|
||||
if (sequence.includes('.')) sequence = sequence.split('.')[1];
|
||||
@@ -749,25 +752,22 @@ export class PostgreSQLClient extends BaseClient {
|
||||
INCREMENT BY ${rows[0].increment}
|
||||
MINVALUE ${rows[0].minimum_value}
|
||||
MAXVALUE ${rows[0].maximum_value}
|
||||
CACHE 1;\n`;
|
||||
CACHE 1;\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Table create
|
||||
createSql += `\nCREATE TABLE "${schema}"."${table}"(
|
||||
createSql += `CREATE TABLE "${schema}"."${table}"(
|
||||
${columnsSql.join(',\n ')}
|
||||
);\n`;
|
||||
|
||||
// Table indexes
|
||||
createSql += '\n';
|
||||
const { rows: indexes } = await this.select('*')
|
||||
.schema('pg_catalog')
|
||||
.from('pg_indexes')
|
||||
.where({ schemaname: `= '${schema}'`, tablename: `= '${table}'` })
|
||||
.run<{indexdef: string}>();
|
||||
|
||||
for (const index of indexes)
|
||||
createSql += `${index.indexdef};\n`;
|
||||
for (const index of remappedIndexes) {
|
||||
if (index.type !== 'PRIMARY')
|
||||
createSql += `CREATE ${index.type}${index.type === 'UNIQUE' ? ' INDEX' : ''} "${index.name}" ON "${schema}"."${table}" (${index.column});\n`;
|
||||
}
|
||||
|
||||
return createSql;
|
||||
}
|
||||
|
@@ -336,7 +336,8 @@ CREATE TABLE \`${view.Name}\`(
|
||||
const connection = await this._client.getConnection();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stream = (connection as any).connection.query(sql).stream();
|
||||
const dispose = () => connection.end();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dispose = () => (connection as any).release();
|
||||
|
||||
stream.on('end', dispose);
|
||||
stream.on('error', dispose);
|
||||
|
@@ -39,115 +39,7 @@ SET row_security = off;\n\n\n`;
|
||||
}
|
||||
|
||||
async getCreateTable (tableName: string) {
|
||||
/* eslint-disable camelcase */
|
||||
interface SequenceRecord {
|
||||
sequence_catalog: string;
|
||||
sequence_schema: string;
|
||||
sequence_name: string;
|
||||
data_type: string;
|
||||
numeric_precision: number;
|
||||
numeric_precision_radix: number;
|
||||
numeric_scale: number;
|
||||
start_value: string;
|
||||
minimum_value: string;
|
||||
maximum_value: string;
|
||||
increment: string;
|
||||
cycle_option: string;
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
let createSql = '';
|
||||
const sequences = [];
|
||||
const columnsSql = [];
|
||||
const arrayTypes: Record<string, string> = {
|
||||
_int2: 'smallint',
|
||||
_int4: 'integer',
|
||||
_int8: 'bigint',
|
||||
_float4: 'real',
|
||||
_float8: 'double precision',
|
||||
_char: '"char"',
|
||||
_varchar: 'character varying'
|
||||
};
|
||||
|
||||
// Table columns
|
||||
const { rows } = await this._client.raw(`
|
||||
SELECT *
|
||||
FROM "information_schema"."columns"
|
||||
WHERE "table_schema" = '${this.schemaName}'
|
||||
AND "table_name" = '${tableName}'
|
||||
ORDER BY "ordinal_position" ASC
|
||||
`, { schema: 'information_schema' });
|
||||
|
||||
if (!rows.length) return '';
|
||||
|
||||
for (const column of rows) {
|
||||
let fieldType = column.data_type;
|
||||
if (fieldType === 'USER-DEFINED') fieldType = `"${this.schemaName}".${column.udt_name}`;
|
||||
else if (fieldType === 'ARRAY') {
|
||||
if (Object.keys(arrayTypes).includes(fieldType))
|
||||
fieldType = arrayTypes[column.udt_name] + '[]';
|
||||
else
|
||||
fieldType = column.udt_name.replaceAll('_', '') + '[]';
|
||||
}
|
||||
|
||||
const columnArr = [
|
||||
`"${column.column_name}"`,
|
||||
`${fieldType}${column.character_maximum_length ? `(${column.character_maximum_length})` : ''}`
|
||||
];
|
||||
|
||||
if (column.column_default) {
|
||||
columnArr.push(`DEFAULT ${column.column_default}`);
|
||||
if (column.column_default.includes('nextval')) {
|
||||
const sequenceName = column.column_default.split('\'')[1];
|
||||
sequences.push(sequenceName);
|
||||
}
|
||||
}
|
||||
if (column.is_nullable === 'NO') columnArr.push('NOT NULL');
|
||||
|
||||
columnsSql.push(columnArr.join(' '));
|
||||
}
|
||||
|
||||
// Table sequences
|
||||
for (let sequence of sequences) {
|
||||
if (sequence.includes('.')) sequence = sequence.split('.')[1];
|
||||
|
||||
const { rows } = await this._client
|
||||
.select('*')
|
||||
.schema('information_schema')
|
||||
.from('sequences')
|
||||
.where({ sequence_schema: `= '${this.schemaName}'`, sequence_name: `= '${sequence}'` })
|
||||
.run<SequenceRecord>();
|
||||
|
||||
if (rows.length) {
|
||||
createSql += `CREATE SEQUENCE "${this.schemaName}"."${sequence}"
|
||||
START WITH ${rows[0].start_value}
|
||||
INCREMENT BY ${rows[0].increment}
|
||||
MINVALUE ${rows[0].minimum_value}
|
||||
MAXVALUE ${rows[0].maximum_value}
|
||||
CACHE 1;\n`;
|
||||
|
||||
// createSql += `\nALTER TABLE "${sequence}" OWNER TO ${this._client._params.user};\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Table create
|
||||
createSql += `\nCREATE TABLE "${this.schemaName}"."${tableName}"(
|
||||
${columnsSql.join(',\n ')}
|
||||
);\n`;
|
||||
|
||||
// createSql += `\nALTER TABLE "${tableName}" OWNER TO ${this._client._params.user};\n\n`;
|
||||
|
||||
// Table indexes
|
||||
createSql += '\n';
|
||||
const { rows: indexes } = await this._client
|
||||
.select('*')
|
||||
.schema('pg_catalog')
|
||||
.from('pg_indexes')
|
||||
.where({ schemaname: `= '${this.schemaName}'`, tablename: `= '${tableName}'` })
|
||||
.run<{indexdef: string}>();
|
||||
|
||||
for (const index of indexes)
|
||||
createSql += `${index.indexdef};\n`;
|
||||
const createSql = await this._client.getTableDll({ schema: this.schemaName, table: tableName });
|
||||
|
||||
// Table foreigns
|
||||
const { rows: foreigns } = await this._client.raw(`
|
||||
|
@@ -1,11 +1,19 @@
|
||||
<template>
|
||||
<SvgIcon
|
||||
v-if="type === 'mdi'"
|
||||
:type="type"
|
||||
:path="iconPath"
|
||||
:size="size"
|
||||
:rotate="rotate"
|
||||
:class="iconFlip"
|
||||
/>
|
||||
<svg
|
||||
v-else
|
||||
:width="size"
|
||||
:height="size"
|
||||
:viewBox="`0 0 ${size} ${size}`"
|
||||
v-html="iconPath"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -13,6 +21,10 @@ import SvgIcon from '@jamescoyle/vue-icon';
|
||||
import * as Icons from '@mdi/js';
|
||||
import { computed, PropType } from 'vue';
|
||||
|
||||
import { useConnectionsStore } from '@/stores/connections';
|
||||
|
||||
const { getIconByUid } = useConnectionsStore();
|
||||
|
||||
const props = defineProps({
|
||||
iconName: {
|
||||
type: String,
|
||||
@@ -23,7 +35,7 @@ const props = defineProps({
|
||||
default: 48
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
type: String as PropType<'mdi' | 'custom'>,
|
||||
default: () => 'mdi'
|
||||
},
|
||||
flip: {
|
||||
@@ -37,7 +49,18 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const iconPath = computed(() => {
|
||||
return (Icons as {[k:string]: string})[props.iconName];
|
||||
if (props.type === 'mdi')
|
||||
return (Icons as {[k:string]: string})[props.iconName];
|
||||
else if (props.type === 'custom') {
|
||||
const base64 = getIconByUid(props.iconName)?.base64;
|
||||
const svgString = Buffer
|
||||
.from(base64, 'base64')
|
||||
.toString('utf-8')
|
||||
.replaceAll(/width="[^"]*"|height="[^"]*"/g, '');
|
||||
|
||||
return svgString;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
const iconFlip = computed(() => {
|
||||
|
@@ -64,6 +64,7 @@
|
||||
>
|
||||
<BaseIcon
|
||||
:icon-name="camelize(connection.icon)"
|
||||
:type="connection.hasCustomIcon ? 'custom' : 'mdi'"
|
||||
:size="42"
|
||||
/>
|
||||
</div>
|
||||
@@ -278,12 +279,15 @@ const remappedConnections = computed(() => {
|
||||
.map(c => {
|
||||
const connTime = lastConnections.value.find((lc) => lc.uid === c.uid)?.time || 0;
|
||||
const connIcon = connectionsOrder.value.find((co) => co.uid === c.uid).icon;
|
||||
const connHasCustomIcon = connectionsOrder.value.find((co) => co.uid === c.uid).hasCustomIcon;
|
||||
const folder = folders.value.find(f => f.connections.includes(c.uid));
|
||||
|
||||
return {
|
||||
...c,
|
||||
icon: connIcon,
|
||||
color: folder?.color,
|
||||
folderName: folder?.name,
|
||||
hasCustomIcon: connHasCustomIcon,
|
||||
time: connTime
|
||||
};
|
||||
})
|
||||
|
@@ -49,18 +49,46 @@
|
||||
class="icon-box"
|
||||
:title="icon.name"
|
||||
:class="[{'selected': localConnection.icon === icon.code}]"
|
||||
@click="localConnection.icon = icon.code"
|
||||
@click="setIcon(icon.code)"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="icon-box"
|
||||
:title="icon.name"
|
||||
:class="[`dbi dbi-${connection.client}`, {'selected': localConnection.icon === icon.code}]"
|
||||
@click="localConnection.icon = icon.code"
|
||||
:class="[`dbi dbi-${connection.client}`, {'selected': localConnection.icon === null}]"
|
||||
@click="setIcon(null)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-3">
|
||||
<label class="form-label">{{ t('application.customIcon') }}</label>
|
||||
</div>
|
||||
<div class="col-9 icons-wrapper">
|
||||
<div
|
||||
v-for="icon in customIcons"
|
||||
:key="icon.uid"
|
||||
>
|
||||
<BaseIcon
|
||||
v-if="icon.uid"
|
||||
:icon-name="icon.uid"
|
||||
type="custom"
|
||||
:size="36"
|
||||
class="icon-box"
|
||||
:class="[{'selected': localConnection.icon === icon.uid}]"
|
||||
@click="setIcon(icon.uid, 'custom')"
|
||||
@contextmenu.prevent="contextMenu($event, icon.uid)"
|
||||
/>
|
||||
</div>
|
||||
<BaseIcon
|
||||
:icon-name="'mdiPlus'"
|
||||
:size="36"
|
||||
class="icon-box"
|
||||
@click="openFile"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,20 +102,45 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BaseContextMenu
|
||||
v-if="isContext"
|
||||
:context-event="contextEvent"
|
||||
@close-context="isContext = false"
|
||||
>
|
||||
<div class="context-element" @click="removeIconHandler">
|
||||
<span class="d-flex">
|
||||
<BaseIcon
|
||||
class="text-light mt-1 mr-1"
|
||||
icon-name="mdiDelete"
|
||||
:size="18"
|
||||
/> {{ t('general.delete') }}</span>
|
||||
</div>
|
||||
</BaseContextMenu>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onBeforeUnmount, PropType, Ref, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||
import BaseIcon from '@/components/BaseIcon.vue';
|
||||
import { useFocusTrap } from '@/composables/useFocusTrap';
|
||||
import Application from '@/ipc-api/Application';
|
||||
import { camelize } from '@/libs/camelize';
|
||||
import { unproxify } from '@/libs/unproxify';
|
||||
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
|
||||
|
||||
const connectionsStore = useConnectionsStore();
|
||||
|
||||
const { addIcon, removeIcon, updateConnectionOrder, getConnectionName } = connectionsStore;
|
||||
const { customIcons } = storeToRefs(connectionsStore);
|
||||
|
||||
const isContext = ref(false);
|
||||
const contextContent: Ref<string> = ref(null);
|
||||
const contextEvent: Ref<MouseEvent> = ref(null);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
@@ -99,8 +152,6 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const { updateConnectionOrder, getConnectionName } = connectionsStore;
|
||||
|
||||
const icons = [
|
||||
{ name: 'default', code: null },
|
||||
|
||||
@@ -160,14 +211,33 @@ const editFolderAppearance = () => {
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const camelize = (text: string) => {
|
||||
const textArr = text.split('-');
|
||||
for (let i = 0; i < textArr.length; i++) {
|
||||
if (i === 0) continue;
|
||||
textArr[i] = textArr[i].charAt(0).toUpperCase() + textArr[i].slice(1);
|
||||
}
|
||||
const setIcon = (code: string, type?: 'mdi' | 'custom') => {
|
||||
localConnection.value.icon = code;
|
||||
localConnection.value.hasCustomIcon = type === 'custom';
|
||||
};
|
||||
|
||||
return textArr.join('');
|
||||
const removeIconHandler = () => {
|
||||
if (localConnection.value.icon === contextContent.value) {
|
||||
setIcon(null);
|
||||
updateConnectionOrder(localConnection.value);
|
||||
}
|
||||
removeIcon(contextContent.value);
|
||||
isContext.value = false;
|
||||
};
|
||||
|
||||
const openFile = async () => {
|
||||
const result = await Application.showOpenDialog({ properties: ['openFile'], filters: [{ name: '"SVG"', extensions: ['svg'] }] });
|
||||
if (result && !result.canceled) {
|
||||
const file = result.filePaths[0];
|
||||
const content = await Application.readFile({ filePath: file, encoding: 'base64url' });
|
||||
addIcon(content);
|
||||
}
|
||||
};
|
||||
|
||||
const contextMenu = (event: MouseEvent, iconUid: string) => {
|
||||
contextEvent.value = event;
|
||||
contextContent.value = iconUid;
|
||||
isContext.value = true;
|
||||
};
|
||||
|
||||
const closeModal = () => emit('close');
|
||||
|
@@ -380,6 +380,7 @@ const startExport = async () => {
|
||||
|
||||
try {
|
||||
const { status, response } = await Schema.export(params);
|
||||
|
||||
if (status === 'success')
|
||||
progressStatus.value = response.cancelled ? t('general.aborted') : t('general.completed');
|
||||
else {
|
||||
|
@@ -75,7 +75,7 @@
|
||||
<code
|
||||
class="cut-text"
|
||||
:title="query.sql"
|
||||
v-html="highlight(highlightWord(query.sql), {html: true})"
|
||||
v-html="highlight(query.sql, {html: true})"
|
||||
/>
|
||||
</div>
|
||||
<div class="tile-bottom-content">
|
||||
@@ -210,17 +210,6 @@ const resizeResults = () => {
|
||||
const refreshScroller = () => resizeResults();
|
||||
const closeModal = () => emit('close');
|
||||
|
||||
const highlightWord = (string: string) => {
|
||||
string = string.replaceAll('<', '<').replaceAll('>', '>');
|
||||
|
||||
if (searchTerm.value) {
|
||||
const regexp = new RegExp(`(${searchTerm.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
||||
return string.replace(regexp, '<span class="text-primary text-bold">$1</span>');
|
||||
}
|
||||
else
|
||||
return string;
|
||||
};
|
||||
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Escape')
|
||||
|
@@ -169,7 +169,7 @@ const emit = defineEmits(['close']);
|
||||
const { trapRef } = useFocusTrap();
|
||||
|
||||
const { getConnectionName } = useConnectionsStore();
|
||||
const { connectionsOrder, connections } = storeToRefs(useConnectionsStore());
|
||||
const { connectionsOrder, connections, customIcons } = storeToRefs(useConnectionsStore());
|
||||
const localConnections = unproxify<ConnectionParams[]>(connections.value);
|
||||
const localConnectionsOrder = unproxify<SidebarElement[]>(connectionsOrder.value);
|
||||
|
||||
@@ -246,7 +246,8 @@ const exportData = () => {
|
||||
|
||||
const exportObj = encrypt(JSON.stringify({
|
||||
connections: filteredConnections,
|
||||
connectionsOrder: filteredOrders
|
||||
connectionsOrder: filteredOrders,
|
||||
customIcons: customIcons.value
|
||||
}), options.value.passkey);
|
||||
|
||||
// console.log(exportObj, JSON.parse(decrypt(exportObj, options.value.passkey)));
|
||||
|
@@ -103,7 +103,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import BaseIcon from '@/components/BaseIcon.vue';
|
||||
import BaseUploadInput from '@/components/BaseUploadInput.vue';
|
||||
import { unproxify } from '@/libs/unproxify';
|
||||
import { SidebarElement, useConnectionsStore } from '@/stores/connections';
|
||||
import { CustomIcon, SidebarElement, useConnectionsStore } from '@/stores/connections';
|
||||
import { useNotificationsStore } from '@/stores/notifications';
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -156,6 +156,7 @@ const importData = () => {
|
||||
const importObj: {
|
||||
connections: ConnectionParams[];
|
||||
connectionsOrder: SidebarElement[];
|
||||
customIcons: CustomIcon[];
|
||||
} = JSON.parse(decrypt(hash, options.value.passkey));
|
||||
|
||||
if (options.value.ignoreDuplicates) {
|
||||
@@ -205,7 +206,6 @@ const importData = () => {
|
||||
.includes(c.uid) ||
|
||||
(c.isFolder && c.connections.every(c => newConnectionsUid.includes(c))));
|
||||
}
|
||||
|
||||
importConnections(importObj);
|
||||
|
||||
addNotification({
|
||||
@@ -215,6 +215,7 @@ const importData = () => {
|
||||
closeModal();
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
addNotification({
|
||||
status: 'error',
|
||||
message: t('application.wrongImportPassword')
|
||||
@@ -222,6 +223,7 @@ const importData = () => {
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
addNotification({
|
||||
status: 'error',
|
||||
message: t('application.wrongFileFormat')
|
||||
|
@@ -341,7 +341,7 @@ onMounted(() => {
|
||||
lastTableFields.value = res.response.map((field: { name: string }) => field.name);
|
||||
editor.value.completers = [tableFieldsCompleter.value];
|
||||
editor.value.execCommand('startAutocomplete');
|
||||
}).catch(console.log);
|
||||
}).catch(console.error);
|
||||
}
|
||||
else
|
||||
editor.value.completers = customCompleter.value;
|
||||
|
@@ -32,7 +32,7 @@
|
||||
v-if="note.type === 'query'"
|
||||
ref="noteParagraph"
|
||||
class="tile-paragraph sql"
|
||||
v-html="highlight(highlightWord(note.note), {html: true})"
|
||||
v-html="highlight(note.note, {html: true})"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
|
@@ -56,6 +56,7 @@
|
||||
>
|
||||
<BaseIcon
|
||||
:icon-name="camelize(element.icon)"
|
||||
:type="element.hasCustomIcon ? 'custom' : 'mdi'"
|
||||
:size="36"
|
||||
/>
|
||||
</div>
|
||||
|
@@ -70,6 +70,7 @@
|
||||
>
|
||||
<BaseIcon
|
||||
:icon-name="camelize(getConnectionOrderByUid(element).icon)"
|
||||
:type="getConnectionOrderByUid(element).hasCustomIcon ? 'custom' : 'mdi'"
|
||||
:size="36"
|
||||
/>
|
||||
</div>
|
||||
|
@@ -253,6 +253,7 @@
|
||||
v-if="results"
|
||||
v-show="!isQuering"
|
||||
ref="queryTable"
|
||||
:is-quering="isQuering"
|
||||
:results="results"
|
||||
:tab-uid="tab.uid"
|
||||
:conn-uid="connection.uid"
|
||||
@@ -715,7 +716,7 @@ const openFile = async () => {
|
||||
const result = await Application.showOpenDialog({ properties: ['openFile'], filters: [{ name: 'SQL', extensions: ['sql', 'txt'] }] });
|
||||
if (result && !result.canceled) {
|
||||
const file = result.filePaths[0];
|
||||
const content = await Application.readFile(file);
|
||||
const content = await Application.readFile({ filePath: file, encoding: 'utf-8' });
|
||||
const fileName = file.split('/').pop().split('\\').pop();
|
||||
if (props.tab.filePath && props.tab.filePath !== file) {
|
||||
newTab({
|
||||
@@ -755,7 +756,7 @@ const saveFile = async () => {
|
||||
};
|
||||
|
||||
const loadFileContent = async (file: string) => {
|
||||
const content = await Application.readFile(file);
|
||||
const content = await Application.readFile({ filePath: file, encoding: 'utf-8' });
|
||||
query.value = content;
|
||||
lastSavedQuery.value = content;
|
||||
};
|
||||
|
@@ -291,6 +291,7 @@ const { consoleHeight } = storeToRefs(consoleStore);
|
||||
const props = defineProps({
|
||||
results: Array as Prop<QueryResult[]>,
|
||||
connUid: String,
|
||||
isQuering: Boolean,
|
||||
mode: String as Prop<'table' | 'query'>,
|
||||
page: {
|
||||
type: Number,
|
||||
@@ -790,7 +791,7 @@ const contextMenu = (event: MouseEvent, cell: any) => {
|
||||
};
|
||||
|
||||
const sort = (field: TableField) => {
|
||||
if (!isSortable.value) return;
|
||||
if (!isSortable.value || props.isQuering) return;
|
||||
|
||||
selectedRows.value = [];
|
||||
let fieldName = field.name;
|
||||
|
@@ -202,6 +202,7 @@
|
||||
v-if="results"
|
||||
ref="queryTable"
|
||||
:results="results"
|
||||
:is-quering="isQuering"
|
||||
:page="page"
|
||||
:tab-uid="tabUid"
|
||||
:conn-uid="connection.uid"
|
||||
|
@@ -312,6 +312,7 @@ export const enUS = {
|
||||
color: 'Color',
|
||||
label: 'Label',
|
||||
icon: 'Icon',
|
||||
customIcon: 'Custom icon',
|
||||
fileName: 'File name',
|
||||
choseFile: 'Choose file',
|
||||
data: 'Data',
|
||||
@@ -409,7 +410,8 @@ export const enUS = {
|
||||
openFile: 'Open file',
|
||||
openNotes: 'Open notes',
|
||||
debugConsole: 'Debug console', // <- console tab name
|
||||
executedQueries: 'Executed queries' // <- console tab name
|
||||
executedQueries: 'Executed queries', // <- console tab name
|
||||
sizeLimitError: 'Maximum size of {size} exceeded'
|
||||
},
|
||||
faker: { // Faker.js methods, used in random generated content
|
||||
address: 'Address',
|
||||
|
@@ -46,6 +46,8 @@ export type AvailableLocale = keyof typeof messages
|
||||
|
||||
const i18n = createI18n<[NestedPartial<MessageSchema>], AvailableLocale>({
|
||||
fallbackLocale: 'en-US',
|
||||
silentTranslationWarn: true,
|
||||
silentFallbackWarn: true,
|
||||
allowComposition: true,
|
||||
messages
|
||||
});
|
||||
|
@@ -15,7 +15,7 @@ export const nlNL = {
|
||||
results: 'Resultaten',
|
||||
size: 'Grootte',
|
||||
mimeType: 'Mime-Type',
|
||||
download: 'Download',
|
||||
download: 'Download', // Same as English
|
||||
add: 'Toevoegen',
|
||||
data: 'Data',
|
||||
properties: 'Eigenschappen',
|
||||
@@ -65,7 +65,13 @@ export const nlNL = {
|
||||
outputFormat: 'Uitvoerformaat',
|
||||
singleFile: 'Enkel {ext}-bestand',
|
||||
zipCompressedFile: 'ZIP gecomprimeerd {ext}-bestand',
|
||||
include: 'Inclusief'
|
||||
include: 'Inclusief',
|
||||
search: 'Zoek',
|
||||
copyName: 'Kopieer naam',
|
||||
title: 'Titel',
|
||||
archive: 'Archief',
|
||||
undo: 'Ongedaan maken',
|
||||
moveTo: 'Verplaats naar'
|
||||
},
|
||||
connection: {
|
||||
connectionName: 'Naam verbinding',
|
||||
@@ -100,7 +106,10 @@ export const nlNL = {
|
||||
readOnlyMode: 'Alleen lezen modus',
|
||||
untrustedConnection: 'Niet vertrouwde verbinding',
|
||||
allConnections: 'Alle verbindingen',
|
||||
searchForConnections: 'Zoek naar verbindingen'
|
||||
searchForConnections: 'Zoek naar verbindingen',
|
||||
singleConnection: 'Enkele verbinding',
|
||||
connection: 'Verbinding',
|
||||
keepAliveInterval: 'Keep alive interval'
|
||||
},
|
||||
database: {
|
||||
schema: 'Schema',
|
||||
@@ -260,7 +269,15 @@ export const nlNL = {
|
||||
targetTable: 'Doeltabel',
|
||||
switchDatabase: 'Wissel van database',
|
||||
importQueryErrors: 'Waarschuwing: {n} fout is opgetreden | Waarschuwing: {n} fouten opgetreden',
|
||||
executedQueries: '{n} query uitgevoerd | {n} queries uitgevoerd'
|
||||
executedQueries: '{n} query uitgevoerd | {n} queries uitgevoerd',
|
||||
insert: 'Invoegen',
|
||||
exportTable: 'Exporteer tabel',
|
||||
savedQueries: 'Opgeslagen queries',
|
||||
searchForElements: 'Zoek naar elementen',
|
||||
searchForSchemas: 'Zoek naar schema\'s',
|
||||
materializedview: 'Materialized view | Materialized views',
|
||||
createNewMaterializedView: 'Materialized view maken',
|
||||
newMaterializedView: 'Nieuwe materialized view'
|
||||
},
|
||||
application: {
|
||||
settings: 'Instellingen',
|
||||
@@ -367,7 +384,30 @@ export const nlNL = {
|
||||
wrongFileFormat: 'Bestand is geen geldig .antares bestand',
|
||||
required: 'Verplicht',
|
||||
choseFile: 'Selecteer bestand',
|
||||
password: 'Wachtwoord'
|
||||
password: 'Wachtwoord',
|
||||
note: 'Notitie',
|
||||
data: 'Data',
|
||||
event: 'Event',
|
||||
key: 'Key',
|
||||
customIcon: 'Aangepast pictogram',
|
||||
fileName: 'bestandsnaam',
|
||||
newFolder: 'Nieuwe map',
|
||||
outOfFolder: 'Out of folder',
|
||||
dataImportSuccess: 'Data succesvol geïmporteerd',
|
||||
thereAreNoNotesYet: 'Er zijn nog geen notities',
|
||||
addNote: 'Voeg notitie toe',
|
||||
editNote: 'Bewerk notitie',
|
||||
saveAsNote: 'Sla op als notitie',
|
||||
showArchivedNotes: 'Toon gearchiveerde notities',
|
||||
hideArchivedNotes: 'Verberg gearchiveerde notities',
|
||||
tag: 'Tag',
|
||||
saveFile: 'Bestand opslaan',
|
||||
saveFileAs: 'Bestand opslaan als',
|
||||
openFile: 'Open bestand',
|
||||
openNotes: 'Open notities',
|
||||
debugConsole: 'Debug Console',
|
||||
executedQueries: 'Voer queries uit',
|
||||
sizeLimitError: 'Maximum grootte {size} overschreden'
|
||||
},
|
||||
faker: {
|
||||
address: 'Adres',
|
||||
@@ -434,7 +474,7 @@ export const nlNL = {
|
||||
engine: 'Engine',
|
||||
past: 'Verleden',
|
||||
now: 'Nu',
|
||||
future: 'Future',
|
||||
future: 'Toekomstig',
|
||||
between: 'Between',
|
||||
recent: 'Recent',
|
||||
soon: 'Soon',
|
||||
@@ -447,11 +487,11 @@ export const nlNL = {
|
||||
amount: 'Amount',
|
||||
transactionType: 'Transaction type',
|
||||
currencyCode: 'Currency code',
|
||||
currencyName: 'Currency name',
|
||||
currencySymbol: 'Currency symbol',
|
||||
bitcoinAddress: 'Bitcoin address',
|
||||
litecoinAddress: 'Litecoin address',
|
||||
creditCardNumber: 'Credit card number',
|
||||
currencyName: 'Valutanaam',
|
||||
currencySymbol: 'Valutateken',
|
||||
bitcoinAddress: 'Bitcoin adres',
|
||||
litecoinAddress: 'Litecoin adres',
|
||||
creditCardNumber: 'Credit card nummer',
|
||||
creditCardCVV: 'Credit card CVV',
|
||||
ethereumAddress: 'Ethereum adres',
|
||||
iban: 'IBAN',
|
||||
@@ -487,10 +527,10 @@ export const nlNL = {
|
||||
sentence: 'Zin',
|
||||
slug: 'Slug',
|
||||
sentences: 'Zinnen',
|
||||
paragraph: 'Paragraph',
|
||||
paragraphs: 'Paragraphs',
|
||||
text: 'Text',
|
||||
lines: 'Lines',
|
||||
paragraph: 'Paragraaf',
|
||||
paragraphs: 'Paragrafen',
|
||||
text: 'Tekst',
|
||||
lines: 'Regels',
|
||||
genre: 'Genre',
|
||||
firstName: 'Voornaam',
|
||||
lastName: 'Achternaam',
|
||||
@@ -500,7 +540,7 @@ export const nlNL = {
|
||||
gender: 'Gender',
|
||||
prefix: 'Prefix',
|
||||
suffix: 'Suffix',
|
||||
title: 'Title',
|
||||
title: 'Titel',
|
||||
jobDescriptor: 'Job descriptor',
|
||||
jobArea: 'Job area',
|
||||
jobType: 'Job type',
|
||||
@@ -514,24 +554,24 @@ export const nlNL = {
|
||||
objectElement: 'Object element',
|
||||
uuid: 'Uuid',
|
||||
boolean: 'Boolean',
|
||||
image: 'Image',
|
||||
image: 'Afbeelding',
|
||||
locale: 'Locale',
|
||||
alpha: 'Alpha',
|
||||
alphaNumeric: 'Alphanumeric',
|
||||
hexaDecimal: 'Hexadecimal',
|
||||
fileName: 'File name',
|
||||
alphaNumeric: 'Alfanumeriek',
|
||||
hexaDecimal: 'Hexadecimaal',
|
||||
fileName: 'Bestandsnaam',
|
||||
commonFileName: 'Common file name',
|
||||
mimeType: 'Mime type',
|
||||
commonFileType: 'Common file type',
|
||||
commonFileExt: 'Common file extension',
|
||||
fileType: 'File type',
|
||||
fileType: 'Filetype',
|
||||
fileExt: 'File extension',
|
||||
directoryPath: 'Directory path',
|
||||
filePath: 'File path',
|
||||
semver: 'Semver',
|
||||
manufacturer: 'Manufacturer',
|
||||
manufacturer: 'Fabrikant',
|
||||
model: 'Model',
|
||||
fuel: 'Fuel',
|
||||
fuel: 'Brandstof',
|
||||
vin: 'Vin'
|
||||
}
|
||||
};
|
||||
|
@@ -32,8 +32,8 @@ export default class {
|
||||
return ipcRenderer.invoke('unregister-shortcuts');
|
||||
}
|
||||
|
||||
static readFile (path: string): Promise<string> {
|
||||
return ipcRenderer.invoke('read-file', path);
|
||||
static readFile (params: {filePath: string; encoding: string}): Promise<string> {
|
||||
return ipcRenderer.invoke('read-file', params);
|
||||
}
|
||||
|
||||
static writeFile (path: string, content: unknown) {
|
||||
|
@@ -5,7 +5,11 @@ import { ipcRenderer } from 'electron';
|
||||
import * as Store from 'electron-store';
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { i18n } from '@/i18n';
|
||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||
|
||||
import { useNotificationsStore } from './notifications';
|
||||
|
||||
let key = localStorage.getItem('key');
|
||||
|
||||
export interface SidebarElement {
|
||||
@@ -16,8 +20,11 @@ export interface SidebarElement {
|
||||
color?: string;
|
||||
name?: string;
|
||||
icon?: null | string;
|
||||
hasCustomIcon?: boolean;
|
||||
}
|
||||
|
||||
export interface CustomIcon {base64: string; uid: string}
|
||||
|
||||
if (!key) { // If no key in local storace
|
||||
const storedKey = ipcRenderer.sendSync('get-key');// Ask for key stored on disk
|
||||
|
||||
@@ -44,7 +51,8 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
state: () => ({
|
||||
connections: persistentStore.get('connections', []) as ConnectionParams[],
|
||||
lastConnections: persistentStore.get('lastConnections', []) as {uid: string; time: number}[],
|
||||
connectionsOrder: persistentStore.get('connectionsOrder', []) as SidebarElement[]
|
||||
connectionsOrder: persistentStore.get('connectionsOrder', []) as SidebarElement[],
|
||||
customIcons: persistentStore.get('custom_icons', []) as CustomIcon[]
|
||||
}),
|
||||
getters: {
|
||||
getConnectionByUid: state => (uid:string) => state.connections.find(connection => connection.uid === uid),
|
||||
@@ -74,7 +82,8 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
.find(connection => connection.uid === uid),
|
||||
getFolders: state => state.connectionsOrder.filter(conn => conn.isFolder),
|
||||
getConnectionFolder: state => (uid:string) => state.connectionsOrder
|
||||
.find(folder => folder.isFolder && folder.connections.includes(uid))
|
||||
.find(folder => folder.isFolder && folder.connections.includes(uid)),
|
||||
getIconByUid: state => (uid:string) => state.customIcons.find(i => i.uid === uid)
|
||||
},
|
||||
actions: {
|
||||
addConnection (connection: ConnectionParams) {
|
||||
@@ -198,7 +207,8 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
client: conn.client,
|
||||
uid: conn.uid,
|
||||
icon: conn.icon,
|
||||
name: conn.name
|
||||
name: conn.name,
|
||||
hasCustomIcon: conn.hasCustomIcon
|
||||
});
|
||||
|
||||
connIndex = connections.findIndex((conn, i) => conn.uid === el.uid && i !== el.index);
|
||||
@@ -247,15 +257,41 @@ export const useConnectionsStore = defineStore('connections', {
|
||||
this.connectionsOrder = (this.connectionsOrder as SidebarElement[]).filter(el => !emptyFolders.includes(el.uid));
|
||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
||||
},
|
||||
// Custom Icons
|
||||
addIcon (svg: string) {
|
||||
if (svg.length > 16384) {
|
||||
const { t } = i18n.global;
|
||||
useNotificationsStore().addNotification({
|
||||
status: 'error',
|
||||
message: t('application.sizeLimitError', { size: '16KB' })
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const icon: CustomIcon = {
|
||||
uid: uidGen('I'),
|
||||
base64: svg
|
||||
};
|
||||
|
||||
this.customIcons.push(icon);
|
||||
persistentStore.set('custom_icons', this.customIcons);
|
||||
},
|
||||
removeIcon (uid: string) {
|
||||
this.customIcons = this.customIcons.filter((i: CustomIcon) => i.uid !== uid);
|
||||
persistentStore.set('custom_icons', this.customIcons);
|
||||
},
|
||||
importConnections (importObj: {
|
||||
connections: ConnectionParams[];
|
||||
connectionsOrder: SidebarElement[];
|
||||
customIcons: CustomIcon[];
|
||||
}) {
|
||||
this.connections = [...this.connections, ...importObj.connections];
|
||||
this.connectionsOrder = [...this.connectionsOrder, ...importObj.connectionsOrder];
|
||||
this.customIcons = [...this.customIcons, ...importObj.customIcons];
|
||||
|
||||
persistentStore.set('connections', this.connections);
|
||||
persistentStore.set('connectionsOrder', this.connectionsOrder);
|
||||
persistentStore.set('customIcons', this.customIcons);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user