Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/custom-software/selector.tsx
1496 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
// cSpell:ignore descr disp dflt
7
8
import { Col, Form } from "antd";
9
import { FormattedMessage, useIntl } from "react-intl";
10
11
import {
12
React,
13
redux,
14
useMemo,
15
useState,
16
useTypedRedux,
17
} from "@cocalc/frontend/app-framework";
18
import { A, HelpIcon, Icon, Paragraph } from "@cocalc/frontend/components";
19
import { labels } from "@cocalc/frontend/i18n";
20
import { ComputeImageSelector } from "@cocalc/frontend/project/settings/compute-image-selector";
21
import { SOFTWARE_ENVIRONMENT_ICON } from "@cocalc/frontend/project/settings/software-consts";
22
import { SoftwareEnvironmentInformation } from "@cocalc/frontend/project/settings/software-env-info";
23
import { SoftwareInfo } from "@cocalc/frontend/project/settings/types";
24
import { KUCALC_COCALC_COM } from "@cocalc/util/db-schema/site-defaults";
25
import { unreachable } from "@cocalc/util/misc";
26
import { ComputeImage, ComputeImageTypes, ComputeImages } from "./init";
27
import {
28
CUSTOM_SOFTWARE_HELP_URL,
29
compute_image2basename,
30
custom_image_name,
31
is_custom_image,
32
} from "./util";
33
34
export interface SoftwareEnvironmentState {
35
image_selected?: string;
36
title_text?: string;
37
image_type?: ComputeImageTypes;
38
}
39
40
// this is used in create-project and course/configuration/actions
41
// this derives the proper image name from the image type & image selection of SoftwareEnvironmentState
42
export async function derive_project_img_name(
43
custom_software: SoftwareEnvironmentState,
44
): Promise<string> {
45
const { image_type, image_selected } = custom_software;
46
const dflt_software_img = await redux
47
.getStore("customize")
48
.getDefaultComputeImage();
49
if (image_selected == null || image_type == null) {
50
return dflt_software_img;
51
}
52
switch (image_type) {
53
case "custom":
54
return custom_image_name(image_selected);
55
case "standard":
56
return image_selected;
57
default:
58
unreachable(image_type);
59
return dflt_software_img; // make TS happy
60
}
61
}
62
63
interface Props {
64
onChange: (obj: SoftwareEnvironmentState) => void;
65
default_image?: string; // which one to initialize state to
66
}
67
68
// this is a selector for the software environment of a project
69
export function SoftwareEnvironment(props: Props) {
70
const { onChange, default_image } = props;
71
const intl = useIntl();
72
const images: ComputeImages | undefined = useTypedRedux(
73
"compute_images",
74
"images",
75
);
76
const customize_kucalc = useTypedRedux("customize", "kucalc");
77
const onCoCalcCom = customize_kucalc === KUCALC_COCALC_COM;
78
const customize_software = useTypedRedux("customize", "software");
79
const organization_name = useTypedRedux("customize", "organization_name");
80
const dflt_software_img = customize_software.get("default");
81
const software_images = customize_software.get("environments");
82
83
const haveSoftwareImages: boolean = useMemo(
84
() => (customize_software.get("environments")?.size ?? 0) > 0,
85
[customize_software],
86
);
87
88
// ID of the image, custom images without "CUSTOM_PREFIX/" – that info is in the image_type variable.
89
const [image_selected, set_image_selected] = useState<string | undefined>(
90
undefined,
91
);
92
const [image_type, set_image_type] = useState<ComputeImageTypes>("standard");
93
94
const [softwareInfo, setSoftwareInfo] = useState<SoftwareInfo | null>(null);
95
96
function setState(
97
image_selected: string,
98
title_text: string,
99
image_type: ComputeImageTypes,
100
): void {
101
const id =
102
image_type === "custom"
103
? custom_image_name(image_selected)
104
: image_selected;
105
set_image_selected(id);
106
set_image_type(image_type);
107
onChange({ image_selected, title_text, image_type });
108
}
109
110
// initialize selection, if there is a default image set
111
React.useEffect(() => {
112
if (default_image == null || default_image === dflt_software_img) {
113
// do nothing, that's the initial state already!
114
} else if (is_custom_image(default_image)) {
115
if (images == null) return;
116
const id = compute_image2basename(default_image);
117
const img: ComputeImage | undefined = images.get(id);
118
if (img == null) {
119
// ignore, user has to select from scratch
120
} else {
121
setState(id, img.get("display", ""), "custom");
122
}
123
} else {
124
// must be standard image
125
const img = software_images.get(default_image);
126
const display = img != null ? img.get("title") ?? "" : "";
127
setState(default_image, display, "standard");
128
}
129
}, []);
130
131
function render_custom_images_config() {
132
if (image_type !== "custom") return;
133
134
return (
135
<>
136
<Col sm={12}>
137
<FormattedMessage
138
id="custom-software.selector.select-custom-image"
139
defaultMessage={`<p>Specialized software environment are provided by 3rd parties and usually contain accompanying files to work with.</p>
140
141
<p>Note: A <em>specialized</em> software environment is tied to the project.
142
In order to work in a different environment, create another project.
143
You can always <A>copy files between projects</A> as well.</p>`}
144
values={{
145
em: (c) => <em>{c}</em>,
146
p: (c) => <Paragraph type="secondary">{c}</Paragraph>,
147
A: (c) => (
148
<A
149
href={
150
"https://doc.cocalc.com/project-files.html#file-actions-on-one-file"
151
}
152
>
153
{c}
154
</A>
155
),
156
}}
157
/>
158
</Col>
159
</>
160
);
161
}
162
163
function render_software_form_label() {
164
return (
165
<span>
166
<Icon name={SOFTWARE_ENVIRONMENT_ICON} />{" "}
167
{intl.formatMessage(labels.software)}
168
</span>
169
);
170
}
171
172
function render_onprem() {
173
const selected = image_selected ?? dflt_software_img;
174
return (
175
<>
176
<Col sm={24}>
177
<Form>
178
<Form.Item
179
label={render_software_form_label()}
180
style={{ marginBottom: "0px", width: "100%" }}
181
>
182
<ComputeImageSelector
183
size={"middle"}
184
current_image={selected}
185
layout={"horizontal"}
186
hideCustomImages={true}
187
onSelect={({ id }) => {
188
const display = software_images.get(id)?.get("title") ?? id;
189
setState(id, display, "standard");
190
}}
191
/>
192
</Form.Item>
193
</Form>
194
</Col>
195
</>
196
);
197
}
198
199
function render_software_env_help() {
200
return (
201
<HelpIcon title={intl.formatMessage(labels.software_environment)}>
202
<Paragraph>
203
<FormattedMessage
204
id="custom-software.selector.explanation.cocalc_com"
205
defaultMessage={`<em>Standard</em> software environments are well tested and
206
maintained by {company}, while <em>specialized</em> software environments are provided by 3rd parties
207
and tied to a given project – <A2>more info...</A2>.
208
`}
209
values={{
210
em: (c) => <em>{c}</em>,
211
company: organization_name,
212
A2: (c) => <A href={CUSTOM_SOFTWARE_HELP_URL}>{c}</A>,
213
}}
214
/>
215
</Paragraph>
216
<SoftwareEnvironmentInformation />
217
</HelpIcon>
218
);
219
}
220
221
function render_standard_image_selector() {
222
const isCustom = is_custom_image(image_selected ?? dflt_software_img);
223
return (
224
<>
225
<Col sm={12}>
226
<Form>
227
<Form.Item
228
label={render_software_form_label()}
229
style={{ marginBottom: "0px" }}
230
>
231
<ComputeImageSelector
232
size="middle"
233
current_image={image_selected ?? dflt_software_img}
234
layout={"dropdown"}
235
setSoftwareInfo={setSoftwareInfo}
236
onSelect={({ id, display, type }) => {
237
setState(id, display, type);
238
}}
239
/>
240
</Form.Item>
241
</Form>
242
</Col>
243
<Col sm={12}>
244
<Paragraph type="secondary">
245
<FormattedMessage
246
id="custom-software.selector.explanation.onprem"
247
defaultMessage={`The software environment provides programming languages, tools and libraries for the project.`}
248
/>{" "}
249
{render_software_env_help()}
250
</Paragraph>
251
</Col>
252
{softwareInfo?.extra != null && (
253
<>
254
<Col
255
sm={isCustom ? 12 : 24}
256
style={{
257
paddingBottom: "30px",
258
}}
259
>
260
{softwareInfo.extra}
261
</Col>
262
{isCustom && render_custom_images_config()}
263
</>
264
)}
265
</>
266
);
267
}
268
269
if (!haveSoftwareImages) {
270
return;
271
}
272
273
if (onCoCalcCom) {
274
return render_standard_image_selector();
275
} else {
276
return render_onprem();
277
}
278
}
279
280