Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/custom-software/selector.tsx
5801 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
// num images > 1 to not show the standard default
85
// https://github.com/sagemathinc/cocalc/issues/8510
86
() => (customize_software.get("environments")?.size ?? 0) > 1,
87
[customize_software],
88
);
89
90
// ID of the image, custom images without "CUSTOM_PREFIX/" – that info is in the image_type variable.
91
const [image_selected, set_image_selected] = useState<string | undefined>(
92
undefined,
93
);
94
const [image_type, set_image_type] = useState<ComputeImageTypes>("standard");
95
96
const [softwareInfo, setSoftwareInfo] = useState<SoftwareInfo | null>(null);
97
98
function setState(
99
image_selected: string,
100
title_text: string,
101
image_type: ComputeImageTypes,
102
): void {
103
const id =
104
image_type === "custom"
105
? custom_image_name(image_selected)
106
: image_selected;
107
set_image_selected(id);
108
set_image_type(image_type);
109
onChange({ image_selected, title_text, image_type });
110
}
111
112
// initialize selection, if there is a default image set
113
React.useEffect(() => {
114
if (default_image == null || default_image === dflt_software_img) {
115
// do nothing, that's the initial state already!
116
} else if (is_custom_image(default_image)) {
117
if (images == null) return;
118
const id = compute_image2basename(default_image);
119
const img: ComputeImage | undefined = images.get(id);
120
if (img == null) {
121
// ignore, user has to select from scratch
122
} else {
123
setState(id, img.get("display", ""), "custom");
124
}
125
} else {
126
// must be standard image
127
const img = software_images.get(default_image);
128
const display = img != null ? img.get("title") ?? "" : "";
129
setState(default_image, display, "standard");
130
}
131
}, []);
132
133
function render_custom_images_config() {
134
if (image_type !== "custom") return;
135
136
return (
137
<>
138
<Col sm={12}>
139
<FormattedMessage
140
id="custom-software.selector.select-custom-image"
141
defaultMessage={`<p>Specialized software environment are provided by 3rd parties and usually contain accompanying files to work with.</p>
142
143
<p>Note: A <em>specialized</em> software environment is tied to the project.
144
In order to work in a different environment, create another project.
145
You can always <A>copy files between projects</A> as well.</p>`}
146
values={{
147
em: (c) => <em>{c}</em>,
148
p: (c) => <Paragraph type="secondary">{c}</Paragraph>,
149
A: (c) => (
150
<A
151
href={
152
"https://doc.cocalc.com/project-files.html#file-actions-on-one-file"
153
}
154
>
155
{c}
156
</A>
157
),
158
}}
159
/>
160
</Col>
161
</>
162
);
163
}
164
165
function render_software_form_label() {
166
return (
167
<span>
168
<Icon name={SOFTWARE_ENVIRONMENT_ICON} />{" "}
169
{intl.formatMessage(labels.software)}
170
</span>
171
);
172
}
173
174
function render_onprem() {
175
const selected = image_selected ?? dflt_software_img;
176
return (
177
<>
178
<Col sm={24}>
179
<Form>
180
<Form.Item
181
label={render_software_form_label()}
182
style={{ marginBottom: "0px", width: "100%" }}
183
>
184
<ComputeImageSelector
185
size={"middle"}
186
current_image={selected}
187
layout={"horizontal"}
188
hideCustomImages={true}
189
onSelect={({ id }) => {
190
const display = software_images.get(id)?.get("title") ?? id;
191
setState(id, display, "standard");
192
}}
193
/>
194
</Form.Item>
195
</Form>
196
</Col>
197
</>
198
);
199
}
200
201
function render_software_env_help() {
202
return (
203
<HelpIcon title={intl.formatMessage(labels.software_environment)}>
204
<Paragraph>
205
<FormattedMessage
206
id="custom-software.selector.explanation.cocalc_com"
207
defaultMessage={`<em>Standard</em> software environments are well tested and
208
maintained by {company}, while <em>specialized</em> software environments are provided by 3rd parties
209
and tied to a given project – <A2>more info...</A2>.
210
`}
211
values={{
212
em: (c) => <em>{c}</em>,
213
company: organization_name,
214
A2: (c) => <A href={CUSTOM_SOFTWARE_HELP_URL}>{c}</A>,
215
}}
216
/>
217
</Paragraph>
218
<SoftwareEnvironmentInformation />
219
</HelpIcon>
220
);
221
}
222
223
function render_standard_image_selector() {
224
const isCustom = is_custom_image(image_selected ?? dflt_software_img);
225
return (
226
<>
227
<Col sm={12}>
228
<Form>
229
<Form.Item
230
label={render_software_form_label()}
231
style={{ marginBottom: "0px" }}
232
>
233
<ComputeImageSelector
234
size="middle"
235
current_image={image_selected ?? dflt_software_img}
236
layout={"dropdown"}
237
setSoftwareInfo={setSoftwareInfo}
238
onSelect={({ id, display, type }) => {
239
setState(id, display, type);
240
}}
241
/>
242
</Form.Item>
243
</Form>
244
</Col>
245
<Col sm={12}>
246
<Paragraph type="secondary">
247
<FormattedMessage
248
id="custom-software.selector.explanation.onprem"
249
defaultMessage={`The software environment provides programming languages, tools and libraries for the project.`}
250
/>{" "}
251
{render_software_env_help()}
252
</Paragraph>
253
</Col>
254
{softwareInfo?.extra != null && (
255
<>
256
<Col
257
sm={isCustom ? 12 : 24}
258
style={{
259
paddingBottom: "30px",
260
}}
261
>
262
{softwareInfo.extra}
263
</Col>
264
{isCustom && render_custom_images_config()}
265
</>
266
)}
267
</>
268
);
269
}
270
271
if (!haveSoftwareImages) {
272
return;
273
}
274
275
if (onCoCalcCom) {
276
return render_standard_image_selector();
277
} else {
278
return render_onprem();
279
}
280
}
281
282