Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/course/handouts/handouts-panel.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Alert, Button } from "antd";6import { Set } from "immutable";7import { useState } from "react";8import { FormattedMessage, useIntl } from "react-intl";910// CoCalc and course components11import { useRedux } from "@cocalc/frontend/app-framework";12import { Icon, Tip } from "@cocalc/frontend/components";13import ScrollableList from "@cocalc/frontend/components/scrollable-list";14import { course } from "@cocalc/frontend/i18n";15import { UserMap } from "@cocalc/frontend/todo-types";16import { cmp } from "@cocalc/util/misc";17import { CourseActions } from "../actions";18import { AddItems, FoldersToolbar } from "../common/folders-tool-bar";19import { HandoutRecord, HandoutsMap, StudentsMap } from "../store";20import * as styles from "../styles";21import * as util from "../util";22import { Handout } from "./handout";2324interface HandoutsPanelReactProps {25frame_id?: string;26name: string;27actions: CourseActions;28project_id: string;29handouts: HandoutsMap; // handout_id -> handout30students: StudentsMap; // student_id -> student31user_map: UserMap;32frameActions;33}3435export function HandoutsPanel({36frame_id,37name,38actions,39project_id,40handouts,41students,42user_map,43frameActions,44}: HandoutsPanelReactProps) {45const intl = useIntl();46const expanded_handouts: Set<string> | undefined = useRedux(47name,48"expanded_handouts",49);5051const [show_deleted, set_show_deleted] = useState<boolean>(false);5253const pageFilter = useRedux(name, "pageFilter");54const filter = pageFilter?.get("handouts") ?? "";55const setFilter = (filter: string) => {56actions.setPageFilter("handouts", filter);57};5859function get_handout(id: string): HandoutRecord {60const handout = handouts.get(id);61if (handout == undefined) {62console.warn(`Tried to access undefined handout ${id}`);63}64return handout as any;65}6667function compute_handouts_list() {68let num_deleted, num_omitted;69let list = util.immutable_to_list(handouts, "handout_id");7071({ list, num_omitted } = util.compute_match_list({72list,73search_key: "path",74search: filter.trim(),75}));7677({ list, num_deleted } = util.order_list({78list,79compare_function: (a, b) =>80cmp(a.path?.toLowerCase(), b.path?.toLowerCase()),81reverse: false,82include_deleted: show_deleted,83}));8485return {86shown_handouts: list,87num_omitted,88num_deleted,89};90}9192function render_show_deleted_button(num_deleted, num_shown) {93const label = intl.formatMessage(94{95id: "course.handouts-panel.show_deleted_button.label",96defaultMessage: `{show_deleted, select, true {Hide} other {Show}} {num_deleted} deleted handouts`,97},98{ num_deleted, show_deleted },99);100if (show_deleted) {101const tooltip = intl.formatMessage({102id: "course.handouts-panel.show_deleted_button.hide.tooltip",103defaultMessage: `Handouts are never really deleted.104Click this button so that deleted handouts aren't included at the bottom of the list.`,105});106return (107<Button108style={styles.show_hide_deleted({ needs_margin: num_shown > 0 })}109onClick={() => set_show_deleted(false)}110>111<Tip placement="left" title="Hide deleted" tip={tooltip}>112{label}113</Tip>114</Button>115);116} else {117const tooltip = intl.formatMessage({118id: "course.handouts-panel.show_deleted_button.show.tooltip",119defaultMessage: `Handouts are not deleted forever even after you delete them.120Click this button to show any deleted handouts at the bottom of the list of handouts.121You can then click on the handout and click undelete to bring the handout back.`,122});123return (124<Button125style={styles.show_hide_deleted({ needs_margin: num_shown > 0 })}126onClick={() => {127set_show_deleted(true);128setFilter("");129}}130>131<Tip placement="left" title="Show deleted" tip={tooltip}>132{label}133</Tip>134</Button>135);136}137}138139function render_handout(handout_id: string, index: number) {140return (141<Handout142frame_id={frame_id}143backgroundColor={index % 2 === 0 ? "#eee" : undefined}144key={handout_id}145handout={get_handout(handout_id)}146project_id={project_id}147students={students}148user_map={user_map}149actions={actions}150is_expanded={expanded_handouts?.has(handout_id) ?? false}151name={name}152/>153);154}155156function render_handouts(handouts) {157if (handouts.length == 0) {158return render_no_handouts();159}160return (161<ScrollableList162virtualize163rowCount={handouts.length}164rowRenderer={({ key, index }) => render_handout(key, index)}165rowKey={(index) => handouts[index]?.handout_id ?? ""}166cacheId={`course-handouts-${name}-${frame_id}`}167/>168);169}170171function render_no_handouts() {172return (173<div>174<Alert175type="info"176style={{177margin: "15px auto",178fontSize: "12pt",179maxWidth: "800px",180}}181message={182<b>183<a onClick={() => frameActions.setModal("add-handouts")}>184<FormattedMessage185id="course.handouts-panel.no_assignments.message"186defaultMessage={"Add Handouts to your Course"}187description={"online course for students"}188/>189</a>190</b>191}192description={193<div>194<FormattedMessage195id="course.handouts-panel.no_assignments.description"196description={"online course for students"}197defaultMessage={`198<p>199A handout is a <i>directory</i> of files somewhere in your200CoCalc project, which you copy to all of your students. They can201then do anything they want with that handout.202</p>203<p>204<A>Add handouts to your course</A> by clicking "Add Handout..." above.205You can create or select one or more directories206and they will become handouts that you can207then customize and distribute to your students.208</p>`}209values={{210A: (c) => (211<a onClick={() => frameActions.setModal("add-handouts")}>212{c}213</a>214),215}}216/>217</div>218}219/>220</div>221);222}223224// Computed data from state changes have to go in render225const { shown_handouts, num_omitted, num_deleted } = compute_handouts_list();226227const header = (228<FoldersToolbar229search={filter}230search_change={setFilter}231num_omitted={num_omitted}232project_id={project_id}233items={handouts}234add_folders={actions.handouts.addHandout}235item_name={"handout"}236plural_item_name={"handouts"}237/>238);239240return (241<div className={"smc-vfill"} style={{ margin: "0" }}>242{header}243<div style={{ marginTop: "5px" }} />244{render_handouts(shown_handouts)}245{num_deleted > 0246? render_show_deleted_button(247num_deleted,248shown_handouts.length != null ? shown_handouts.length : 0,249)250: undefined}251</div>252);253}254255export function HandoutsPanelHeader(props: { n: number }) {256const intl = useIntl();257258return (259<Tip260delayShow={1300}261title="Handouts"262tip={intl.formatMessage({263id: "course.handouts-panel.header.tooltip",264defaultMessage:265"This tab lists all of the handouts associated with your course.",266description: "online course for students",267})}268>269<span>270<Icon name="files" /> {intl.formatMessage(course.handouts)}{" "}271{props.n != null ? ` (${props.n})` : ""}272</span>273</Tip>274);275}276277// used for adding assignments outside of the above component.278export function AddHandouts({ name, actions, close }) {279const handouts = useRedux(name, "handouts");280return (281<AddItems282itemName="handout"283items={handouts}284addItems={(paths) => {285actions.handouts.addHandout(paths);286close?.();287}}288selectorStyle={{289position: null,290width: "100%",291boxShadow: null,292zIndex: null,293backgroundColor: null,294}}295defaultOpen296closable={false}297/>298);299}300301302