Skip to content

Commit f110a9c

Browse files
committed
added functionality to share window data in URL
1 parent eeb2890 commit f110a9c

File tree

8 files changed

+178
-43
lines changed

8 files changed

+178
-43
lines changed

packages/altair-app/src/app/modules/altair/effects/account.effect.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -281,20 +281,6 @@ export class AccountEffects {
281281
{ dispatch: false }
282282
);
283283

284-
onAccountCheckedInit$ = createEffect(
285-
() => {
286-
return this.actions$.pipe(
287-
ofType(accountActions.ACCOUNT_IS_LOGGED_IN),
288-
switchMap((action) => {
289-
// check for shared links
290-
this.sharingService.checkForShareUrl();
291-
return EMPTY;
292-
})
293-
);
294-
},
295-
{ dispatch: false }
296-
);
297-
298284
constructor(
299285
private actions$: Actions,
300286
private store: Store<RootState>,

packages/altair-app/src/app/modules/altair/effects/windows.effect.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { downloadJson, openFile } from '../utils';
1515
import { RootState } from 'altair-graphql-core/build/types/state/state.interfaces';
1616
import { debug } from '../utils/logger';
1717
import { windowHasUnsavedChanges } from '../store';
18+
import { APP_INIT_ACTION } from '../store/action';
19+
import { SharingService } from '../services';
1820

1921
@Injectable()
2022
export class WindowsEffects {
@@ -218,9 +220,24 @@ export class WindowsEffects {
218220
{ dispatch: false }
219221
);
220222

223+
checkShareUrls$ = createEffect(
224+
() => {
225+
return this.actions$.pipe(
226+
ofType(APP_INIT_ACTION),
227+
switchMap(() => {
228+
// check for shared links
229+
this.sharingService.checkForShareUrl();
230+
return EMPTY;
231+
})
232+
);
233+
},
234+
{ dispatch: false }
235+
);
236+
221237
constructor(
222-
private actions$: Actions,
223-
private store: Store<RootState>,
224-
private windowService: WindowService
238+
private readonly actions$: Actions,
239+
private readonly store: Store<RootState>,
240+
private readonly windowService: WindowService,
241+
private readonly sharingService: SharingService
225242
) {}
226243
}

packages/altair-app/src/app/modules/altair/services/sharing/sharing.service.ts

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,22 @@ import { AccountService } from '../account/account.service';
77
import { copyToClipboard } from '../../utils';
88
import { consumeQueryParam } from '../../utils/url';
99

10-
interface ShareDetails {
10+
// ?q=<queryId>
11+
interface SharedRemoteQuery {
12+
type: 'remote-query';
1113
queryId: string;
1214
}
15+
16+
// ?query=<query>&variables=<variables>&endpoint=<endpoint>
17+
interface SharedWindowData {
18+
type: 'window-data';
19+
endpoint: string | undefined;
20+
query: string;
21+
variables: string | undefined;
22+
}
23+
24+
type SharedUrlInfo = SharedRemoteQuery | SharedWindowData;
25+
1326
@Injectable({
1427
providedIn: 'root',
1528
})
@@ -30,43 +43,70 @@ export class SharingService {
3043
if (!shareDetails) {
3144
return;
3245
}
33-
this.accountService.observeUser().subscribe((user) => {
34-
if (!user) {
35-
return;
36-
}
37-
this.handleShareDetails(shareDetails);
38-
});
46+
47+
this.handleShareDetails(shareDetails);
3948
}
4049

4150
copyQueryShareUrl(queryId: string) {
4251
copyToClipboard(this.apiService.getQueryShareUrl(queryId));
4352
this.notifyService.info(`Copied share url to clipboard`);
4453
}
4554

46-
private getShareDetailsFromUrl(url: string) {
55+
private getShareDetailsFromUrl(url: string): SharedUrlInfo | undefined {
4756
const queryId = consumeQueryParam('q', url);
48-
if (!queryId) {
49-
// no shared link
50-
return;
57+
if (queryId) {
58+
// shared remote query
59+
return { type: 'remote-query', queryId };
60+
}
61+
const query = consumeQueryParam('query', url);
62+
if (query) {
63+
// shared window data
64+
return {
65+
type: 'window-data',
66+
query,
67+
variables: consumeQueryParam('variables', url),
68+
endpoint: consumeQueryParam('endpoint', url),
69+
};
5170
}
5271

53-
return { queryId };
72+
return;
5473
}
5574

56-
private async handleShareDetails(shareDetails: ShareDetails) {
75+
private async handleShareDetails(shareDetails: SharedUrlInfo) {
5776
try {
58-
const res = await this.apiService.getQuery(shareDetails.queryId);
59-
if (!res) {
60-
throw new Error('Query not found');
77+
switch (shareDetails.type) {
78+
case 'window-data': {
79+
const { query, variables, endpoint } = shareDetails;
80+
return this.windowService.importWindowData({
81+
version: 1,
82+
type: 'window',
83+
query,
84+
variables: variables ?? '{}',
85+
apiUrl: endpoint ?? '',
86+
windowName: 'From url',
87+
headers: [],
88+
subscriptionUrl: '',
89+
});
90+
}
91+
case 'remote-query': {
92+
const user = await this.accountService.getUser();
93+
if (!user) {
94+
return;
95+
}
96+
const res = await this.apiService.getQuery(shareDetails.queryId);
97+
if (!res) {
98+
throw new Error('Query not found');
99+
}
100+
return this.windowService.loadQueryFromCollection(
101+
res.query,
102+
res.collectionId,
103+
res.query.id ?? shareDetails.queryId
104+
);
105+
}
61106
}
62-
await this.windowService.loadQueryFromCollection(
63-
res.query,
64-
res.collectionId,
65-
res.query.id ?? shareDetails.queryId
66-
);
67107
} catch (err) {
68108
debug.error(err);
69-
this.notifyService.error(`Error loading shared query`);
109+
this.notifyService.error(`Error loading shared details`);
70110
}
71111
}
72112
}

packages/altair-docs/.vitepress/config.mts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { defineConfig } from 'vitepress';
22
import { getConfig } from './plugins/sidebar-generation';
33
import coreApiSidebar from '../api/core/typedoc-sidebar.json';
44
import { dynamicFiles } from './plugins/dynamic-files';
5+
import { toString } from 'hast-util-to-string';
6+
import { openInAltairShikiPlugin } from './plugins/open-in-altair-shiki';
57

68
const { sidebar: retrievedSidebar } = getConfig({
79
filter: (meta) => meta.sidebar !== false,
@@ -139,4 +141,7 @@ export default defineConfig({
139141
vite: {
140142
plugins: [dynamicFiles()],
141143
},
144+
markdown: {
145+
codeTransformers: [openInAltairShikiPlugin()],
146+
},
142147
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { toString } from 'hast-util-to-string';
2+
import { ShikijiTransformer } from 'shikiji';
3+
export function openInAltairShikiPlugin(): ShikijiTransformer {
4+
return {
5+
code(node) {
6+
if (this.options.lang === 'graphql') {
7+
node.children.push({
8+
type: 'element',
9+
tagName: 'button',
10+
properties: {
11+
type: 'button',
12+
data: toString(node),
13+
title: 'Open in Altair Web',
14+
'aria-label': 'Open in Altair Web',
15+
class: 'open-in-altair-btn',
16+
'data-name': 'open-in-altair-button',
17+
onclick: /* javascript */ `
18+
const url = new window.URL('https://web.altairgraphql.dev/');
19+
const params = new URLSearchParams();
20+
params.set('query', this.attributes.data.value);
21+
params.set('variables', JSON.stringify({}));
22+
url.search = params.toString();
23+
24+
window.open(url.toString(), '_blank');
25+
`.trim(),
26+
},
27+
children: [
28+
{
29+
type: 'text',
30+
value: 'Open in Altair Web',
31+
},
32+
],
33+
});
34+
}
35+
},
36+
};
37+
}

packages/altair-docs/.vitepress/theme/custom.css

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,24 @@ p {
4545
.medium-zoom-image--opened {
4646
z-index: 31;
4747
}
48+
49+
.open-in-altair-btn {
50+
position: absolute;
51+
bottom: 10px;
52+
right: 10px;
53+
font-size: 12px;
54+
font-weight: 500;
55+
font-family: var(--vp-font-family-base);
56+
z-index: var(--vp-z-index-local-nav);
57+
background-color: var(--vp-code-copy-code-bg);
58+
color: var(--vp-code-copy-code-active-text);
59+
border: 1px solid var(--vp-code-copy-code-border-color);
60+
color: white;
61+
padding: 5px 10px;
62+
border-radius: 5px;
63+
transition: all 0.3s ease;
64+
}
65+
.open-in-altair-btn:hover {
66+
background-color: var(--vp-code-copy-code-hover-bg);
67+
border: 1px solid var(--vp-code-copy-code-hover-border-color);
68+
}

packages/altair-docs/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@types/lodash.sortby": "^4.7.9",
1616
"altair-graphql-core": "workspace:*",
1717
"glob": "^11.0.0",
18+
"hast-util-to-string": "^3.0.1",
1819
"inflection": "^3.0.0",
1920
"lodash.escaperegexp": "^4.1.2",
2021
"lodash.sortby": "^4.7.0",
@@ -23,6 +24,7 @@
2324
"markdown-it-title": "^4.0.0",
2425
"node-cache": "^5.1.2",
2526
"parent-module": "^3.1.0",
27+
"shikiji": "^0.10.2",
2628
"transliteration": "^2.3.5",
2729
"vitepress": "^1.0.0-rc.39",
2830
"vitepress-plugin-google-analytics": "^1.0.2",

pnpm-lock.yaml

Lines changed: 31 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)