From b38a3a22009ed951db6e9566724c7b0b1fe8b0da Mon Sep 17 00:00:00 2001
From: danieldeng2 <danieldeng223@gmail.com>
Date: Wed, 19 Aug 2020 22:53:06 +0100
Subject: [PATCH] Add download and open functionality back to resource files

---
 .../molecules/CurrentDirectoryView/index.tsx  | 222 ----------------
 .../CurrentDirectoryView/style.module.scss    |   5 -
 .../molecules/QuickAccessView/index.tsx       | 238 ------------------
 .../QuickAccessView/style.module.scss         |  34 ---
 .../molecules/ResourcesFolderView/index.tsx   | 153 -----------
 .../ResourcesFolderView/style.module.scss     |   1 -
 .../molecules/SelectionView/index.tsx         |  21 +-
 .../pages/ModuleResources/index.tsx           |  88 ++++++-
 8 files changed, 96 insertions(+), 666 deletions(-)
 delete mode 100644 src/components/molecules/CurrentDirectoryView/index.tsx
 delete mode 100644 src/components/molecules/CurrentDirectoryView/style.module.scss
 delete mode 100644 src/components/molecules/QuickAccessView/index.tsx
 delete mode 100644 src/components/molecules/QuickAccessView/style.module.scss
 delete mode 100644 src/components/molecules/ResourcesFolderView/index.tsx
 delete mode 100644 src/components/molecules/ResourcesFolderView/style.module.scss

