mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-26 01:46:02 +00:00
JSON UI
This commit is contained in:
@@ -9,6 +9,8 @@ const {
|
|||||||
putCloudContent,
|
putCloudContent,
|
||||||
removeCloudCachedConnection,
|
removeCloudCachedConnection,
|
||||||
getPromoWidgetData,
|
getPromoWidgetData,
|
||||||
|
getPromoWidgetList,
|
||||||
|
getPromoWidgetPreview,
|
||||||
} = require('../utility/cloudIntf');
|
} = require('../utility/cloudIntf');
|
||||||
const connections = require('./connections');
|
const connections = require('./connections');
|
||||||
const socket = require('../utility/socket');
|
const socket = require('../utility/socket');
|
||||||
@@ -296,6 +298,16 @@ module.exports = {
|
|||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
promoWidgetList_meta: true,
|
||||||
|
async promoWidgetList() {
|
||||||
|
return getPromoWidgetList();
|
||||||
|
},
|
||||||
|
|
||||||
|
promoWidgetPreview_meta: true,
|
||||||
|
async promoWidgetPreview({ campaign, variant }) {
|
||||||
|
return getPromoWidgetPreview(campaign, variant);
|
||||||
|
},
|
||||||
|
|
||||||
// chatStream_meta: {
|
// chatStream_meta: {
|
||||||
// raw: true,
|
// raw: true,
|
||||||
// method: 'post',
|
// method: 'post',
|
||||||
|
|||||||
@@ -480,6 +480,16 @@ async function getPromoWidgetData() {
|
|||||||
return promoWidgetData;
|
return promoWidgetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getPromoWidgetPreview(campaign, variant) {
|
||||||
|
const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/premium-promo-widget-preview/${campaign}/${variant}`);
|
||||||
|
return resp.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPromoWidgetList() {
|
||||||
|
const resp = await axios.default.get(`${DBGATE_CLOUD_URL}/promo-widget-list`);
|
||||||
|
return resp.data;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createDbGateIdentitySession,
|
createDbGateIdentitySession,
|
||||||
startCloudTokenChecking,
|
startCloudTokenChecking,
|
||||||
@@ -498,4 +508,6 @@ module.exports = {
|
|||||||
readCloudTestTokenHolder,
|
readCloudTestTokenHolder,
|
||||||
getPublicIpInfo,
|
getPublicIpInfo,
|
||||||
getPromoWidgetData,
|
getPromoWidgetData,
|
||||||
|
getPromoWidgetPreview,
|
||||||
|
getPromoWidgetList,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,6 +35,11 @@
|
|||||||
background: linear-gradient(135deg, #1686c8, #8a25b1);
|
background: linear-gradient(135deg, #1686c8, #8a25b1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.premium-gradient {
|
||||||
|
background: linear-gradient(135deg, #1686c8, #8a25b1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.web-color-primary {
|
.web-color-primary {
|
||||||
background: #1686c8;
|
background: #1686c8;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
getCloudSigninTokenHolder,
|
getCloudSigninTokenHolder,
|
||||||
getExtensions,
|
getExtensions,
|
||||||
getVisibleToolbar,
|
getVisibleToolbar,
|
||||||
|
promoWidgetPreview,
|
||||||
visibleToolbar,
|
visibleToolbar,
|
||||||
visibleWidgetSideBar,
|
visibleWidgetSideBar,
|
||||||
} from '../stores';
|
} from '../stores';
|
||||||
@@ -50,6 +51,7 @@ import { isProApp } from '../utility/proTools';
|
|||||||
import { openWebLink } from '../utility/simpleTools';
|
import { openWebLink } from '../utility/simpleTools';
|
||||||
import { _t } from '../translations';
|
import { _t } from '../translations';
|
||||||
import ExportImportConnectionsModal from '../modals/ExportImportConnectionsModal.svelte';
|
import ExportImportConnectionsModal from '../modals/ExportImportConnectionsModal.svelte';
|
||||||
|
import { getBoolSettingsValue } from '../settings/settingsTools';
|
||||||
|
|
||||||
// function themeCommand(theme: ThemeDefinition) {
|
// function themeCommand(theme: ThemeDefinition) {
|
||||||
// return {
|
// return {
|
||||||
@@ -1164,6 +1166,41 @@ registerCommand({
|
|||||||
onClick: () => currentDatabase.set(null),
|
onClick: () => currentDatabase.set(null),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let loadedCampaignList = [];
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'internal.loadCampaigns',
|
||||||
|
category: 'Internal',
|
||||||
|
name: 'Load campaign list',
|
||||||
|
testEnabled: () => getBoolSettingsValue('internal.showCampaigns', false),
|
||||||
|
onClick: async () => {
|
||||||
|
const resp = await apiCall('cloud/promo-widget-list', {});
|
||||||
|
loadedCampaignList = resp;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registerCommand({
|
||||||
|
id: 'internal.showCampaigns',
|
||||||
|
category: 'Internal',
|
||||||
|
name: 'Show campaigns',
|
||||||
|
testEnabled: () => getBoolSettingsValue('internal.showCampaigns', false) && loadedCampaignList?.length > 0,
|
||||||
|
getSubCommands: () => {
|
||||||
|
return loadedCampaignList.map(campaign => ({
|
||||||
|
text: `${campaign.campaignName} (${campaign.countries || 'Global'}) - #${campaign.quantileRank ?? '*'}/${
|
||||||
|
campaign.quantileGroupCount ?? '*'
|
||||||
|
} - ${campaign.variantIdentifier}`,
|
||||||
|
onClick: async () => {
|
||||||
|
promoWidgetPreview.set(
|
||||||
|
await apiCall('cloud/promo-widget-preview', {
|
||||||
|
campaign: campaign.campaignIdentifier,
|
||||||
|
variant: campaign.variantIdentifier,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const electron = getElectron();
|
const electron = getElectron();
|
||||||
if (electron) {
|
if (electron) {
|
||||||
electron.addEventListener('run-command', (e, commandId) => runCommand(commandId));
|
electron.addEventListener('run-command', (e, commandId) => runCommand(commandId));
|
||||||
|
|||||||
@@ -1,22 +1,29 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import JsonUiCountdown from './JsonUiCountdown.svelte';
|
||||||
import JsonUiHeading from './JsonUiHeading.svelte';
|
import JsonUiHeading from './JsonUiHeading.svelte';
|
||||||
|
import JsonUiHighlight from './JsonUiHighlight.svelte';
|
||||||
import JsonUiLinkButton from './JsonUiLinkButton.svelte';
|
import JsonUiLinkButton from './JsonUiLinkButton.svelte';
|
||||||
|
import JsonUiMarkdown from './JsonUiMarkdown.svelte';
|
||||||
import JsonUiTextBlock from './JsonUiTextBlock.svelte';
|
import JsonUiTextBlock from './JsonUiTextBlock.svelte';
|
||||||
import JsonUiTickList from './JsonUiTickList.svelte';
|
import JsonUiTickList from './JsonUiTickList.svelte';
|
||||||
import { JsonUiBlock } from './jsonuitypes';
|
import { JsonUiBlock } from './jsonuitypes';
|
||||||
|
|
||||||
export let blocks: JsonUiBlock[] = [];
|
export let blocks: JsonUiBlock[] = [];
|
||||||
|
export let passProps = {};
|
||||||
|
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
text: JsonUiTextBlock,
|
text: JsonUiTextBlock,
|
||||||
heading: JsonUiHeading,
|
heading: JsonUiHeading,
|
||||||
ticklist: JsonUiTickList,
|
ticklist: JsonUiTickList,
|
||||||
button: JsonUiLinkButton,
|
button: JsonUiLinkButton,
|
||||||
|
markdown: JsonUiMarkdown,
|
||||||
|
highlight: JsonUiHighlight,
|
||||||
|
countdown: JsonUiCountdown,
|
||||||
} as const;
|
} as const;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each blocks as block, i}
|
{#each blocks as block, i}
|
||||||
{#if block.type in componentMap}
|
{#if block.type in componentMap}
|
||||||
<svelte:component this={componentMap[block.type]} {...block} />
|
<svelte:component this={componentMap[block.type]} {...block} {...passProps} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
73
packages/web/src/jsonui/JsonUiCountdown.svelte
Normal file
73
packages/web/src/jsonui/JsonUiCountdown.svelte
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
export let colorClass: string = 'premium-gradient';
|
||||||
|
export let validTo;
|
||||||
|
|
||||||
|
function formatRemaining(validTo, now) {
|
||||||
|
let diffMs = validTo.getTime() - now.getTime();
|
||||||
|
if (diffMs <= 0) return '0 minutes';
|
||||||
|
|
||||||
|
const totalMinutes = Math.floor(diffMs / 60000);
|
||||||
|
const days = Math.floor(totalMinutes / (24 * 60));
|
||||||
|
const hours = Math.floor((totalMinutes % (24 * 60)) / 60);
|
||||||
|
const minutes = totalMinutes % 60;
|
||||||
|
|
||||||
|
const parts = [];
|
||||||
|
const en = (n, unit) => ({
|
||||||
|
num: n,
|
||||||
|
unit: n == 1 ? unit : unit + 's',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (days) parts.push(en(days, 'day'));
|
||||||
|
if (hours) parts.push(en(hours, 'hour'));
|
||||||
|
// Always include minutes to report down to minutes
|
||||||
|
parts.push(en(minutes, 'minute'));
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentDate = new Date();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
currentDate = new Date();
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$: parts = formatRemaining(new Date(validTo), currentDate);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if validTo}
|
||||||
|
<div class="countdown {colorClass}">
|
||||||
|
<span class="big">Offer ends in:</span><br />
|
||||||
|
{#each parts as part}
|
||||||
|
<span class="part">
|
||||||
|
<span class="big">{part.num}</span>
|
||||||
|
{part.unit}
|
||||||
|
</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.countdown {
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px;
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big {
|
||||||
|
font-size: large;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
19
packages/web/src/jsonui/JsonUiHighlight.svelte
Normal file
19
packages/web/src/jsonui/JsonUiHighlight.svelte
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let text: string;
|
||||||
|
export let colorClass: string = 'premium-gradient';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="highlight {colorClass}">
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.highlight {
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px;
|
||||||
|
font-size: large;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
15
packages/web/src/jsonui/JsonUiMarkdown.svelte
Normal file
15
packages/web/src/jsonui/JsonUiMarkdown.svelte
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Markdown from '../elements/Markdown.svelte';
|
||||||
|
|
||||||
|
export let text: string;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Markdown source={text} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -187,6 +187,9 @@ export const seenPremiumPromoWidget = writableWithStorage(null, 'seenPremiumProm
|
|||||||
|
|
||||||
export const cloudConnectionsStore = writable({});
|
export const cloudConnectionsStore = writable({});
|
||||||
|
|
||||||
|
export const promoWidgetPreview = writable(null);
|
||||||
|
|
||||||
|
|
||||||
export const DEFAULT_OBJECT_SEARCH_SETTINGS = {
|
export const DEFAULT_OBJECT_SEARCH_SETTINGS = {
|
||||||
pureName: true,
|
pureName: true,
|
||||||
schemaName: false,
|
schemaName: false,
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import JsonUiContentRenderer from '../jsonui/JsonUiContentRenderer.svelte';
|
import JsonUiContentRenderer from '../jsonui/JsonUiContentRenderer.svelte';
|
||||||
|
import { promoWidgetPreview } from '../stores';
|
||||||
import { usePromoWidget } from '../utility/metadataLoaders';
|
import { usePromoWidget } from '../utility/metadataLoaders';
|
||||||
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
|
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';
|
||||||
|
|
||||||
const promoWidget = usePromoWidget({});
|
const promoWidget = usePromoWidget({});
|
||||||
|
|
||||||
|
$: promoWidgetData = $promoWidgetPreview || $promoWidget;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<WidgetsInnerContainer>
|
<WidgetsInnerContainer>
|
||||||
{#if $promoWidget?.state == 'data'}
|
{#if promoWidgetData?.state == 'data'}
|
||||||
<JsonUiContentRenderer blocks={$promoWidget?.blocks} />
|
<JsonUiContentRenderer blocks={promoWidgetData?.blocks} passProps={{ validTo: promoWidgetData?.validTo }} />
|
||||||
{/if}
|
{/if}
|
||||||
</WidgetsInnerContainer>
|
</WidgetsInnerContainer>
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
getCurrentConfig,
|
getCurrentConfig,
|
||||||
cloudSigninTokenHolder,
|
cloudSigninTokenHolder,
|
||||||
seenPremiumPromoWidget,
|
seenPremiumPromoWidget,
|
||||||
|
promoWidgetPreview,
|
||||||
} from '../stores';
|
} from '../stores';
|
||||||
import mainMenuDefinition from '../../../../app/src/mainMenuDefinition';
|
import mainMenuDefinition from '../../../../app/src/mainMenuDefinition';
|
||||||
import hasPermission from '../utility/hasPermission';
|
import hasPermission from '../utility/hasPermission';
|
||||||
@@ -167,6 +168,8 @@
|
|||||||
openWebLink(url, true);
|
openWebLink(url, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: promoWidgetData = $promoWidgetPreview || $promoWidget;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="main">
|
<div class="main">
|
||||||
@@ -177,7 +180,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#each widgets
|
{#each widgets
|
||||||
.filter(x => x && hasPermission(`widgets/${x.name}`))
|
.filter(x => x && hasPermission(`widgets/${x.name}`))
|
||||||
.filter(x => !x.isPremiumPromo || (!isProApp() && $promoWidget?.state == 'data'))
|
.filter(x => !x.isPremiumPromo || (!isProApp() && promoWidgetData?.state == 'data'))
|
||||||
// .filter(x => !x.isPremiumOnly || isProApp())
|
// .filter(x => !x.isPremiumOnly || isProApp())
|
||||||
.filter(x => x.name != 'cloud-private' || $cloudSigninTokenHolder) as item}
|
.filter(x => x.name != 'cloud-private' || $cloudSigninTokenHolder) as item}
|
||||||
<div
|
<div
|
||||||
@@ -186,7 +189,7 @@
|
|||||||
data-testid={`WidgetIconPanel_${item.name}`}
|
data-testid={`WidgetIconPanel_${item.name}`}
|
||||||
on:click={() => handleChangeWidget(item.name)}
|
on:click={() => handleChangeWidget(item.name)}
|
||||||
>
|
>
|
||||||
{#if item.isPremiumPromo && $promoWidget?.isColoredIcon}
|
{#if item.isPremiumPromo && promoWidgetData?.isColoredIcon}
|
||||||
<FontIcon
|
<FontIcon
|
||||||
icon={item.icon}
|
icon={item.icon}
|
||||||
title={item.title}
|
title={item.title}
|
||||||
@@ -197,7 +200,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if item.isPremiumPromo}
|
{#if item.isPremiumPromo}
|
||||||
<div class="premium-promo">Premium</div>
|
<div class="premium-promo">Premium</div>
|
||||||
{#if $promoWidget?.identifier != $seenPremiumPromoWidget}
|
{#if promoWidgetData?.identifier != $seenPremiumPromoWidget}
|
||||||
<div class="premium-promo-not-seen">•</div>
|
<div class="premium-promo-not-seen">•</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user