Skip to content

Commit e70d745

Browse files
authored
Merge pull request #2553 from RedisInsight/fe/feature/RI-4810_Open_existing_db_in_new_window
#RI-4810 - open existing db in new window
2 parents ca694ff + 576ab66 commit e70d745

File tree

17 files changed

+124
-53
lines changed

17 files changed

+124
-53
lines changed

redisinsight/desktop/src/lib/app/deep-link.handlers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { IpcOnEvent } from 'uiSrc/electron/constants'
88

99
export interface IParsedDeepLink {
1010
initialPage?: string
11-
target: string
12-
from: string
11+
target?: string
12+
from?: string
1313
}
1414

1515
export const deepLinkHandler = async (from?: string): Promise<undefined | IParsedDeepLink> => {

redisinsight/desktop/src/lib/app/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { initWindowIPCHandlers } from 'desktopSrc/lib/window'
12
import { initAppHandlers } from './app.handlers'
23
import { initDialogHandlers } from './dialog.handlers'
34
import { initIPCHandlers } from './ipc.handlers'
@@ -6,4 +7,6 @@ export const initElectronHandlers = () => {
67
initAppHandlers()
78
initIPCHandlers()
89
initDialogHandlers()
10+
11+
initWindowIPCHandlers()
912
}

redisinsight/desktop/src/lib/window/window.handlers.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { BrowserWindow, app, shell } from 'electron'
1+
import { BrowserWindow, app, shell, ipcMain } from 'electron'
22
import {
33
MenuBuilder,
44
getDisplayAppInTrayValue,
55
getIsQuiting,
66
getTray,
77
getTrayInstance,
8-
electronStore
8+
electronStore,
9+
windowFactory,
10+
WindowType,
911
} from 'desktopSrc/lib'
10-
import { ElectronStorageItem, IpcOnEvent } from 'uiSrc/electron/constants'
12+
import { IpcInvokeEvent, ElectronStorageItem, IpcOnEvent } from 'uiSrc/electron/constants'
13+
import { Pages } from 'uiSrc/constants/pages'
1114

1215
export const initWindowHandlers = (
1316
newWindow: BrowserWindow,
@@ -92,3 +95,9 @@ export const initWindowHandlers = (
9295
return { action: 'deny' }
9396
})
9497
}
98+
99+
export const initWindowIPCHandlers = () => {
100+
ipcMain.handle(IpcInvokeEvent.windowOpen, async (_event, { location }) => {
101+
await windowFactory(WindowType.Main, null, { parsedDeepLink: { initialPage: location } })
102+
})
103+
}

