Skip to content

Commit

Permalink
Temporary realm paths (#182)
Browse files Browse the repository at this point in the history
* Adding fs-extra

* Changing CWDs

* Renamed AdminRealmLoadingComponent to a more general RealmLoadingComponent

* Satisfying prettier
  • Loading branch information
kraenhansen authored Sep 30, 2017
1 parent 55db89d commit dd7c807
Show file tree
Hide file tree
Showing 17 changed files with 251 additions and 148 deletions.
16 changes: 9 additions & 7 deletions package-lock.json

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

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,25 @@
},
"dependencies": {
"classnames": "^2.2.5",
"electron-updater": "^2.10.1",
"faker": "^4.1.0",
"font-awesome": "^4.7.0",
"fs-extra": "^4.0.2",
"lodash": "^4.17.4",
"moment": "^2.18.1",
"electron-updater": "^2.10.1",
"react": "^15.6.1",
"react-addons-css-transition-group": "^15.6.2",
"react-addons-transition-group": "^15.6.2",
"react-dom": "^15.6.1",
"react-draggable": "^3.0.3",
"react-virtualized": "^9.9.0",
"lodash": "^4.17.4",
"reactstrap": "^4.8.0",
"realm": "2.0.0-rc14"
},
"devDependencies": {
"@types/classnames": "^2.2.3",
"@types/faker": "^4.1.1",
"@types/fs-extra": "^4.0.2",
"@types/node": "^8.0.31",
"@types/react": "^16.0.7",
"@types/react-dom": "^15.5.5",
Expand Down
12 changes: 11 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { dialog } from 'electron';
import { app, dialog } from 'electron';
import * as fs from 'fs-extra';
import * as path from 'path';

import Application from './main/application';

// TODO: Submit these to a service like opbeat instead.
process.on('uncaughtException', error => {
dialog.showErrorBox('Uncaught exception', `${error.message}: ${error.stack}`);
});

// Create a directory for the renderer processes to create directories in
const userDataPath = app.getPath('userData');
const processDir = path.resolve(userDataPath, 'realm-studio');
if (!fs.existsSync(processDir)) {
fs.mkdir(processDir);
}

const isProduction = process.env.NODE_ENV === 'production';

// Make node understand the source-maps emitted from WebPack.
Expand Down
19 changes: 18 additions & 1 deletion src/renderer.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import * as electron from 'electron';
import * as fs from 'fs-extra';
import * as path from 'path';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

const isProduction = process.env.NODE_ENV === 'production';

// Create and change working directory to aviod conflicts of opening two realms twice
// FIXME: see https://github.com/realm/realm-js/issues/818
// This needs to happen before realm is loaded

// Generating a path for this process
const userDataPath = electron.remote.app.getPath('userData');
process.chdir(userDataPath);
const processDir = path.resolve(
userDataPath,
'realm-studio',
process.pid.toString(),
);
// Create the directory
fs.mkdirSync(processDir);
// Change to it
process.chdir(processDir);
// Make sure directory is removed when process / window is closed
process.on('exit', () => {
fs.removeSync(processDir);
});

// Make sync only report errors
import * as Realm from 'realm';
Expand Down
24 changes: 18 additions & 6 deletions src/services/ros.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { remote as electron } from 'electron';
import * as path from 'path';
import * as Realm from 'realm';

export interface IUser {
Expand Down Expand Up @@ -42,15 +44,15 @@ export enum AccessLevel {

// These schemas are copied from ROS

const getRealmUrl = (user: Realm.Sync.User, path: string) => {
const url = new URL(path, user.server);
const getRealmUrl = (user: Realm.Sync.User, realmPath: string) => {
const url = new URL(realmPath, user.server);
url.protocol = 'realm:';
return url.toString();
};

export const timeoutPromise = (
url: string,
delay: number = 2000,
delay: number = 30000,
): Promise<Realm> => {
return new Promise((resolve, reject) => {
setTimeout(() => {
Expand All @@ -59,15 +61,25 @@ export const timeoutPromise = (
});
};

export const getAdminRealm = (user: Realm.Sync.User): Promise<Realm> => {
const url = getRealmUrl(user, '__admin');
export const getRealm = async (
user: Realm.Sync.User,
realmPath: string,
progressCallback?: Realm.Sync.ProgressNotificationCallback,
): Promise<Realm> => {
const url = getRealmUrl(user, realmPath);
const realm = Realm.open({
sync: {
url,
user,
},
});
return Promise.race<Realm>([realm, timeoutPromise(url)]);

if (progressCallback) {
realm.progress(progressCallback);
}

// Return a promise that resolves once the entire synced Realm has been downloaded
return await Promise.race<Realm>([realm, timeoutPromise(url)]);
};

export const createUser = async (
Expand Down
7 changes: 4 additions & 3 deletions src/ui/greeting/Greeting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ export const Greeting = ({
className="Greeting__Action"
onClick={onConnectToServer}
disabled={!isSyncEnabled}
title={isSyncEnabled ?
"Click to connect to Realm Object Server" :
`This feature is currently not available on ${os.type()}`
title={
isSyncEnabled
? 'Click to connect to Realm Object Server'
: `This feature is currently not available on ${os.type()}`
}
>
Connect to Realm Object Server
Expand Down
107 changes: 107 additions & 0 deletions src/ui/reusable/RealmLoadingComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as React from 'react';

import {
createUser,
getRealm,
IRealmFile,
IUser,
IUserMetadataRow,
updateUserPassword,
} from '../../services/ros';

import { showError } from './errors';
import { ILoadingProgress } from './loading-overlay';

export { ILoadingProgress };

export interface IRealmLoadingComponentProps {
user: Realm.Sync.User;
}

export interface IRealmLoadingComponentState {
progress: ILoadingProgress;
}

export abstract class RealmLoadingComponent<
P extends IRealmLoadingComponentProps,
S extends IRealmLoadingComponentState
> extends React.Component<P, S> {
protected abstract onRealmChanged: () => void;
protected abstract onRealmLoaded: () => void;

protected realm: Realm;
protected cancellations: Array<() => void> = [];

private realmPath: string;

public constructor(realmPath: string) {
super();
this.realmPath = realmPath;
}

public async componentDidMount() {
await this.initializeRealm();
}

public componentWillUnmount() {
// Remove any existing a change listeners
if (this.realm) {
this.realm.removeListener('change', this.onRealmChanged);
}
this.cancelLoadingRealms();
}

protected cancelLoadingRealms() {
// Iterate over everything that can be cancelled
this.cancellations.forEach(cancel => cancel());
}

protected async initializeRealm() {
// Remove any existing a change listeners
if (this.realm) {
this.realm.removeListener('change', this.onRealmChanged);
}

try {
this.setState({ progress: { done: false } });
// Get the realms from the ROS interface
this.realm = await this.openRealm();

// Register change listeners
this.realm.addListener('change', this.onRealmChanged);
this.onRealmLoaded();
// Update the state, to indicate we're done loading
this.setState({ progress: { done: true } });
} catch (err) {
if (!err.wasCancelled) {
showError('Failed to synchronize Realms with the server', err);
const failure = err.message || 'Failed to open the Realm';
this.setState({ progress: { failure, done: true } });
}
}
}

private openRealm(): Promise<Realm> {
const realmPromise = getRealm(
this.props.user,
this.realmPath,
this.progressChanged,
);
// Save a wrapping promise so this can be cancelled
return new Promise((resolve, reject) => {
this.cancellations.push(() => reject({ wasCancelled: true }));
realmPromise.then(resolve, reject);
});
}

private progressChanged = (transferred: number, transferable: number) => {
// console.log('progressChanged', transferred, transferable);
this.setState({
progress: {
done: transferred >= transferable,
transferred,
transferable,
},
});
};
}
6 changes: 6 additions & 0 deletions src/ui/reusable/loading-overlay/LoadingOverlay.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ $loading-overlay-background: rgba($color-charcoal, .25);
background: $loading-overlay-background;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
left: 0;
opacity: 0;
Expand Down Expand Up @@ -66,4 +67,9 @@ $loading-overlay-background: rgba($color-charcoal, .25);
&__dot-3 {
animation-delay: .66s;
}

&__Progress {
margin: $spacer;
width: 60vw;
}
}
18 changes: 17 additions & 1 deletion src/ui/reusable/loading-overlay/LoadingOverlay.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import * as React from 'react';
import { Progress } from 'reactstrap';

import { ILoadingProgress } from './index';

import './LoadingOverlay.scss';

Expand All @@ -9,15 +12,21 @@ import './LoadingOverlay.scss';
*/
export default ({
loading,
progress,
fade = true,
}: {
loading: boolean;
loading?: boolean;
progress?: ILoadingProgress;
fade?: boolean;
}) => {
const classNames = ['LoadingOverlay'];
if (!fade) {
classNames.push('LoadingOverlay--no-fade');
}
// If a progress has been supplied, it overrides loading
if (progress) {
loading = !progress.done;
}
return loading ? (
<div className={classNames.join(' ')}>
<svg
Expand Down Expand Up @@ -52,6 +61,13 @@ export default ({
/>
</g>
</svg>
{progress && (
<Progress
className="LoadingOverlay__Progress"
value={progress.transferred}
max={progress.transferable}
/>
)}
</div>
) : null;
};
7 changes: 7 additions & 0 deletions src/ui/reusable/loading-overlay/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
export { default as LoadingOverlay } from './LoadingOverlay';

export interface ILoadingProgress {
done: boolean;
failure?: string;
transferable?: number;
transferred?: number;
}
Loading

0 comments on commit dd7c807

Please sign in to comment.