Skip to content
Snippets Groups Projects
Commit 5c14687f authored by Tom Robinson's avatar Tom Robinson
Browse files

Port Entity*Loader to entity-specific versions, e.x. Collection.ListLoader

parent 117de4b7
No related branches found
No related tags found
No related merge requests found
Showing
with 77 additions and 259 deletions
......@@ -16,14 +16,13 @@ import FormMessage from "metabase/components/form/FormMessage";
import CreatedDatabaseModal from "../components/CreatedDatabaseModal.jsx";
import DeleteDatabaseModal from "../components/DeleteDatabaseModal.jsx";
import Databases from "metabase/entities/databases";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
import Database from "metabase/entities/databases";
import { getDeletes, getDeletionError } from "../selectors";
import { deleteDatabase, addSampleDataset } from "../database";
const mapStateToProps = (state, props) => ({
hasSampleDataset: Databases.selectors.getHasSampleDataset(state),
hasSampleDataset: Database.selectors.getHasSampleDataset(state),
created: props.location.query.created,
engines: MetabaseSettings.get("engines"),
......@@ -33,14 +32,13 @@ const mapStateToProps = (state, props) => ({
});
const mapDispatchToProps = {
fetchDatabases: Databases.actions.fetchList,
// NOTE: still uses deleteDatabase from metabaseadmin/databases/databases.js
// rather than metabase/entities/databases since it updates deletes/deletionError
deleteDatabase: deleteDatabase,
addSampleDataset: addSampleDataset,
};
@entityListLoader({ entityType: "databases" })
@Database.loadList()
@connect(mapStateToProps, mapDispatchToProps)
export default class DatabaseList extends Component {
static propTypes = {
......
......@@ -3,14 +3,13 @@ import { t } from "c-3po";
import { connect } from "react-redux";
import { goBack } from "react-router-redux";
import { entityObjectLoader } from "metabase/entities/containers/EntityObjectLoader";
import Task from "metabase/entities/tasks";
import Code from "metabase/components/Code";
import ModalContent from "metabase/components/ModalContent";
@entityObjectLoader({
entityType: "tasks",
entityId: (state, props) => props.params.taskId,
@Task.load({
id: (state, props) => props.params.taskId,
})
@connect(null, { goBack })
class TaskModal extends React.Component {
......
......@@ -2,24 +2,17 @@ import React from "react";
import { t } from "c-3po";
import { Box, Flex } from "grid-styled";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
import Task from "metabase/entities/tasks";
import AdminHeader from "metabase/components/AdminHeader";
import Icon, { IconWrapper } from "metabase/components/Icon";
import Link from "metabase/components/Link";
import Tooltip from "metabase/components/Tooltip";
@entityListLoader({
entityType: "tasks",
@Task.loadList({
pageSize: 50,
})
class TasksApp extends React.Component {
constructor(props) {
super(props);
this.state = {
offset: this.props.entityQuery.offset,
};
}
render() {
const {
tasks,
......
......@@ -3,10 +3,10 @@ import { connect } from "react-redux";
import { goBack } from "react-router-redux";
import CollectionForm from "metabase/containers/CollectionForm";
import Collections from "metabase/entities/collections";
import Collection from "metabase/entities/collections";
const mapStateToProps = (state, props) => ({
initialCollectionId: Collections.selectors.getInitialCollectionId(
initialCollectionId: Collection.selectors.getInitialCollectionId(
state,
props,
),
......
......@@ -10,18 +10,16 @@ import ModalContent from "metabase/components/ModalContent.jsx";
import * as Urls from "metabase/lib/urls";
import Collections from "metabase/entities/collections";
import { entityObjectLoader } from "metabase/entities/containers/EntityObjectLoader";
import Collection from "metabase/entities/collections";
const mapDispatchToProps = {
setCollectionArchived: Collections.actions.setArchived,
setCollectionArchived: Collection.actions.setArchived,
push,
};
@connect(null, mapDispatchToProps)
@entityObjectLoader({
entityType: "collections",
entityId: (state, props) => props.params.collectionId,
@Collection.load({
id: (state, props) => props.params.collectionId,
})
@withRouter
class ArchiveCollectionModal extends React.Component {
......
......@@ -4,9 +4,11 @@ import { t } from "c-3po";
import BrowserCrumbs from "metabase/components/BrowserCrumbs";
import { connect } from "react-redux";
import Database from "metabase/entities/databases";
import Schema from "metabase/entities/schemas";
import Table from "metabase/entities/tables";
import EntityItem from "metabase/components/EntityItem";
import EntityListLoader from "metabase/entities/containers/EntityListLoader";
import EntityObjectLoader from "metabase/entities/containers/EntityObjectLoader";
import { normal } from "metabase/lib/colors";
import Question from "metabase-lib/lib/Question";
......@@ -48,43 +50,16 @@ function getDefaultQuestionForTable(table) {
});
}
export const DatabaseListLoader = props => (
<EntityListLoader entityType="databases" {...props} />
);
const PAGE_PADDING = [1, 2, 4];
const ITEM_WIDTHS = [1, 1 / 2, 1 / 3];
const ANALYTICS_CONTEXT = "Data Browse";
const SchemaListLoader = ({ dbId, ...props }) => (
<EntityListLoader entityType="schemas" entityQuery={{ dbId }} {...props} />
);
const TableListLoader = ({ dbId, schemaName, ...props }) => (
<EntityListLoader
entityType="tables"
entityQuery={{ dbId, schemaName }}
{...props}
/>
);
const DatabaseName = ({ dbId }) => (
<EntityObjectLoader
entityType="databases"
entityId={dbId}
properties={["name"]}
loadingAndErrorWrapper={false}
>
{({ object }) => (object ? <span>{object.name}</span> : null)}
</EntityObjectLoader>
);
export class SchemaBrowser extends React.Component {
render() {
const { dbId } = this.props.params;
return (
<Box>
<SchemaListLoader dbId={dbId}>
<Schema.ListLoader query={{ dbId }}>
{({ schemas }) =>
schemas.length > 1 ? (
<Box>
......@@ -93,7 +68,7 @@ export class SchemaBrowser extends React.Component {
analyticsContext={ANALYTICS_CONTEXT}
crumbs={[
{ title: t`Our data`, to: "browse" },
{ title: <DatabaseName dbId={dbId} /> },
{ title: <Database.Name id={dbId} /> },
]}
/>
</Box>
......@@ -132,7 +107,7 @@ export class SchemaBrowser extends React.Component {
<TableBrowser {...this.props} />
)
}
</SchemaListLoader>
</Schema.ListLoader>
</Box>
);
}
......@@ -146,7 +121,7 @@ export class TableBrowser extends React.Component {
const { dbId, schemaName } = this.props.params;
return (
<Box>
<TableListLoader dbId={dbId} schemaName={schemaName}>
<Table.ListLoader query={{ dbId, schemaName }}>
{({ tables, loading, error }) => {
return (
<Box>
......@@ -156,7 +131,7 @@ export class TableBrowser extends React.Component {
crumbs={[
{ title: t`Our data`, to: "browse" },
{
title: <DatabaseName dbId={dbId} />,
title: <Database.Name id={dbId} />,
to: `browse/${dbId}`,
},
schemaName != null && { title: schemaName },
......@@ -229,7 +204,7 @@ export class TableBrowser extends React.Component {
</Box>
);
}}
</TableListLoader>
</Table.ListLoader>
</Box>
);
}
......@@ -251,7 +226,7 @@ export class DatabaseBrowser extends React.Component {
analyticsContext={ANALYTICS_CONTEXT}
/>
</Box>
<DatabaseListLoader>
<Database.ListLoader>
{({ databases, loading, error }) => {
return (
<Grid>
......@@ -271,7 +246,7 @@ export class DatabaseBrowser extends React.Component {
</Grid>
);
}}
</DatabaseListLoader>
</Database.ListLoader>
</Box>
);
}
......
......@@ -9,6 +9,9 @@ import { dissoc } from "icepick";
import withToast from "metabase/hoc/Toast";
import Collection from "metabase/entities/collections";
import Search from "metabase/entities/search";
import listSelect from "metabase/hoc/ListSelect";
import BulkActionBar from "metabase/components/BulkActionBar";
......@@ -33,7 +36,6 @@ import Tooltip from "metabase/components/Tooltip";
import CollectionMoveModal from "metabase/containers/CollectionMoveModal";
import EntityCopyModal from "metabase/entities/containers/EntityCopyModal";
import { entityObjectLoader } from "metabase/entities/containers/EntityObjectLoader";
import { entityTypeForObject } from "metabase/schema";
import CollectionList from "metabase/components/CollectionList";
......@@ -93,11 +95,8 @@ const EMPTY_STATES = {
card: <QuestionEmptyState />,
};
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
@entityListLoader({
entityType: "search",
entityQuery: (state, props) => ({ collection: props.collectionId }),
@Search.loadList({
query: (state, props) => ({ collection: props.collectionId }),
wrapped: true,
})
@connect((state, props) => {
......@@ -648,9 +647,8 @@ const SelectionControls = ({
<StackedCheckBox checked indeterminate onChange={onSelectAll} size={size} />
);
@entityObjectLoader({
entityType: "collections",
entityId: (state, props) => props.params.collectionId,
@Collection.load({
id: (state, props) => props.params.collectionId,
reload: true,
})
class CollectionLanding extends React.Component {
......
/* @flow */
import React from "react";
import EntityObjectLoader from "metabase/entities/containers/EntityObjectLoader";
import EntityListLoader from "metabase/entities/containers/EntityListLoader";
import Collection from "metabase/entities/collections";
import Search from "metabase/entities/search";
type Props = {
collectionId: number,
......@@ -9,15 +10,13 @@ type Props = {
};
const CollectionItemsLoader = ({ collectionId, children, ...props }: Props) => (
<EntityObjectLoader
<Collection.Loader
{...props}
entityType="collections"
entityId={collectionId}
id={collectionId}
children={({ object }) => (
<EntityListLoader
<Search.ListLoader
{...props}
entityType="search"
entityQuery={{ collection: collectionId }}
query={{ collection: collectionId }}
wrapped
children={({ list }) =>
object &&
......
/* @flow */
import React from "react";
import EntityListLoader from "metabase/entities/containers/EntityListLoader";
type Props = {
writable: boolean, // inherited from old CollectionList component
children: () => void,
};
const CollectionListLoader = ({ children, writable, ...props }: Props) => (
<EntityListLoader
entityType="collections"
{...props}
children={({ list, collections, ...props }) =>
children({
list: writable ? list && list.filter(c => c.can_write) : list,
collections: writable
? collections && collections.filter(c => c.can_write)
: collections,
...props,
})
}
/>
);
export default CollectionListLoader;
import React from "react";
import { entityObjectLoader } from "metabase/entities/containers/EntityObjectLoader";
import { ROOT_COLLECTION } from "metabase/entities/collections";
const CollectionNameLoader = entityObjectLoader({
entityType: "collections",
properties: ["name"],
loadingAndErrorWrapper: false,
})(({ object }) => <span>{object && object.name}</span>);
import Collection, { ROOT_COLLECTION } from "metabase/entities/collections";
const CollectionName = ({ collectionId }) => {
if (collectionId === undefined || isNaN(collectionId)) {
......@@ -15,7 +8,7 @@ const CollectionName = ({ collectionId }) => {
} else if (collectionId === "root" || collectionId === null) {
return <span>{ROOT_COLLECTION.name}</span>;
} else {
return <CollectionNameLoader entityId={collectionId} />;
return <Collection.Name id={collectionId} />;
}
};
......
......@@ -5,15 +5,17 @@ import cx from "classnames";
import { Flex, Box } from "grid-styled";
import Icon from "metabase/components/Icon";
import Breadcrumbs from "metabase/components/Breadcrumbs";
import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
import colors from "metabase/lib/colors";
import { connect } from "react-redux";
// NOTE: replacing these with Collections.ListLoader etc currently fails due to circular dependency
import EntityListLoader, {
entityListLoader,
} from "metabase/entities/containers/EntityListLoader";
import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
import Collections from "metabase/entities/collections";
const COLLECTION_ICON_COLOR = colors["text-light"];
......
......@@ -3,15 +3,14 @@ import _ from "underscore";
import { Box, Flex } from "grid-styled";
import { connect } from "react-redux";
import { t, jt } from "c-3po";
import { createSelector } from "reselect";
import CollectionItemsLoader from "metabase/containers/CollectionItemsLoader";
import CandidateListLoader from "metabase/containers/CandidateListLoader";
import { DatabaseListLoader } from "metabase/components/BrowseApp";
import ExplorePane from "metabase/components/ExplorePane";
import Tooltip from "metabase/components/Tooltip.jsx";
import * as Urls from "metabase/lib/urls";
import colors, { normal } from "metabase/lib/colors";
import MetabotLogo from "metabase/components/MetabotLogo";
import CollectionList from "metabase/components/CollectionList";
import Card from "metabase/components/Card";
import { Grid, GridItem } from "metabase/components/Grid";
......@@ -20,22 +19,19 @@ import Link from "metabase/components/Link";
import Subhead from "metabase/components/Subhead";
import RetinaImage from "react-retina-image";
import { getUser } from "metabase/home/selectors";
import CollectionList from "metabase/components/CollectionList";
import * as Urls from "metabase/lib/urls";
import colors, { normal } from "metabase/lib/colors";
import Greeting from "metabase/lib/greeting";
import Database from "metabase/entities/databases";
import Search from "metabase/entities/search";
import { ROOT_COLLECTION } from "metabase/entities/collections";
import MetabotLogo from "metabase/components/MetabotLogo";
import Greeting from "metabase/lib/greeting";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
import { getUser } from "metabase/home/selectors";
import { getXraysEnabled } from "metabase/selectors/settings";
const PAGE_PADDING = [1, 2, 4];
import { createSelector } from "reselect";
import { getXraysEnabled } from "metabase/selectors/settings";
// use reselect select to avoid re-render if list doesn't change
const getParitionedCollections = createSelector(
[(state, props) => props.list],
......@@ -60,9 +56,8 @@ const getParitionedCollections = createSelector(
);
//class Overworld extends Zelda
@entityListLoader({
entityType: "search",
entityQuery: { collection: "root" },
@Search.loadList({
query: { collection: "root" },
wrapped: true,
})
@connect((state, props) => ({
......@@ -214,7 +209,7 @@ class Overworld extends React.Component {
</Box>
</Box>
<DatabaseListLoader>
<Database.ListLoader>
{({ databases }) => {
if (databases.length === 0) {
return null;
......@@ -270,7 +265,7 @@ class Overworld extends React.Component {
</Box>
);
}}
</DatabaseListLoader>
</Database.ListLoader>
</Box>
);
}
......
import React from "react";
import { entityObjectLoader } from "metabase/entities/containers/EntityObjectLoader";
import Question from "metabase/entities/questions";
const QuestionNameLoader = entityObjectLoader({
entityType: "question",
properties: ["name"],
loadingAndErrorWrapper: false,
})(({ object }) => <span>{object && object.name}</span>);
// TODO: remove this in favor of using Question.Name directly
const QuestionName = ({ questionId }) => {
if (questionId == undefined || isNaN(questionId)) {
return null;
} else {
return <QuestionNameLoader entityId={questionId} />;
}
};
const QuestionName = ({ questionId }) => <Question.Name id={questionId} />;
export default QuestionName;
......@@ -10,17 +10,12 @@ import { Grid, GridItem } from "metabase/components/Grid";
import Link from "metabase/components/Link";
import BrowserCrumbs from "metabase/components/BrowserCrumbs";
import EntityListLoader from "metabase/entities/containers/EntityListLoader";
import User from "metabase/entities/users";
import {
ROOT_COLLECTION,
PERSONAL_COLLECTIONS,
} from "metabase/entities/collections";
const UserListLoader = ({ children, ...props }) => (
<EntityListLoader entityType="users" children={children} {...props} />
);
const UserCollectionList = () => (
<Box px={4}>
<Box py={2}>
......@@ -31,7 +26,7 @@ const UserCollectionList = () => (
]}
/>
</Box>
<UserListLoader>
<User.ListLoader>
{({ list }) => {
return (
<Box>
......@@ -64,7 +59,7 @@ const UserCollectionList = () => (
</Box>
);
}}
</UserListLoader>
</User.ListLoader>
</Box>
);
......
......@@ -12,7 +12,7 @@ import HeaderWithBack from "metabase/components/HeaderWithBack";
import StackedCheckBox from "metabase/components/StackedCheckBox";
import VirtualizedList from "metabase/components/VirtualizedList";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
import Search from "metabase/entities/search";
import listSelect from "metabase/hoc/ListSelect";
import { getUserIsAdmin } from "metabase/selectors/user";
......@@ -23,9 +23,8 @@ const mapStateToProps = (state, props) => ({
const ROW_HEIGHT = 68;
@entityListLoader({
entityType: "search",
entityQuery: { archived: true },
@Search.loadList({
query: { archived: true },
reload: true,
wrapped: true,
})
......
......@@ -6,7 +6,7 @@ import Link from "metabase/components/Link";
import { Box, Flex } from "grid-styled";
import EntityListLoader from "metabase/entities/containers/EntityListLoader";
import Search from "metabase/entities/search";
import Card from "metabase/components/Card";
import EmptyState from "metabase/components/EmptyState";
......@@ -37,11 +37,7 @@ export default class SearchApp extends React.Component {
icon: "all",
})}
/>
<EntityListLoader
entityType="search"
entityQuery={location.query}
wrapped
>
<Search.ListLoader query={location.query} wrapped>
{({ list }) => {
if (list.length === 0) {
return (
......@@ -162,7 +158,7 @@ export default class SearchApp extends React.Component {
</Box>
);
}}
</EntityListLoader>
</Search.ListLoader>
</Box>
</Box>
);
......
......@@ -28,7 +28,7 @@ import CreateDashboardModal from "metabase/components/CreateDashboardModal";
import ProfileLink from "metabase/nav/components/ProfileLink.jsx";
import { getPath, getContext, getUser } from "../selectors";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
import Database from "metabase/entities/databases";
const mapStateToProps = (state, props) => ({
path: getPath(state, props),
......@@ -147,8 +147,7 @@ class SearchBar extends React.Component {
const MODAL_NEW_DASHBOARD = "MODAL_NEW_DASHBOARD";
@entityListLoader({
entityType: "databases",
@Database.loadList({
// set this to false to prevent a potential spinner on the main nav
loadingAndErrorWrapper: false,
})
......
import React from "react";
import { Box, Flex } from "grid-styled";
import cx from "classnames";
import { withRouter } from "react-router";
import { PulseApi } from "metabase/services";
import Button from "metabase/components/Button";
import Icon from "metabase/components/Icon";
import CollectionListLoader from "metabase/containers/CollectionListLoader";
import colors from "metabase/lib/colors";
@withRouter
class PulseMoveModal extends React.Component {
state = {
// will eventually be the collection object representing the selected collection
// we store the whole object instead of just the ID so that we can use its
// name in the action button, and other properties
selectedCollection: {},
// whether the move action has started
moving: false,
error: null,
};
async _movePulse() {
try {
await PulseApi.update({
id: this.props.params.pulseId,
collection_id: this.state.selectedCollection.id,
});
this.props.onClose();
} catch (error) {
this.setState({ error, moving: false });
}
}
render() {
const { selectedCollection } = this.state;
return (
<Box p={3}>
Move to...
<CollectionListLoader>
{({ collections, loading, error }) => {
if (loading) {
return <Box>Loading...</Box>;
}
return (
<Box>
{collections.map(collection => (
<Box
my={1}
p={1}
onClick={() =>
this.setState({ selectedCollection: collection })
}
className={cx(
"bg-brand-hover text-white-hover cursor-pointer rounded",
{
"bg-brand text-white":
selectedCollection.id === collection.id,
},
)}
>
<Flex align="center">
<Icon name="all" color={colors["text-light"]} size={32} />
<h4 className="ml1">{collection.name}</h4>
</Flex>
</Box>
))}
</Box>
);
}}
</CollectionListLoader>
<Flex>
<Button primary className="ml-auto" onClick={() => this._movePulse()}>
Move
</Button>
</Flex>
</Box>
);
}
}
export default PulseMoveModal;
......@@ -5,10 +5,10 @@ import { connect } from "react-redux";
import title from "metabase/hoc/Title";
import PulseEdit from "../components/PulseEdit.jsx";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
import Collections from "metabase/entities/collections";
import Pulses from "metabase/entities/pulses";
import User from "metabase/entities/users";
import { push, goBack } from "react-router-redux";
......@@ -53,7 +53,7 @@ const mapDispatchToProps = {
goBack,
};
@entityListLoader({ entityType: "users" })
@User.loadList()
@connect(mapStateToProps, mapDispatchToProps)
@title(({ pulse }) => pulse && pulse.name)
export default class PulseEditApp extends Component {
......
......@@ -16,7 +16,7 @@ import ButtonWithStatus from "metabase/components/ButtonWithStatus";
import PulseEditChannels from "metabase/pulse/components/PulseEditChannels";
import RetinaImage from "react-retina-image";
import { entityListLoader } from "metabase/entities/containers/EntityListLoader";
import User from "metabase/entities/users";
// actions
import { createAlert, deleteAlert, updateAlert } from "metabase/alert/alert";
......@@ -574,7 +574,7 @@ export class AlertEditSchedule extends Component {
}
}
@entityListLoader({ entityType: "users" })
@User.loadList()
@connect(
(state, props) => ({
user: getUser(state),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment