Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Views: 39550
1
##############################################################################
2
#
3
# CoCalc: Collaborative Calculation in the Cloud
4
#
5
# Copyright (C) 2016 -- 2017, Sagemath Inc.
6
#
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#
20
###############################################################################
21
22
{React, ReactDOM, rclass, redux, rtypes, Redux, Actions, Store, COLOR} = require('./smc-react')
23
{Button, Col, Row, Modal, NavItem} = require('react-bootstrap')
24
{Icon, Space, Tip} = require('./r_misc')
25
{COLORS} = require('smc-util/theme')
26
{webapp_client} = require('./webapp_client')
27
misc = require('smc-util/misc')
28
29
{HelpPage} = require('./r_help')
30
{ProjectsPage} = require('./projects')
31
{ProjectPage, MobileProjectPage} = require('./project_page')
32
{AccountPage} = require('./account_page')
33
{FileUsePage} = require('./file_use')
34
35
ACTIVE_BG_COLOR = COLORS.TOP_BAR.ACTIVE
36
feature = require('./feature')
37
38
exports.ActiveAppContent = ({active_top_tab, render_small}) ->
39
switch active_top_tab
40
when 'projects'
41
return <ProjectsPage />
42
when 'account'
43
return <AccountPage />
44
when 'about'
45
return <HelpPage />
46
when 'help'
47
return <div>To be implemented</div>
48
when 'file-use'
49
return <FileUsePage redux={redux} />
50
when undefined
51
return
52
else
53
project_name = redux.getProjectStore(active_top_tab).name
54
if render_small
55
<MobileProjectPage name={project_name} project_id={active_top_tab} />
56
else
57
<ProjectPage name={project_name} project_id={active_top_tab} />
58
59
exports.NavTab = rclass
60
displayName : "NavTab"
61
62
propTypes :
63
label : rtypes.string
64
icon : rtypes.oneOfType([rtypes.string, rtypes.object])
65
close : rtypes.bool
66
on_click : rtypes.func
67
active_top_tab : rtypes.string
68
actions : rtypes.object
69
style : rtypes.object
70
inner_style : rtypes.object
71
add_inner_style : rtypes.object
72
73
make_icon: ->
74
if typeof(@props.icon) == 'string'
75
<Icon
76
name = {@props.icon}
77
style = {fontSize: 20, paddingRight: 2} />
78
else if @props.icon?
79
@props.icon
80
81
on_click: (e) ->
82
if @props.name?
83
@actions('page').set_active_tab(@props.name)
84
@props.on_click?()
85
86
render: ->
87
is_active = @props.active_top_tab == @props.name
88
89
if @props.style?
90
outer_style = @props.style
91
else
92
outer_style = {}
93
94
outer_style.float = 'left'
95
96
outer_style.fontSize ?= '14px'
97
outer_style.cursor ?= 'pointer'
98
outer_style.border = 'none'
99
100
if is_active
101
outer_style.backgroundColor = ACTIVE_BG_COLOR
102
103
if @props.inner_style
104
inner_style = @props.inner_style
105
else
106
inner_style =
107
padding : '10px'
108
if @props.add_inner_style
109
misc.merge(inner_style, @props.add_inner_style)
110
111
<NavItem
112
active = {is_active}
113
onClick = {@on_click}
114
style = {outer_style}
115
>
116
<div style={inner_style}>
117
{@make_icon()}
118
{<span style={marginLeft: 5}>{@props.label}</span> if @props.label?}
119
{@props.children}
120
</div>
121
</NavItem>
122
123
exports.NotificationBell = rclass
124
displayName: 'NotificationBell'
125
126
propTypes :
127
count : rtypes.number
128
active : rtypes.bool
129
on_click : rtypes.func
130
131
getDefaultProps: ->
132
active : false
133
134
on_click: (e) ->
135
@actions('page').toggle_show_file_use()
136
document.activeElement.blur() # otherwise, it'll be highlighted even when closed again
137
@props.on_click?()
138
139
notification_count: ->
140
count_styles =
141
fontSize : '10pt'
142
color : COLOR.FG_RED
143
position : 'absolute'
144
left : '17.5px'
145
fontWeight : 700
146
background : 'transparent'
147
if @props.count > 9
148
count_styles.left = '15.8px'
149
count_styles.background = COLORS.GRAY_L
150
count_styles.borderRadius = '50%'
151
count_styles.border = '2px solid lightgrey'
152
if @props.count > 0
153
<span style={count_styles}>{@props.count}</span>
154
155
render: ->
156
outer_style =
157
position : 'relative'
158
float : 'left'
159
160
if @props.active
161
outer_style.backgroundColor = ACTIVE_BG_COLOR
162
163
inner_style =
164
padding : '10px'
165
fontSize : '17pt'
166
cursor : 'pointer'
167
168
clz = ''
169
bell_style = {}
170
if @props.count > 0
171
clz = 'smc-bell-notification'
172
bell_style = {color: COLOR.FG_RED}
173
174
<NavItem
175
ref='bell'
176
style={outer_style}
177
onClick={@on_click}
178
className={'active' if @props.active}
179
>
180
<div style={inner_style}>
181
<Icon name='bell-o' className={clz} style={bell_style} />
182
{@notification_count()}
183
</div>
184
</NavItem>
185
186
exports.ConnectionIndicator = rclass
187
displayName : 'ConnectionIndicator'
188
189
reduxProps :
190
page :
191
avgping : rtypes.number
192
connection_status : rtypes.string
193
194
propTypes :
195
ping : rtypes.number
196
status : rtypes.string
197
actions : rtypes.object
198
on_click : rtypes.func
199
200
connection_status: ->
201
if @props.connection_status == 'connected'
202
<div>
203
<Icon name='wifi' style={marginRight: 8, fontSize: '13pt', display: 'inline'} />
204
{<Tip
205
title = {'Most recently recorded roundtrip time to the server.'}
206
placement = {'left'}
207
>
208
{Math.floor(@props.avgping)}ms
209
</Tip> if @props.avgping?}
210
</div>
211
else if @props.connection_status == 'connecting'
212
<span style={backgroundColor : '#FFA500', color : 'white', padding : '1ex', 'zIndex': 100001}>
213
connecting...
214
</span>
215
else if @props.connection_status == 'disconnected'
216
<span style={backgroundColor : 'darkred', color : 'white', padding : '1ex', 'zIndex': 100001}>
217
disconnected
218
</span>
219
220
connection_click: ->
221
@props.actions.show_connection(true)
222
@props.on_click?()
223
document.activeElement.blur() # otherwise, it'll be highlighted even when closed again
224
225
render: ->
226
outer_styles =
227
width : '8.5em'
228
color : '#666'
229
fontSize : '10pt'
230
lineHeight : '10pt'
231
cursor : 'pointer'
232
float : 'left'
233
inner_styles =
234
padding : '13.5px'
235
236
<NavItem style={outer_styles} onClick={@connection_click}>
237
<div style={inner_styles} >
238
{@connection_status()}
239
</div>
240
</NavItem>
241
242
exports.ConnectionInfo = rclass
243
displayName : 'ConnectionInfo'
244
245
propTypes :
246
actions : rtypes.object
247
hub : rtypes.string
248
ping : rtypes.number
249
avgping : rtypes.number
250
status : rtypes.string
251
252
reduxProps :
253
account :
254
hub : rtypes.string
255
256
close: ->
257
@actions('page').show_connection(false)
258
259
connection_body: ->
260
<div>
261
{<Row>
262
<Col sm=3>
263
<h4>Ping Time</h4>
264
</Col>
265
<Col sm=5>
266
<pre>{@props.avgping}ms (latest: {@props.ping}ms)</pre>
267
</Col>
268
</Row> if @props.ping}
269
<Row>
270
<Col sm=3>
271
<h4>Hub Server</h4>
272
</Col>
273
<Col sm=5>
274
<pre>{if @props.hub? then @props.hub else "Not signed in"}</pre>
275
</Col>
276
<Col sm=3 smOffset=1>
277
<Button bsStyle='warning' onClick={=>webapp_client._fix_connection(true)}>
278
<Icon name='repeat' spin={@props.status == 'connecting'} /> Reconnect
279
</Button>
280
</Col>
281
</Row>
282
</div>
283
284
render: ->
285
<Modal show={true} onHide={@close} animation={false}>
286
<Modal.Header closeButton>
287
<Modal.Title>
288
<Icon name='wifi' style={marginRight: '1em'} /> Connection
289
</Modal.Title>
290
</Modal.Header>
291
<Modal.Body>
292
{@connection_body()}
293
</Modal.Body>
294
<Modal.Footer>
295
<Button onClick={@close}>Close</Button>
296
</Modal.Footer>
297
</Modal>
298
299
exports.FullscreenButton = rclass
300
displayName : 'FullscreenButton'
301
302
reduxProps :
303
page :
304
fullscreen : rtypes.oneOf(['default', 'kiosk'])
305
306
on_fullscreen: (ev) ->
307
if ev.shiftKey
308
@actions('page').set_fullscreen('kiosk')
309
else
310
@actions('page').toggle_fullscreen()
311
312
render: ->
313
icon = if @props.fullscreen then 'expand' else 'compress'
314
315
outer_style =
316
position : 'fixed'
317
zIndex : 10000
318
right : 0
319
top : '1px'
320
borderRadius: '3px'
321
322
icon_style =
323
fontSize : '13pt'
324
padding : 4
325
color : COLORS.GRAY
326
cursor : 'pointer'
327
328
if @props.fullscreen
329
outer_style.background = '#fff'
330
outer_style.opacity = .7
331
outer_style.border = '1px solid grey'
332
333
<Tip
334
style = {outer_style}
335
title = {'Removes navigational chrome from the UI. Shift-click to enter "kiosk-mode".'}
336
placement = {'left'}
337
>
338
<Icon style={icon_style} name={icon} onClick = {(e) => @on_fullscreen(e)} />
339
</Tip>
340
341
exports.AppLogo = rclass
342
displayName : 'AppLogo'
343
344
render: ->
345
{APP_ICON} = require('./misc_page')
346
styles =
347
display : 'inline-block'
348
backgroundImage : "url('#{APP_ICON}')"
349
backgroundSize : 'contain'
350
backgroundRepeat: 'no-repeat'
351
height : 36
352
width : 36
353
position : 'relative'
354
margin : '2px'
355
<div style={styles}></div>
356
357
exports.VersionWarning = rclass
358
displayName : 'VersionWarning'
359
360
propTypes :
361
new_version : rtypes.object
362
363
render_critical: ->
364
if @props.new_version.min_version > webapp_client.version()
365
<div>
366
<br />
367
THIS IS A CRITICAL UPDATE. YOU MUST <Space/>
368
<a onClick={=>window.location.reload()} style={cursor:'pointer', color: 'white', fontWeight: 'bold', textDecoration: 'underline'}>
369
RELOAD THIS PAGE
370
</a>
371
<Space/> IMMEDIATELY OR YOU WILL BE DISCONNECTED. Sorry for the inconvenience.
372
</div>
373
374
render_close: ->
375
if not (@props.new_version.min_version > webapp_client.version())
376
<Icon
377
name = 'times'
378
className = 'pull-right'
379
style = {cursor : 'pointer'}
380
onClick = {=>redux.getActions('page').set_new_version(undefined)} />
381
382
render: ->
383
styles =
384
position : 'fixed'
385
left : 12
386
backgroundColor : 'red'
387
color : '#fff'
388
top : 20
389
opacity : .75
390
borderRadius : 4
391
padding : 5
392
zIndex : 900
393
boxShadow : '8px 8px 4px #888'
394
width : '70%'
395
marginTop : '1em'
396
<div style={styles}>
397
<Icon name='refresh' /> New Version Available: upgrade by clicking on <Space/>
398
<a onClick={=>window.location.reload()} style={cursor:'pointer', color: 'white', fontWeight: 'bold', textDecoration: 'underline'}>
399
reload this page
400
</a>.
401
{@render_close()}
402
{@render_critical()}
403
</div>
404
405
warning_styles =
406
position : 'fixed'
407
left : 12
408
backgroundColor : 'red'
409
color : '#fff'
410
top : 20
411
opacity : .9
412
borderRadius : 4
413
padding : 5
414
marginTop : '1em'
415
zIndex : 100000
416
boxShadow : '8px 8px 4px #888'
417
width : '70%'
418
419
exports.CookieWarning = rclass
420
displayName : 'CookieWarning'
421
422
render: ->
423
<div style={warning_styles}>
424
<Icon name='warning' /> You <em>must</em> enable cookies to sign into CoCalc.
425
</div>
426
427
misc = require('smc-util/misc')
428
storage_warning_style = misc.copy(warning_styles)
429
storage_warning_style.top = 55
430
431
exports.LocalStorageWarning = rclass
432
displayName : 'LocalStorageWarning'
433
434
render: ->
435
<div style={storage_warning_style}>
436
<Icon name='warning' /> You <em>must</em> enable local storage to use this website{' (on Safari you must disable private browsing mode)' if feature.get_browser() == 'safari'}.
437
</div>
438
439
# This is used in the "desktop_app" to show a global announcement on top of CC
440
# It was first used for a general CoCalc announcement, but it's general enough to be used later on
441
# for other global announcements.
442
# For now, it just has a simple dismiss button backed by the account other_settings, though.
443
exports.GlobalInformationMessage = rclass
444
displayName: 'GlobalInformationMessage'
445
446
dismiss: ->
447
redux.getTable('account').set(other_settings:{show_global_info2:webapp_client.server_time()})
448
449
render: ->
450
more_url = 'https://github.com/sagemathinc/cocalc/wiki/KubernetesMigration'
451
bgcol = COLORS.YELL_L
452
style =
453
padding : '5px 0 5px 5px'
454
backgroundColor : bgcol
455
fontSize : '18px'
456
position : 'fixed'
457
zIndex : '101'
458
right : 0
459
left : 0
460
height : '40px'
461
462
<Row style={style}>
463
<Col sm={9} style={paddingTop: 3}>
464
<p><b>CoCalc <a target='_blank' href={more_url}>migrated to Kubernetes</a></b>.
465
{' '}Please report any issues.
466
{' '}<a target='_blank' href={more_url}>More information...</a></p>
467
</Col>
468
<Col sm={3}>
469
<Button bsStyle='danger' bsSize="small" className='pull-right' style={marginRight:'20px'}
470
onClick={@dismiss}>Close</Button>
471
</Col>
472
</Row>
473
474