redisinsight/desktop/src/utils/resolveHtmlPath.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ export const resolveHtmlPath = (htmlFileName: string, parsedDeepLink?: IParsedDe
1717
if (parsedDeepLink) {
1818
try {
1919
if (parsedDeepLink.initialPage) {
20-
resolved += `${parsedDeepLink.initialPage}`
20+
const initialPage = parsedDeepLink.initialPage.slice(+parsedDeepLink.initialPage.startsWith('/'))
21+
resolved += initialPage
2122
}
2223

2324
const queryParameters = new URLSearchParams([
24-
['from', parsedDeepLink.from],
25-
['target', parsedDeepLink.target],
25+
['from', parsedDeepLink.from || ''],
26+
['target', parsedDeepLink.target || ''],
2627
])
2728

28-
resolved += `?${queryParameters.toString()}`
29+
resolved += `${resolved.indexOf('?') !== -1 ? '&' : '?'}${queryParameters.toString()}`
2930
} catch (e) {
3031
// todo: log error
3132
}

redisinsight/ui/src/components/live-time-recommendations/components/recommendation/Recommendation.spec.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Pages } from 'uiSrc/constants'
99
import { updateRecommendation } from 'uiSrc/slices/recommendations/recommendations'
1010
import { INSTANCE_ID_MOCK } from 'uiSrc/mocks/handlers/instances/instancesHandlers'
1111
import { MOCK_RECOMMENDATIONS } from 'uiSrc/constants/mocks/mock-recommendations'
12+
import { openNewWindowDatabase } from 'uiSrc/utils'
1213
import Recommendation, { IProps } from './Recommendation'
1314

1415
const recommendationsContent = MOCK_RECOMMENDATIONS
@@ -24,6 +25,11 @@ jest.mock('uiSrc/telemetry', () => ({
2425
sendEventTelemetry: jest.fn(),
2526
}))
2627

28+
jest.mock('uiSrc/utils', () => ({
29+
...jest.requireActual('uiSrc/utils'),
30+
openNewWindowDatabase: jest.fn(),
31+
}))
32+
2733
let store: typeof mockedStore
2834
beforeEach(() => {
2935
cleanup()
@@ -88,10 +94,10 @@ describe('Recommendation', () => {
8894
sendEventTelemetry.mockRestore()
8995
})
9096

91-
it('should properly push history on workbench page to specific guide', () => {
97+
it('should properly call openNewWindowDatabase and open a new window on workbench page to specific guide', () => {
9298
// will be improved
93-
const pushMock = jest.fn()
94-
reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock })
99+
const openNewWindowDatabaseMock = jest.fn();
100+
(openNewWindowDatabase as jest.Mock).mockImplementation(() => openNewWindowDatabaseMock)
95101

96102
const { container } = render(
97103
<Recommendation
@@ -106,7 +112,7 @@ describe('Recommendation', () => {
106112
fireEvent.click(container.querySelector('[data-test-subj="searchJSON-button"]') as HTMLButtonElement)
107113
fireEvent.click(screen.getByTestId('searchJSON-to-tutorial-btn'))
108114

109-
expect(pushMock)
115+
expect(openNewWindowDatabase)
110116
.toHaveBeenCalledWith(`${Pages.workbench(INSTANCE_ID_MOCK)}?guidePath=quick-guides/working-with-hash.html`)
111117
expect(sendEventTelemetry).toBeCalledWith({
112118
event: TelemetryEvent.INSIGHTS_RECOMMENDATION_TUTORIAL_CLICKED,
@@ -117,12 +123,13 @@ describe('Recommendation', () => {
117123
}
118124
})
119125
sendEventTelemetry.mockRestore()
126+
openNewWindowDatabase.mockRestore()
120127
})
121128

122129
it('should properly push history on workbench page to specific tutorial', () => {
123130
// will be improved
124-
const pushMock = jest.fn()
125-
reactRouterDom.useHistory = jest.fn().mockReturnValue({ push: pushMock })
131+
const openNewWindowDatabaseMock = jest.fn();
132+
(openNewWindowDatabase as jest.Mock).mockImplementation(() => openNewWindowDatabaseMock)
126133

127134
const { container } = render(
128135
<Recommendation
@@ -137,7 +144,7 @@ describe('Recommendation', () => {
137144
fireEvent.click(container.querySelector('[data-test-subj="searchJSON-button"]') as HTMLButtonElement)
138145
fireEvent.click(screen.getByTestId('searchJSON-to-tutorial-btn'))
139146

140-
expect(pushMock)
147+
expect(openNewWindowDatabase)
141148
.toHaveBeenCalledWith(`${Pages.workbench(INSTANCE_ID_MOCK)}?guidePath=/redis_stack/working_with_json.md`)
142149
expect(sendEventTelemetry).toBeCalledWith({
143150
event: TelemetryEvent.INSIGHTS_RECOMMENDATION_TUTORIAL_CLICKED,
@@ -148,6 +155,7 @@ describe('Recommendation', () => {
148155
}
149156
})
150157
sendEventTelemetry.mockRestore()
158+
openNewWindowDatabase.mockRestore()
151159
})
152160

153161
it('should render hide/unhide button', () => {

redisinsight/ui/src/components/live-time-recommendations/components/recommendation/Recommendation.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import { isUndefined } from 'lodash'
1717
import cx from 'classnames'
1818

19-
import { Nullable, Maybe } from 'uiSrc/utils'
19+
import { Nullable, Maybe, openNewWindowDatabase } from 'uiSrc/utils'
2020
import { renderRecommendationContent } from 'uiSrc/utils/recommendation/utils'
2121
import { Pages, Theme } from 'uiSrc/constants'
2222
import { RecommendationVoting, RecommendationCopyComponent } from 'uiSrc/components'
@@ -83,7 +83,7 @@ const Recommendation = ({
8383

8484
dispatch(setWorkbenchEAMinimized(false))
8585
if (tutorial) {
86-
history.push(`${Pages.workbench(instanceId)}?guidePath=${tutorial}`)
86+
openNewWindowDatabase(`${Pages.workbench(instanceId)}?guidePath=${tutorial}`)
8787
return
8888
}
8989

redisinsight/ui/src/components/messages/filter-not-available/FilterNotAvailable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const FilterNotAvailable = ({ onClose } : { onClose?: () => void }) => {
2828
{!!freeInstance && (
2929
<>
3030
<EuiText color="subdued">
31-
Use your Redis Stack database in Redis Enterprise Cloud to start exploring these capabilities.
31+
Use your free all-in-one Redis Enterprise Cloud database to start exploring these capabilities.
3232
</EuiText>
3333
<EuiSpacer size="l" />
3434
<OAuthConnectFreeDb source={OAuthSocialSource.BrowserFiltering} onSuccessClick={onClose} />

redisinsight/ui/src/components/messages/module-not-loaded/ModuleNotLoaded.tsx

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
import React, { useCallback, useEffect, useState } from 'react'
1+
import React, { useCallback, useContext, useEffect, useState } from 'react'
22
import cx from 'classnames'
33
import {
44
EuiTextColor,
55
EuiText,
66
EuiTitle,
77
EuiLink,
88
EuiButton,
9+
EuiIcon,
910
} from '@elastic/eui'
1011
import { useSelector } from 'react-redux'
1112

1213
import { ReactComponent as MobileIcon } from 'uiSrc/assets/img/icons/mobile_module_not_loaded.svg'
1314
import { ReactComponent as DesktopIcon } from 'uiSrc/assets/img/icons/module_not_loaded.svg'
15+
import { ReactComponent as TelescopeDark } from 'uiSrc/assets/img/telescope-dark.svg'
16+
import { ReactComponent as TelescopeLight } from 'uiSrc/assets/img/telescope-light.svg'
1417
import { ReactComponent as CheerIcon } from 'uiSrc/assets/img/icons/cheer.svg'
15-
import { MODULE_NOT_LOADED_CONTENT as CONTENT, MODULE_TEXT_VIEW } from 'uiSrc/constants'
18+
import { MODULE_NOT_LOADED_CONTENT as CONTENT, MODULE_TEXT_VIEW, Theme } from 'uiSrc/constants'
1619
import { RedisDefaultModules, OAuthSocialSource } from 'uiSrc/slices/interfaces'
1720
import { OAuthConnectFreeDb, OAuthSsoHandlerDialog } from 'uiSrc/components'
1821
import { freeInstanceSelector } from 'uiSrc/slices/instances/instances'
22+
import { ThemeContext } from 'uiSrc/contexts/themeContext'
1923

2024
import styles from './styles.module.scss'
2125

@@ -51,6 +55,7 @@ const ListItem = ({ item }: { item: string }) => (
5155
const ModuleNotLoaded = ({ moduleName, id, type = 'workbench', onClose }: IProps) => {
5256
const [width, setWidth] = useState(0)
5357
const freeInstance = useSelector(freeInstanceSelector)
58+
const { theme } = useContext(ThemeContext)
5459

5560
const module = MODULE_TEXT_VIEW[moduleName]
5661

@@ -78,8 +83,8 @@ const ModuleNotLoaded = ({ moduleName, id, type = 'workbench', onClose }: IProps
7883
{`Create a free Redis Stack database with ${moduleName} which extends the core capabilities of open-source Redis`}
7984
</EuiText>
8085
) : (
81-
<EuiText className={cx(styles.text, styles.marginBottom)}>
82-
Use your Redis Stack database in Redis Enterprise Cloud to start exploring these capabilities.
86+
<EuiText className={cx(styles.text, styles.marginBottom, styles.textFooter)}>
87+
Use your free all-in-one Redis Enterprise Cloud database to start exploring these capabilities.
8388
</EuiText>
8489
)), [freeInstance])
8590

@@ -95,9 +100,18 @@ const ModuleNotLoaded = ({ moduleName, id, type = 'workbench', onClose }: IProps
95100
>
96101
<div className={styles.flex}>
97102
<div>
98-
{width > MAX_ELEMENT_WIDTH
99-
? <DesktopIcon className={styles.bigIcon} />
100-
: <MobileIcon className={styles.icon} />}
103+
{type !== 'browser' && (
104+
width > MAX_ELEMENT_WIDTH
105+
? <DesktopIcon className={styles.bigIcon} />
106+
: <MobileIcon className={styles.icon} />
107+
)}
108+
{type === 'browser' && (
109+
<EuiIcon
110+
className={styles.iconTelescope}
111+
type={theme === Theme.Dark ? TelescopeDark : TelescopeLight}
112+
size="original"
113+
/>
114+
)}
101115
</div>
102116
<div className={styles.contentWrapper}>
103117
{renderTitle(width, module)}

redisinsight/ui/src/components/messages/module-not-loaded/styles.module.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@
4747
width: 317px;
4848
margin-bottom: 42px;
4949
}
50+
51+
.iconTelescope {
52+
width: 250px;
53+
margin-left: 15px;
54+
}
5055
}
5156

5257
.list.bloomList {
@@ -148,5 +153,20 @@
148153

149154
&.modal {
150155
padding: 30px;
156+
background-color: var(--browserTableRowEven);
157+
158+
.title {
159+
padding-top: 42px;
160+
font-size: 18px;
161+
line-height: 1;
162+
}
163+
164+
.bigText {
165+
font-size: 14px;
166+
}
167+
168+
.textFooter {
169+
font-weight: 500;
170+
}
151171
}
152172
}

redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('OAuthConnectFreeDb', () => {
5656
}
5757
})
5858

59-
const expectedActions = [setDefaultInstance(), resetConnectedInstance()]
59+
const expectedActions = [setDefaultInstance()]
6060
expect(store.getActions()).toEqual(expectedActions)
6161
})
6262

0 commit comments

Comments
 (0)