diff --git a/frontend/models/Collection.js b/frontend/models/Collection.js
index 70036fba..66c207ca 100644
--- a/frontend/models/Collection.js
+++ b/frontend/models/Collection.js
@@ -26,7 +26,9 @@ class Collection {
/* Computed */
@computed get entryUrl(): string {
- return this.type === 'atlas' ? this.documents[0].url : this.url;
+ return this.type === 'atlas' && this.documents.length > 0
+ ? this.documents[0].url
+ : this.url;
}
/* Actions */
diff --git a/frontend/scenes/Collection/Collection.js b/frontend/scenes/Collection/Collection.js
index 7a47bbdb..57184d6a 100644
--- a/frontend/scenes/Collection/Collection.js
+++ b/frontend/scenes/Collection/Collection.js
@@ -1,14 +1,20 @@
// @flow
import React from 'react';
+import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import { Redirect } from 'react-router';
+import { Link } from 'react-router-dom';
import _ from 'lodash';
+import styled from 'styled-components';
+import { newDocumentUrl } from 'utils/routeHelpers';
import CollectionsStore from 'stores/CollectionsStore';
-import CollectionStore from './CollectionStore';
import CenteredContent from 'components/CenteredContent';
import LoadingListPlaceholder from 'components/LoadingListPlaceholder';
+import Button from 'components/Button';
+import Flex from 'components/Flex';
+import HelpText from 'components/HelpText';
type Props = {
collections: CollectionsStore,
@@ -17,24 +23,66 @@ type Props = {
@observer class Collection extends React.Component {
props: Props;
- store: CollectionStore;
-
- constructor(props) {
- super(props);
- this.store = new CollectionStore();
- }
+ collection: ?Collection;
+ @observable isFetching = true;
+ @observable redirectUrl;
componentDidMount = () => {
- const { id } = this.props.match.params;
- this.store.fetchCollection(id);
+ this.fetchDocument();
};
+ fetchDocument = async () => {
+ const { collections } = this.props;
+ const { id } = this.props.match.params;
+
+ // $FlowIssue not the correct type?
+ this.collection = await collections.getById(id);
+
+ if (!this.collection) this.redirectUrl = '/404';
+
+ if (this.collection && this.collection.documents.length > 0) {
+ this.redirectUrl = this.collection.documents[0].url;
+ }
+ this.isFetching = false;
+ };
+
+ renderNewDocument() {
+ return (
+
+ Create a document
+
+ Publish your first document to start building your collection.
+
+
+
+
+
+
+
+ );
+ }
+
render() {
- return this.store.redirectUrl
- ?
- :
-
- ;
+ return (
+
+ {this.redirectUrl && }
+ {this.isFetching
+ ?
+ : this.renderNewDocument()}
+
+ );
}
}
+
+const NewDocumentContainer = styled(Flex)`
+ padding-top: 70px;
+`;
+
+const Action = styled(Flex)`
+ margin: 20px 0;
+`;
+
export default inject('collections')(Collection);
diff --git a/frontend/scenes/Collection/CollectionStore.js b/frontend/scenes/Collection/CollectionStore.js
deleted file mode 100644
index e10870e7..00000000
--- a/frontend/scenes/Collection/CollectionStore.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// @flow
-import { observable, action } from 'mobx';
-import invariant from 'invariant';
-import { client } from 'utils/ApiClient';
-import { notFoundUrl } from 'utils/routeHelpers';
-
-class CollectionStore {
- @observable redirectUrl: ?string;
- @observable isFetching = true;
-
- /* Actions */
-
- @action fetchCollection = async (id: string) => {
- this.isFetching = true;
-
- try {
- const res = await client.get('/collections.info', { id });
- invariant(res && res.data, 'Data should be available');
- const { data } = res;
-
- if (data.type === 'atlas') this.redirectUrl = data.documents[0].url;
- else throw new Error('TODO code up non-atlas collections');
- } catch (e) {
- console.log(e);
- this.redirectUrl = notFoundUrl();
- }
- this.isFetching = false;
- };
-}
-
-export default CollectionStore;
diff --git a/frontend/stores/CollectionsStore.js b/frontend/stores/CollectionsStore.js
index 4914dccc..a0c09491 100644
--- a/frontend/stores/CollectionsStore.js
+++ b/frontend/stores/CollectionsStore.js
@@ -49,8 +49,25 @@ class CollectionsStore {
}
};
- getById = (id: string): ?Collection => {
- return _.find(this.data, { id });
+ @action getById = async (id: string): Promise => {
+ let collection = _.find(this.data, { id });
+ if (!collection) {
+ try {
+ const res = await this.client.post('/collections.info', {
+ id,
+ });
+ invariant(res && res.data, 'Collection not available');
+ const { data } = res;
+ runInAction('CollectionsStore#getById', () => {
+ collection = new Collection(data);
+ this.add(collection);
+ });
+ } catch (e) {
+ // No-op
+ }
+ }
+
+ return collection;
};
@action add = (collection: Collection): void => {
diff --git a/frontend/utils/routeHelpers.js b/frontend/utils/routeHelpers.js
index 8c43c22d..6569726d 100644
--- a/frontend/utils/routeHelpers.js
+++ b/frontend/utils/routeHelpers.js
@@ -1,5 +1,6 @@
// @flow
import Document from 'models/Document';
+import Collection from 'models/Collection';
export function homeUrl(): string {
return '/dashboard';
@@ -21,6 +22,10 @@ export function documentEditUrl(doc: Document): string {
return `${doc.url}/edit`;
}
+export function newDocumentUrl(collection: Collection): string {
+ return `${collection.url}/new`;
+}
+
export function searchUrl(query?: string): string {
if (query) return `/search/${query}`;
return `/search`;
diff --git a/server/models/Collection.js b/server/models/Collection.js
index 762b3542..bf1713fc 100644
--- a/server/models/Collection.js
+++ b/server/models/Collection.js
@@ -39,19 +39,29 @@ const Collection = sequelize.define(
collection.urlId = collection.urlId || randomstring.generate(10);
},
afterCreate: async collection => {
+ const team = await collection.getTeam();
+ const collections = await team.getCollections();
+
+ // Don't auto-create for journal types, yet
if (collection.type !== 'atlas') return;
- const document = await Document.create({
- parentDocumentId: null,
- atlasId: collection.id,
- teamId: collection.teamId,
- userId: collection.creatorId,
- lastModifiedById: collection.creatorId,
- createdById: collection.creatorId,
- title: 'Introduction',
- text: '# Introduction\n\nLets get started...',
- });
- collection.documentStructure = [document.toJSON()];
+ if (collections.length === 0) {
+ // Create intro document if first collection for team
+ const document = await Document.create({
+ parentDocumentId: null,
+ atlasId: collection.id,
+ teamId: collection.teamId,
+ userId: collection.creatorId,
+ lastModifiedById: collection.creatorId,
+ createdById: collection.creatorId,
+ title: 'Introduction',
+ text: '# Introduction\n\nLets get started...',
+ });
+ collection.documentStructure = [document.toJSON()];
+ } else {
+ // Let user create first document
+ collection.documentStructure = [];
+ }
await collection.save();
},
},
@@ -65,6 +75,9 @@ Collection.associate = models => {
as: 'documents',
foreignKey: 'atlasId',
});
+ Collection.belongsTo(models.Team, {
+ as: 'team',
+ });
Collection.addScope('withRecentDocuments', {
include: [
{
diff --git a/server/models/Team.js b/server/models/Team.js
index 5841c0da..bd486117 100644
--- a/server/models/Team.js
+++ b/server/models/Team.js
@@ -1,3 +1,4 @@
+// @flow
import { DataTypes, sequelize } from '../sequelize';
import Collection from './Collection';
@@ -24,7 +25,7 @@ const Team = sequelize.define(
);
Team.associate = models => {
- Team.hasMany(models.Collection, { as: 'atlases' });
+ Team.hasMany(models.Collection, { as: 'collections' });
Team.hasMany(models.Document, { as: 'documents' });
Team.hasMany(models.User, { as: 'users' });
};