diff --git a/src/components/molecules/CurrentDirectoryView/index.tsx b/src/components/molecules/CurrentDirectoryView/index.tsx
deleted file mode 100644
index bd5696bb7..000000000
--- a/src/components/molecules/CurrentDirectoryView/index.tsx
+++ /dev/null
@@ -1,222 +0,0 @@
-import React from "react";
-import Row from "react-bootstrap/esm/Row";
-import Col from "react-bootstrap/esm/Col";
-import ResourceSectionHeader from "../ResourceSectionHeader";
-import { api, methods } from "../../../constants/routes";
-import { request } from "../../../utils/api";
-import { faSquare, faCheckSquare } from "@fortawesome/free-regular-svg-icons";
-import { faFileAlt, faFileVideo, faFilePdf, IconDefinition } from "@fortawesome/free-solid-svg-icons";
-import FileCard from "components/atoms/FileCard";
-
-// TODO: Refactor out duplication with QuickAccessView
-export interface CurrentDirectoryViewProps {
-  documentItems: {
-    title: string;
-    type: string;
-    tags: string[];
-    id: number;
-  }[];
-  moduleCode?: string;
-}
-
-type idBooleanMap = { [key: number]: boolean };
-interface MyState {
-  isSelected: idBooleanMap;
-  isHoveringOver: idBooleanMap;
-}
-
-class CurrentDirectoryView extends React.Component<CurrentDirectoryViewProps, MyState> {
-  constructor(props: CurrentDirectoryViewProps) {
-    super(props);
-    this.state = { isSelected: [], isHoveringOver: []};
-  }
-
-  componentDidMount() {
-    let isSelected: idBooleanMap = [];
-    this.props.documentItems.forEach(({ id }: { id: number }) => {
-      isSelected[id] = false;
-    });
-    this.setState({ isSelected });
-  }
-
-  isAnySelected(): boolean {
-    let items = this.props.documentItems;
-    let isSelected = this.state.isSelected;
-    for (let item in items) {
-      if (isSelected[items[item].id]) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  isAllSelected(): boolean {
-    let items = this.props.documentItems;
-    let isSelected = this.state.isSelected;
-    for (let item in items) {
-      if (!isSelected[items[item].id]) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  handleIconClick(id: number) {
-    let isSelected = JSON.parse(JSON.stringify(this.state.isSelected));
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isSelected[id] = !isSelected[id];
-    isHoveringOver[id] = false;
-    this.setState({ isSelected, isHoveringOver });
-  }
-
-  handleDownloadClick() {
-    const onSuccess = (filename: string, data: any) => {
-      data.blob().then((blob: any) => {
-        let url = URL.createObjectURL(blob);
-        let a = document.createElement("a");
-        a.href = url;
-        a.download = filename;
-        a.click();
-        a.remove();
-      });
-    };
-    // Partial application utility
-    const downloadFilename = (filename: string) => {
-      return (data: any) => {
-        return onSuccess(filename, data);
-      };
-    };
-    const onFailure = (error: { text: () => Promise<any> }) => {
-      error.text().then((errorText) => {
-        console.log(errorText);
-      });
-    };
-
-    let indices : number[] = [];
-    for (let key in this.state.isSelected) {
-      if (this.state.isSelected[key]) {
-        indices.push(parseInt(key));
-      }
-    }
-
-    if (indices.length === 1) {
-      // Only one file to download, call single file endpoint
-      let filename = this.props.documentItems.filter(document => document.id === indices[0])[0].title;
-      request(api.MATERIALS_RESOURCES_FILE(indices[0]), methods.GET, downloadFilename(filename), onFailure);
-    } else {
-      // Multiple files to download, call zipped selection endpoint
-      request(api.MATERIALS_ZIPPED_SELECTION, methods.GET, downloadFilename("materials.zip"), onFailure, {
-        ids: indices,
-        course: this.props.moduleCode,
-      });
-    }
-  }
-
-  handleSelectAllClick() {
-    let items = this.props.documentItems;
-    let isSelected = JSON.parse(JSON.stringify(this.state.isSelected));
-    let setValue = !this.isAllSelected();
-    for (let item in items) {
-      isSelected[items[item].id] = setValue;
-    }
-    this.setState({ isSelected});
-  }
-
-  handleCardClick(id: number) {
-    const onSuccess = (data: any) => {
-      data.blob().then((blob: any) => {
-        let url = URL.createObjectURL(blob);
-        let a = document.createElement("a");
-        a.target = "_blank";
-        a.href = url;
-        a.click();
-        a.remove();
-      });
-    };
-    const onFailure = (error: { text: () => Promise<any> }) => {
-      error.text().then((errorText) => {
-        console.log(errorText);
-      });
-		};
-    request(api.MATERIALS_RESOURCES_FILE(id), methods.GET, onSuccess, onFailure);
-
-    if (this.isAnySelected()) {
-      this.handleIconClick(id);
-    }
-  }
-
-  handleMouseOver(id: number) {
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isHoveringOver[id] = true;
-    this.setState({ isHoveringOver });
-  }
-
-  handleMouseOut(id: number) {
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isHoveringOver[id] = false;
-    this.setState({ isHoveringOver });
-  }
-
-  render() {
-    return (
-      <>
-        <ResourceSectionHeader
-          heading="Files"
-          showDownload={this.isAnySelected()}
-          onDownloadClick={() => this.handleDownloadClick()}
-          onSelectAllClick={() => this.handleSelectAllClick()}
-					selectAllIcon={this.isAllSelected() ? faCheckSquare : faSquare}
-					checkBoxColur={this.isAnySelected() ? "#495057" : "#dee2e6"}
-        />
-
-        <Row style={{ marginTop: "10px", marginLeft: "-10px", marginRight: "-10px" }}>
-          {this.props.documentItems.map(({ title, type, tags, id }) => {
-						let normalIcon : IconDefinition;
-						switch (type) {
-							case "pdf":
-								normalIcon = faFilePdf;
-								break;
-							case "video":
-								normalIcon = faFileVideo;
-								break;
-							default:
-								normalIcon = faFileAlt;
-								break;
-						}
-            return (<Col
-              xs={6}
-              sm={6}
-              md={6}
-              lg={4}
-              xl={3}
-              key={id}
-              style={{ marginBottom: ".5rem", marginTop: ".5rem", paddingLeft: "10px", paddingRight: "10px" }}
-            >
-              <FileCard
-                title={title}
-                type={type}
-                tags={tags}
-                icon={
-                  this.isAnySelected() || this.state.isHoveringOver[id]
-                    ? this.state.isSelected[id]
-                      ? faCheckSquare
-                      : faSquare
-                    : normalIcon
-                }
-                onClick={() => this.handleCardClick(id)}
-                onIconClick={(e) => {
-                  e.stopPropagation();
-                  this.handleIconClick(id);
-                }}
-                onMouseOver={() => this.handleMouseOver(id)}
-                onMouseOut={() => this.handleMouseOut(id)}
-              />
-            </Col>)
-})}
-        </Row>
-      </>
-    );
-  }
-}
-
-export default CurrentDirectoryView;
diff --git a/src/components/molecules/CurrentDirectoryView/style.module.scss b/src/components/molecules/CurrentDirectoryView/style.module.scss
deleted file mode 100644
index 471dfec95..000000000
--- a/src/components/molecules/CurrentDirectoryView/style.module.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-@import "assets/scss/custom";
-
-.quickAccessRow {
-  height: fit-content;
-}
diff --git a/src/components/molecules/QuickAccessView/index.tsx b/src/components/molecules/QuickAccessView/index.tsx
deleted file mode 100644
index 76bab3782..000000000
--- a/src/components/molecules/QuickAccessView/index.tsx
+++ /dev/null
@@ -1,238 +0,0 @@
-import React from "react";
-import styles from "./style.module.scss";
-
-import classNames from "classnames";
-import Row from "react-bootstrap/esm/Row";
-import Col from "react-bootstrap/esm/Col";
-import { api, methods } from "../../../constants/routes";
-import { request } from "../../../utils/api";
-import ResourceSectionHeader from "../ResourceSectionHeader";
-import FileCard from "components/atoms/FileCard";
-import { faSquare, faCheckSquare } from "@fortawesome/free-regular-svg-icons";
-import { faFileAlt, IconDefinition, faFilePdf, faFileVideo } from "@fortawesome/free-solid-svg-icons";
-
-export interface QuickAccessProps {
-  quickAccessItems: {
-    title: string;
-    type: string;
-    tags: string[];
-    id: number;
-  }[];
-  moduleCode?: string;
-}
-
-type idBooleanMap = { [key: number]: boolean };
-interface MyState {
-  isSelected: idBooleanMap;
-  isHoveringOver: idBooleanMap;
-}
-
-class QuickAccessView extends React.Component<QuickAccessProps, MyState> {
-  constructor(props: QuickAccessProps) {
-    super(props);
-    this.state = { isSelected: [], isHoveringOver: [] };
-  }
-
-  componentDidMount() {
-    let isSelected: idBooleanMap = [];
-    let isHoveringOver: idBooleanMap = [];
-    this.props.quickAccessItems.forEach(({ id }: { id: number }) => {
-      isSelected[id] = false;
-      isHoveringOver[id] = false;
-    });
-    this.setState({ isSelected, isHoveringOver });
-  }
-
-  isAnySelected(): boolean {
-    let items = this.props.quickAccessItems;
-    let isSelected = this.state.isSelected;
-    for (let item in items) {
-      if (isSelected[items[item].id]) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  isAllSelected(): boolean {
-    let items = this.props.quickAccessItems;
-    let isSelected = this.state.isSelected;
-    for (let item in items) {
-      if (!isSelected[items[item].id]) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  handleIconClick(id: number) {
-    let isSelected = JSON.parse(JSON.stringify(this.state.isSelected));
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isSelected[id] = !isSelected[id];
-    isHoveringOver[id] = false;
-    this.setState({ isSelected, isHoveringOver });
-  }
-
-  handleDownloadClick() {
-    const onSuccess = (filename: string, data: any) => {
-      // TODO: Try to navigate straight to the endpoint url instead of creating an object url
-      data.blob().then((blob: any) => {
-        let url = URL.createObjectURL(blob);
-        let a = document.createElement("a");
-        a.href = url;
-        a.download = filename;
-        a.click();
-        a.remove();
-      });
-    };
-    // Partial application utility
-    const downloadFilename = (filename: string) => {
-      return (data: any) => {
-        return onSuccess(filename, data);
-      };
-    };
-    const onFailure = (error: { text: () => Promise<any> }) => {
-      error.text().then((errorText) => {
-        console.log(errorText);
-      });
-    };
-
-    let indices : number[] = [];
-    for (let key in this.state.isSelected) {
-      if (this.state.isSelected[key]) {
-        indices.push(parseInt(key));
-      }
-    }
-
-    if (indices.length === 1) {
-      // Only one file to download, call single file endpoint
-      let filename = this.props.quickAccessItems.filter(document => document.id === indices[0])[0].title;
-      request(api.MATERIALS_RESOURCES_FILE(indices[0]), methods.GET, downloadFilename(filename), onFailure);
-    } else {
-      // Multiple files to download, call zipped selection endpoint
-      request(api.MATERIALS_ZIPPED_SELECTION, methods.GET, downloadFilename("materials.zip"), onFailure, {
-        ids: indices,
-        course: this.props.moduleCode,
-      });
-    }
-  }
-
-  handleSelectAllClick() {
-    let items = this.props.quickAccessItems;
-    let isSelected = JSON.parse(JSON.stringify(this.state.isSelected));
-    let setValue = !this.isAllSelected();
-    for (let item in items) {
-      isSelected[items[item].id] = setValue;
-    }
-    this.setState({ isSelected });
-  }
-
-  handleCardClick(id: number) {
-		if (this.isAnySelected()) {
-			this.handleIconClick(id);
-			return;
-		}
-		
-    const onSuccess = (data: any) => {
-      // TODO: Try to navigate straight to the endpoint url instead of creating an object url
-      data.blob().then((blob: any) => {
-        let url = URL.createObjectURL(blob);
-        let a = document.createElement("a");
-        a.target = "_blank";
-        a.href = url;
-        a.click();
-        a.remove();
-      });
-    };
-    const onFailure = (error: { text: () => Promise<any> }) => {
-      error.text().then((errorText) => {
-        console.log(errorText);
-      });
-		};
-    request(api.MATERIALS_RESOURCES_FILE(id), methods.GET, onSuccess, onFailure);
-  }
-
-  handleMouseOver(id: number) {
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isHoveringOver[id] = true;
-    this.setState({ isHoveringOver });
-  }
-
-  handleMouseOut(id: number) {
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isHoveringOver[id] = false;
-    this.setState({ isHoveringOver });
-  }
-
-  render() {
-    return (
-      <>
-        <ResourceSectionHeader
-          heading="Quick Access"
-          onDownloadClick={() => this.handleDownloadClick()}
-          showDownload={this.isAnySelected()}
-          onSelectAllClick={() => this.handleSelectAllClick()}
-          selectAllIcon={this.isAllSelected() ? faCheckSquare : faSquare}
-          checkBoxColur={this.isAnySelected() ? "#495057" : "#dee2e6"}
-        />
-
-        <Row
-          className={classNames(
-            "d-flex",
-            "flex-row",
-            "flex-nowrap",
-            styles.quickAccessRow
-          )}
-        >
-          {this.props.quickAccessItems.map(({ title, type, tags, id }) => {
-            let normalIcon: IconDefinition;
-            switch (type) {
-              case "pdf":
-                normalIcon = faFilePdf;
-                break;
-              case "video":
-                normalIcon = faFileVideo;
-                break;
-              default:
-                normalIcon = faFileAlt;
-                break;
-            }
-            return (
-              <Col
-                xs={7}
-                sm={5}
-                md={5}
-                lg={4}
-                xl={3}
-                key={id}
-                style={{ marginBottom: ".5rem", marginTop: ".5rem", paddingLeft: "10px", paddingRight: "10px" }}
-              >
-                <FileCard
-                  title={title}
-                  type={type}
-                  tags={tags}
-                  icon={
-                    this.isAnySelected() || this.state.isHoveringOver[id]
-                      ? this.state.isSelected[id]
-                        ? faCheckSquare
-                        : faSquare
-                      : normalIcon
-                  }
-                  onClick={() => this.handleCardClick(id)}
-                  onIconClick={(e) => {
-                    e.stopPropagation();
-                    this.handleIconClick(id);
-                  }}
-                  onMouseOver={() => this.handleMouseOver(id)}
-                  onMouseOut={() => this.handleMouseOut(id)}
-                />
-              </Col>
-            );
-          })}
-        </Row>
-      </>
-    );
-  }
-}
-
-export default QuickAccessView;
diff --git a/src/components/molecules/QuickAccessView/style.module.scss b/src/components/molecules/QuickAccessView/style.module.scss
deleted file mode 100644
index be0bbee19..000000000
--- a/src/components/molecules/QuickAccessView/style.module.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-@import "assets/scss/custom";
-
-.quickAccessRow {
-  scrollbar-width: thin;
-	scrollbar-color: $white $white;
-	margin-top: 10px;
-	overflow-y: visible;
-	overflow-x: auto;
-  margin-right: -10px;
-  margin-left: -10px;
-	// margin-left: 0; // leave space before card
-}
-
-.quickAccessRow::-webkit-scrollbar {
-	width: 1rem;
-  height: 0.5rem;
-}
-.quickAccessRow::-webkit-scrollbar-track {
-  background: $white;
-	margin-left: 10px; 
-	margin-right: 10px; 
-}
-.quickAccessRow::-webkit-scrollbar-thumb {
-  background-color: $white;
-  border-radius: .5rem;
-}
-
-.quickAccessRow:hover {
-	scrollbar-color: $gray-400 $white;
-}
-
-.quickAccessRow:hover::-webkit-scrollbar-thumb {
-	background-color: $gray-400;
-}
diff --git a/src/components/molecules/ResourcesFolderView/index.tsx b/src/components/molecules/ResourcesFolderView/index.tsx
deleted file mode 100644
index 4184388fb..000000000
--- a/src/components/molecules/ResourcesFolderView/index.tsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import React from "react";
-import Row from "react-bootstrap/esm/Row";
-import Col from "react-bootstrap/esm/Col";
-import ResourceSectionHeader from "../ResourceSectionHeader";
-import FolderCard from "components/atoms/FolderCard";
-import { faSquare, faCheckSquare } from "@fortawesome/free-regular-svg-icons";
-import { faFolder } from "@fortawesome/free-solid-svg-icons";
-import { withRouter, RouteComponentProps } from "react-router-dom";
-
-type PathParamsType = {
-  location: any;
-  history: string;
-};
-
-type PropsType = RouteComponentProps<PathParamsType> & ResourceFoldersProps;
-
-export interface ResourceFoldersProps {
-  folderItems: {
-    title: string;
-    id: number;
-  }[];
-}
-
-type idBooleanMap = { [key: number]: boolean };
-interface MyState {
-  isSelected: idBooleanMap;
-  isHoveringOver: idBooleanMap;
-}
-
-class ResourcesFolderView extends React.Component<PropsType, MyState> {
-  constructor(props: PropsType) {
-    super(props);
-    this.state = { isSelected: [], isHoveringOver: []};
-  }
-
-  componentDidMount() {
-    let isSelected: idBooleanMap = [];
-    this.props.folderItems.forEach(({ id }: { id: number }) => {
-      isSelected[id] = false;
-    });
-    this.setState({ isSelected });
-  }
-
-  isAnySelected(): boolean {
-    let items = this.props.folderItems;
-    let isSelected = this.state.isSelected;
-    for (let item in items) {
-      if (isSelected[items[item].id]) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  isAllSelected(): boolean {
-    let items = this.props.folderItems;
-    let isSelected = this.state.isSelected;
-    for (let item in items) {
-      if (!isSelected[items[item].id]) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  handleIconClick(id: number) {
-    let isSelected = JSON.parse(JSON.stringify(this.state.isSelected));
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isSelected[id] = !isSelected[id];
-    isHoveringOver[id] = false;
-    this.setState({ isSelected, isHoveringOver });
-  }
-
-  handleSelectAllClick() {
-    let items = this.props.folderItems;
-    let isSelected = JSON.parse(JSON.stringify(this.state.isSelected));
-    let setValue = !this.isAllSelected();
-    for (let item in items) {
-      isSelected[items[item].id] = setValue;
-    }
-    this.setState({ isSelected });
-  }
-
-  handleCardClick(id: number) {
-    if (this.isAnySelected()) {
-      this.handleIconClick(id);
-      return;
-    }
-
-    let items = this.props.folderItems;
-    for (let item in items) {
-      if (items[item].id === id) {
-        this.props.history.push(
-          `${this.props.location.pathname}/${items[item].title}`
-        );
-        return;
-      }
-    }
-  }
-
-  handleMouseOver(id: number) {
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isHoveringOver[id] = true;
-    this.setState({ isHoveringOver });
-  }
-
-  handleMouseOut(id: number) {
-    let isHoveringOver = JSON.parse(JSON.stringify(this.state.isHoveringOver));
-    isHoveringOver[id] = false;
-    this.setState({ isHoveringOver });
-  }
-
-  render() {
-    return (
-      <>
-        <ResourceSectionHeader
-          heading="Folders"
-          showDownload={this.isAnySelected()}
-          onDownloadClick={() => {}}
-          onSelectAllClick={() => this.handleSelectAllClick()}
-					selectAllIcon={this.isAllSelected() ? faCheckSquare : faSquare}
-					checkBoxColur={this.isAnySelected() ? "#495057" : "#dee2e6"}
-        />
-
-        <Row style={{ marginTop: "10px", marginRight: "-10px", marginLeft: "-10px" }}>
-          {this.props.folderItems.map(({ title, id }) => (
-            <Col xs={6} sm={6} md={3} key={id} style={{ paddingLeft: "10px", paddingRight: "10px" }}>
-              <FolderCard
-                title={title}
-                icon={
-                  this.isAnySelected() || this.state.isHoveringOver[id]
-                    ? this.state.isSelected[id]
-                      ? faCheckSquare
-                      : faSquare
-                    : faFolder
-                }
-                onIconClick={(e) => {
-                  e.stopPropagation();
-                  this.handleIconClick(id);
-                }}
-                onClick={() => this.handleCardClick(id)}
-                onMouseOver={() => this.handleMouseOver(id)}
-                onMouseOut={() => this.handleMouseOut(id)}
-              />
-            </Col>
-          ))}
-        </Row>
-      </>
-    );
-  }
-}
-
-export default withRouter(ResourcesFolderView);
diff --git a/src/components/molecules/ResourcesFolderView/style.module.scss b/src/components/molecules/ResourcesFolderView/style.module.scss
deleted file mode 100644
index 90ea856af..000000000
--- a/src/components/molecules/ResourcesFolderView/style.module.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import "assets/scss/custom";
\ No newline at end of file
diff --git a/src/components/molecules/SelectionView/index.tsx b/src/components/molecules/SelectionView/index.tsx
index b41bc74f8..1e74ac2ad 100644
--- a/src/components/molecules/SelectionView/index.tsx
+++ b/src/components/molecules/SelectionView/index.tsx
@@ -26,9 +26,10 @@ export interface SelectionProps {
 
 export interface MyProps {
   selectionItems: SelectionItem[];
-  moduleCode?: string;
 	render: (selection: SelectionProps) => any;
 	heading: string;
+	onDownloadClick: (identifiers: number[]) => void;
+	onItemClick: (identifier: number) => void;
 }
 
 interface MyState {
@@ -80,10 +81,19 @@ class SelectionView extends React.Component<MyProps, MyState> {
     isSelected[id] = !isSelected[id];
     isHoveringOver[id] = false;
     this.setState({ isSelected, isHoveringOver });
+	}
+	
+	handleDownloadClick(e: React.MouseEvent) {
+		e.preventDefault();
+    let indices : number[] = [];
+    for (let key in this.state.isSelected) {
+      if (this.state.isSelected[key]) {
+        indices.push(parseInt(key));
+      }
+    }
+		this.props.onDownloadClick(indices);
   }
 
-  handleDownloadClick() {}
-
   handleSelectAllClick() {
     let items = this.props.selectionItems;
     let isSelected = JSON.parse(JSON.stringify(this.state.isSelected));
@@ -98,7 +108,8 @@ class SelectionView extends React.Component<MyProps, MyState> {
     if (this.isAnySelected()) {
       this.handleIconClick(id);
       return;
-    }
+		}
+		this.props.onItemClick(id);
   }
 
   handleMouseOver(id: number) {
@@ -128,7 +139,7 @@ class SelectionView extends React.Component<MyProps, MyState> {
       <>
         <ResourceSectionHeader
           heading={this.props.heading}
-          onDownloadClick={() => this.handleDownloadClick()}
+          onDownloadClick={(e) => this.handleDownloadClick(e)}
           showDownload={this.isAnySelected()}
           onSelectAllClick={() => this.handleSelectAllClick()}
           selectAllIcon={this.isAllSelected() ? faCheckSquare : faSquare}
diff --git a/src/components/pages/ModuleResources/index.tsx b/src/components/pages/ModuleResources/index.tsx
index 3d07f8d01..89242227b 100644
--- a/src/components/pages/ModuleResources/index.tsx
+++ b/src/components/pages/ModuleResources/index.tsx
@@ -5,8 +5,6 @@ import { api, methods } from "../../../constants/routes";
 import MyBreadcrumbs from "components/atoms/MyBreadcrumbs";
 
 import QuickAccessRow from "components/molecules/QuickAccessRow";
-import ResourcesFolderView from "components/molecules/ResourcesFolderView";
-import CurrentDirectoryView from "components/molecules/CurrentDirectoryView";
 import SearchBox from "components/molecules/SearchBox";
 import SelectionView, {
   SelectionProps,
@@ -113,6 +111,76 @@ class ModuleResources extends React.Component<ResourcesProps, ResourceState> {
     return title.indexOf(rest) !== -1;
   }
 
+  handleFileDownload(indices: number[]) {
+    const onSuccess = (filename: string, data: any) => {
+      // TODO: Try to navigate straight to the endpoint url instead of creating an object url
+      data.blob().then((blob: any) => {
+        let url = URL.createObjectURL(blob);
+        let a = document.createElement("a");
+        a.href = url;
+        a.download = filename;
+        a.click();
+        a.remove();
+      });
+    };
+    // Partial application utility
+    const downloadFilename = (filename: string) => {
+      return (data: any) => {
+        return onSuccess(filename, data);
+      };
+    };
+    const onFailure = (error: { text: () => Promise<any> }) => {
+      error.text().then((errorText) => {
+        console.log(errorText);
+      });
+    };
+
+    if (indices.length === 1) {
+      // Only one file to download, call single file endpoint
+      let filename = this.state.resources.filter(
+        (document) => document.id === indices[0]
+      )[0].title;
+      request(
+        api.MATERIALS_RESOURCES_FILE(indices[0]),
+        methods.GET,
+        downloadFilename(filename),
+        onFailure
+      );
+    } else {
+      // Multiple files to download, call zipped selection endpoint
+      request(
+        api.MATERIALS_ZIPPED_SELECTION,
+        methods.GET,
+        downloadFilename("materials.zip"),
+        onFailure,
+        {
+          ids: indices,
+          course: this.moduleCode,
+        }
+      );
+    }
+	}
+	
+	handleFileClick(id: number){
+		const onSuccess = (data: any) => {
+      // TODO: Try to navigate straight to the endpoint url instead of creating an object url
+      data.blob().then((blob: any) => {
+        let url = URL.createObjectURL(blob);
+        let a = document.createElement("a");
+        a.target = "_blank";
+        a.href = url;
+        a.click();
+        a.remove();
+      });
+    };
+    const onFailure = (error: { text: () => Promise<any> }) => {
+      error.text().then((errorText) => {
+        console.log(errorText);
+      });
+		};
+    request(api.MATERIALS_RESOURCES_FILE(id), methods.GET, onSuccess, onFailure);
+	}
+
   getResourcesFolderView(scope: any) {
     let folders: { title: string; id: number }[] = Array.from(
       new Set<string>(this.state.resources.map((res: Resource) => res.folder))
@@ -121,16 +189,16 @@ class ModuleResources extends React.Component<ResourcesProps, ResourceState> {
       id: id,
     }));
 
-    if (this.state.searchText === "" && scope === "" && folders.length > 0) {			
-			return (
+    if (this.state.searchText === "" && scope === "" && folders.length > 0) {
+      return (
         <SelectionView
           heading="Folders"
+					onDownloadClick={() => {}}
+					onItemClick={() => {}}
           selectionItems={folders}
-          render={(select: SelectionProps) => (
-            <FoldersRow select={select} />
-          )}
+          render={(select: SelectionProps) => <FoldersRow select={select} />}
         />
-			);
+      );
     }
   }
 
@@ -149,6 +217,8 @@ class ModuleResources extends React.Component<ResourcesProps, ResourceState> {
       return (
         <SelectionView
           heading="Files"
+					onItemClick={(id) => this.handleFileClick(id)}
+          onDownloadClick={(ids) => this.handleFileDownload(ids)}
           selectionItems={filesContent}
           render={(select: SelectionProps) => (
             <CurrentDirectoryRow select={select} />
@@ -172,6 +242,8 @@ class ModuleResources extends React.Component<ResourcesProps, ResourceState> {
       return (
         <SelectionView
           heading="Quick Access"
+					onItemClick={(id) => this.handleFileClick(id)}
+          onDownloadClick={(ids) => this.handleFileDownload(ids)}
           selectionItems={quickAccessItems}
           render={(select: SelectionProps) => (
             <QuickAccessRow select={select} />
-- 
GitLab