Skip to content

Commit

Permalink
make FileTab a thin wrapper around a new useFile hook
Browse files Browse the repository at this point in the history
  • Loading branch information
SillyFreak committed May 10, 2020
1 parent aa4150e commit e94e364
Showing 1 changed file with 57 additions and 53 deletions.
110 changes: 57 additions & 53 deletions src/components/ide/FileTab/FileTab.js
Expand Up @@ -6,66 +6,70 @@ import { fs } from 'filer';

import { Project } from '../../../core/store/projects';

type PropTypes = {|
type Content = string;
type MaybeContent = Content | null;
type ContentSetter = (content: Content) => void | Promise<void>;

function useFile(
project: Project,
path: string,
children: (
content: string | null,
onContentChange: (content: string) => void | Promise<void>,
) => React.Node,
|};
type StateTypes = {|
content: string | null,
|};

class FileTab extends React.Component<PropTypes, StateTypes> {
state = {
content: null,
): [MaybeContent, ContentSetter] {
const [content, setContentImpl] = React.useState<MaybeContent>(null);

// reload file contents when the project or file changes
React.useEffect(() => {
// load the file's contents
(async () => {
const absolutePath = project.resolve(path);

const newContent = await fs.promises.readFile(absolutePath, 'utf8');
setContentImpl(newContent);
})();

// after changing the file, set the content to null to prevent further use
// of the old content
return () => {
setContentImpl(null);
};
}, [project, path]);

// save the file when content changed
// TODO test that the setContentImpl(null) when changing file is dispatched
// before this effect, so that content can not be erroneously written to the
// wrong file
React.useEffect(() => {
(async () => {
// if the content was not loaded yet for whatever reason, don't delete
// existing file contents.
if (content === null) return;

const absolutePath = project.resolve(path);
await fs.promises.writeFile(absolutePath, content, 'utf8');
})();
}, [project, path, content]);

const setContent = (newContent: Content) => {
// only set content if the current content is not null,
// i.e. the original file contents were loaded successfully
if (content !== null) setContentImpl(newContent);
};

componentDidMount() {
this.refreshFile();
}
return [content, setContent];
}

async refreshFile() {
const { project, path } = this.props;
const absolutePath = project.resolve(path);
type Props = {|
project: Project,
path: string,
children: (
content: MaybeContent,
onContentChange: ContentSetter,
) => React.Node,
|};

const content = await fs.promises.readFile(absolutePath, 'utf8');
this.setState({ content });
}
function FileTab({ project, path, children }: Props) {
const [content, setContent] = useFile(project, path);

setContent(content: string) {
// only set content if the current content is not null,
// i.e. the original file contents were loaded successfully,
// and subsequently persist that content
this.setState(
({ content: oldContent }) => (oldContent === null ? {} : { content }),
() => this.save(),
);
}

async save() {
const { project, path } = this.props;
const { content } = this.state;

// if the content was not loaded yet for whatever reason,
// don't delete existing file contents.
if (content === null) return;

const absolutePath = project.resolve(path);

await fs.promises.writeFile(absolutePath, this.state.content, 'utf8');
}

render() {
const { children } = this.props;
const { content } = this.state;

return children(content, (newContent: string) => {
this.setContent(newContent);
});
}
return children(content, setContent);
}

export default FileTab;

0 comments on commit e94e364

Please sign in to comment.