Skip to content

Commit

Permalink
Listen to changes via websockets.
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed Aug 31, 2022
1 parent 10ae834 commit 8713c4a
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 31 deletions.
2 changes: 1 addition & 1 deletion cli/Squidex.CLI/Squidex.CLI/Commands/App_OpenLibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public async Task Authors(ImportArguments arguments)

await using (var stream = new FileStream(arguments.File, FileMode.Open))
{
var importer = new AuthorImporter(session);
var importer = new AuthorImporter(session, log);

await importer.ImportAsync(stream);
}
Expand Down
54 changes: 35 additions & 19 deletions jscript/react/sample-hotels/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions jscript/react/sample-hotels/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
"graphql-ws": "^5.10.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-markdown": "^8.0.3",
Expand Down
4 changes: 3 additions & 1 deletion jscript/react/sample-hotels/src/components/HotelSite.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from 'react';
import { useParams } from 'react-router-dom';
import { Hotel } from './Hotel.js';
import { getHotel } from './../service';
import { useRefresh } from './hooks.js';

export const HotelSite = () => {
const { id } = useParams();

const [hotel, setHotel] = React.useState(undefined);
const needsUpdate = useRefresh([hotel?.id]);

React.useEffect(() => {
async function fetchData() {
Expand All @@ -20,7 +22,7 @@ export const HotelSite = () => {
}

fetchData();
}, [id]);
}, [id, needsUpdate]);

if (hotel) {
return <Hotel hotel={hotel} />
Expand Down
4 changes: 3 additions & 1 deletion jscript/react/sample-hotels/src/components/HotelsSite.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from 'react';
import { Hotel } from './Hotel.js';
import { getHotels } from './../service';
import { useRefresh } from './hooks.js';

export const HotelsSite = () => {
const [hotels, setHotels] = React.useState();
const needsUpdate = useRefresh(hotels?.map(x => x.id));

React.useEffect(() => {
async function fetchData() {
Expand All @@ -13,7 +15,7 @@ export const HotelsSite = () => {
}

fetchData();
}, []);
}, [needsUpdate]);

if (!hotels) {
return <div>Loading Hotels...</div>
Expand Down
3 changes: 2 additions & 1 deletion jscript/react/sample-hotels/src/components/Markdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { CONFIG } from './../service';

const hotelsRegex = new RegExp(`${escapeRegExp(CONFIG.url)}\\/api/content\\/${escapeRegExp(CONFIG.appName)}\\/hotels/(?<id>[a-z0-9\\-]+)`);

export const Markdown = ({ markdown, references }) => {
export const
Markdown = ({ markdown, references }) => {
return (
<ReactMarkdown children={markdown} components={{
a({ href, children }) {
Expand Down
4 changes: 3 additions & 1 deletion jscript/react/sample-hotels/src/components/PageSite.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from 'react';
import { useParams } from 'react-router-dom';
import { Page } from './Page.js';
import { getPage } from './../service';
import { useRefresh } from './hooks.js';

export const PageSite = () => {
const { slug } = useParams();

const [page, setPage] = React.useState(undefined);
const needsUpdate = useRefresh([page?.id]);

React.useEffect(() => {
setPage(undefined);
Expand All @@ -22,7 +24,7 @@ export const PageSite = () => {
}

fetchData();
}, [slug]);
}, [needsUpdate, slug]);

if (page) {
return <Page page={page} />
Expand Down
6 changes: 4 additions & 2 deletions jscript/react/sample-hotels/src/components/PostSite.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React from 'react';
import { useParams } from 'react-router-dom';
import { Post } from './Post.js';
import { getPost } from './../service';
import { getPost, getPostsIds } from './../service';
import { useRefresh } from './hooks.js';

export const PostSite = () => {
const { id } = useParams();

const [post, setPost] = React.useState(undefined);
const needsUpdate = useRefresh(getPostsIds([post.id]));

React.useEffect(() => {
async function fetchData() {
Expand All @@ -20,7 +22,7 @@ export const PostSite = () => {
}

fetchData();
}, [id]);
}, [id, needsUpdate]);

if (post) {
return <Post post={post} />
Expand Down
6 changes: 4 additions & 2 deletions jscript/react/sample-hotels/src/components/PostsSite.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from 'react';
import { Post } from './Post.js';
import { getPosts } from './../service';
import { getPosts, getPostsIds } from './../service';
import { useRefresh } from './hooks.js';

export const PostsSite = () => {
const [posts, setPosts] = React.useState();
const needsUpdate = useRefresh(getPostsIds(posts));

React.useEffect(() => {
async function fetchData() {
Expand All @@ -13,7 +15,7 @@ export const PostsSite = () => {
}

fetchData();
}, []);
}, [needsUpdate]);

if (!posts) {
return <div>Loading Posts...</div>
Expand Down
120 changes: 120 additions & 0 deletions jscript/react/sample-hotels/src/components/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React from 'react';
import { buildSubscriptionClient } from '../service';

class Subscription {
elementIds;
elementUpdater;

constructor(elementIds, action) {
this.elementIds = elementIds;
this.elementUpdater = action;
}

refresh(id) {
if (this.elementIds.indexOf(id) >= 0) {
this.elementUpdater();
console.log('Data has been reloaded');
}
}
}

const ACTIVE = window.location.search?.indexOf('preview=1') >= 0;

class RefreshListener {
subscriptions = [];

static INSTANCE = new RefreshListener(ACTIVE);

constructor(isActive) {
if (isActive) {
this.init();
}
}

add(subscription) {
this.subscriptions.push(subscription);
}

remove(subscription) {
this.subscriptions.splice(this.subscriptions.indexOf(subscription), 1);
}

async init() {
const client = await buildSubscriptionClient();

client.subscribe({
query:
`subscription {
contentChanges {
id
}
}`,
}, {
next: event => {
const id = event.data.contentChanges.id;

for (const subscription of this.subscriptions) {
subscription.refresh(id);
}
},
error: error => {
console.log(`GRAPHQL error ${error}`);
}
})
}
}

export function useRefresh(ids) {
const [updateIds, setUpdateIds] = React.useState([]);
const [updateNeeded, setUpdateNeeded] = React.useState(0);

React.useEffect(() => {
setUpdateIds(previous => {
if (isSame(previous, ids)) {
return previous;
} else {
return ids;
}
});
}, [ids]);

React.useEffect(() => {
if (!updateIds || updateIds.length === 0 || !updateIds[0]) {
return;
}

const subscription = new Subscription(updateIds, () => {
setUpdateNeeded(x => x + 1);
});

RefreshListener.INSTANCE.add(subscription);

return () => {
RefreshListener.INSTANCE.remove(subscription);
};
}, [updateIds]);

return updateNeeded;
}

function isSame(a, b) {
if (!a && !b) {
return true;
}

if (!a || !b) {
return false;
}

if (a.length !== b.length) {
return false;
}

for (let i = 0 ; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}

return true;
}
Loading

0 comments on commit 8713c4a

Please sign in to comment.