explorak5_canvas_login 3.2.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0d747595ee60e68d49c8917fadb27ae17c318a9526d65ed547ce349e6530f20
4
- data.tar.gz: d6eddb9622d4811dcfae630a449aa45aceb01db8a59a1ea841f2acd39556d9fd
3
+ metadata.gz: 7981f86c76cbb443863e7f07488c4b9189c59e4dbf98a3a89c587229cdd51f2b
4
+ data.tar.gz: ff6971539967b149a1b6a0ebce34ba0295dbd38e8b0ebf5ee28735e5ef7b49e2
5
5
  SHA512:
6
- metadata.gz: 734c9d5c97f379a338ddc481913b5f0ac7c72a0a4fd0e32a37a19fff6884818f96e63bbab4892c6c7e0c07c9af942b2e166937bf316b1f784045c827e2425ae0
7
- data.tar.gz: 65038d7abb35b021247cb6c3cebcf28b4136ae8263bb3e8bff25f40cc010d9504b7a583a749a27e24f1d5e4c73465956c768e62ae668edea21b79b932b1c4c1f
6
+ metadata.gz: 2f3a5dbf03c4ad9996810c807cd8ec7e51c07d261a219cccdade8430c0bbf77d58b6bf66e69298affaa93cf1062df389fa077ff9b73d1a518cac985949daa128
7
+ data.tar.gz: 66fe3b27cf9e1997022e66c1f8375411a730fafdc50facef46bbf8cf1ec1be166959f89e1cbd0b02ac6a86c1b552b656a2b14470a06e68bba683706d8e3bea38
@@ -0,0 +1,372 @@
1
+ // @ts-nocheck
2
+ /*
3
+ * Copyright (C) 2015 - present Instructure, Inc.
4
+ *
5
+ * This file is part of Canvas.
6
+ *
7
+ * Canvas is free software: you can redistribute it and/or modify it under
8
+ * the terms of the GNU Affero General Public License as published by the Free
9
+ * Software Foundation, version 3 of the License.
10
+ *
11
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
12
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
14
+ * details.
15
+ *
16
+ * You should have received a copy of the GNU Affero General Public License along
17
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+
20
+ import React, {MouseEventHandler, useCallback, useEffect, useRef, useState} from 'react'
21
+ import {useScope as useI18nScope} from '@canvas/i18n'
22
+ import axios from '@canvas/axios'
23
+
24
+ import DashboardCardAction from './DashboardCardAction'
25
+ import CourseActivitySummaryStore from './CourseActivitySummaryStore'
26
+ import DashboardCardMenu from './DashboardCardMenu'
27
+ import PublishButton from './PublishButton'
28
+ import {showConfirmUnfavorite} from './ConfirmUnfavoriteCourseModal'
29
+ import {showFlashError} from '@canvas/alerts/react/FlashAlert'
30
+ import instFSOptimizedImageUrl from '../util/instFSOptimizedImageUrl'
31
+ import {ConnectDragSource, ConnectDropTarget} from 'react-dnd'
32
+
33
+ const I18n = useI18nScope('dashcards')
34
+
35
+ export type DashboardCardHeaderHeroProps = {
36
+ image?: string
37
+ backgroundColor?: string
38
+ hideColorOverlays?: boolean
39
+ onClick?: MouseEventHandler<HTMLElement>
40
+ }
41
+
42
+ export const DashboardCardHeaderHero = ({
43
+ image,
44
+ backgroundColor,
45
+ hideColorOverlays,
46
+ onClick,
47
+ }: DashboardCardHeaderHeroProps) => {
48
+ if (image) {
49
+ return (
50
+ <div
51
+ className="ic-DashboardCard__header_image"
52
+ style={{backgroundImage: `url(${instFSOptimizedImageUrl(image, {x: 262, y: 146})})`}}
53
+ >
54
+ <div
55
+ className="ic-DashboardCard__header_hero"
56
+ style={{backgroundColor, opacity: hideColorOverlays ? 0 : 0.6}}
57
+ onClick={onClick}
58
+ aria-hidden="true"
59
+ />
60
+ </div>
61
+ )
62
+ }
63
+
64
+ return (
65
+ <div
66
+ className="ic-DashboardCard__header_hero"
67
+ style={{backgroundColor}}
68
+ onClick={onClick}
69
+ aria-hidden="true"
70
+ />
71
+ )
72
+ }
73
+
74
+ export type DashboardCardProps = {
75
+ id: string
76
+ backgroundColor?: string
77
+ shortName: string
78
+ originalName: string
79
+ courseCode: string
80
+ assetString: string
81
+ term?: string
82
+ href: string
83
+ links: any[] // TODO: improve type
84
+ image?: string
85
+ handleColorChange?: (color: string) => void
86
+ hideColorOverlays?: boolean
87
+ isDragging?: boolean
88
+ isFavorited?: boolean
89
+ connectDragSource?: ConnectDragSource
90
+ connectDropTarget?: ConnectDropTarget
91
+ moveCard?: (assetString: string, atIndex: number, callback: () => void) => void
92
+ onConfirmUnfavorite: (id: string) => void
93
+ totalCards?: number
94
+ position?: number | (() => number)
95
+ enrollmentType?: string
96
+ observee?: string
97
+ published?: boolean
98
+ canChangeCoursePublishState?: boolean
99
+ defaultView?: string
100
+ pagesUrl?: string
101
+ frontPageTitle?: string
102
+ onPublishedCourse?: (id: string) => void
103
+ }
104
+
105
+ export const DashboardCard = ({
106
+ id,
107
+ backgroundColor = '#394B58',
108
+ shortName,
109
+ originalName,
110
+ courseCode,
111
+ assetString,
112
+ term,
113
+ href,
114
+ links = [],
115
+ image,
116
+ handleColorChange = () => {},
117
+ hideColorOverlays,
118
+ isDragging,
119
+ isFavorited,
120
+ connectDragSource = c => c,
121
+ connectDropTarget = c => c,
122
+ moveCard = () => {},
123
+ onConfirmUnfavorite,
124
+ totalCards = 0,
125
+ position = 0,
126
+ enrollmentType,
127
+ observee,
128
+ published,
129
+ canChangeCoursePublishState,
130
+ defaultView,
131
+ pagesUrl,
132
+ frontPageTitle,
133
+ onPublishedCourse = () => {},
134
+ }: DashboardCardProps) => {
135
+ const handleNicknameChange = nickname => setNicknameInfo(getNicknameInfo(nickname))
136
+
137
+ const getNicknameInfo = (nickname: string) => ({
138
+ nickname,
139
+ originalName,
140
+ courseId: id,
141
+ onNicknameChange: handleNicknameChange,
142
+ })
143
+
144
+ const [nicknameInfo, setNicknameInfo] = useState(getNicknameInfo(shortName))
145
+ const [course, setCourse] = useState(CourseActivitySummaryStore.getStateForCourse(id))
146
+ const settingsToggle = useRef<HTMLButtonElement | null>()
147
+
148
+ const handleStoreChange = useCallback(
149
+ () => setCourse(CourseActivitySummaryStore.getStateForCourse(id)),
150
+ [id]
151
+ )
152
+
153
+ useEffect(() => {
154
+ CourseActivitySummaryStore.addChangeListener(handleStoreChange)
155
+ return () => CourseActivitySummaryStore.removeChangeListener(handleStoreChange)
156
+ }, [handleStoreChange])
157
+
158
+ // ===============
159
+ // ACTIONS
160
+ // ===============
161
+
162
+ const getCardPosition = () => (typeof position === 'function' ? position() : position)
163
+
164
+ const headerClick: MouseEventHandler = e => {
165
+ e.preventDefault()
166
+ window.location.assign(href)
167
+ }
168
+
169
+ const handleMove = (asset: string, atIndex: number) => {
170
+ if (moveCard) {
171
+ moveCard(asset, atIndex, () => settingsToggle.current?.focus())
172
+ }
173
+ }
174
+
175
+ const handleUnfavorite = () => {
176
+ const modalProps = {
177
+ courseId: id,
178
+ courseName: originalName,
179
+ onConfirm: removeCourseFromFavorites,
180
+ }
181
+ showConfirmUnfavorite(modalProps)
182
+ }
183
+
184
+ // ===============
185
+ // HELPERS
186
+ // ===============
187
+
188
+ const unreadCount = (icon: string, stream?: any[]) => {
189
+ const activityType = {
190
+ 'icon-announcement': 'Announcement',
191
+ 'icon-assignment': 'Message',
192
+ 'icon-discussion': 'DiscussionTopic',
193
+ }[icon]
194
+
195
+ const itemStream = stream || []
196
+ const streamItem = itemStream.find(
197
+ item =>
198
+ // only return 'Message' type if category is 'Due Date' (for assignments)
199
+ item.type === activityType &&
200
+ (activityType !== 'Message' || item.notification_category === I18n.t('Due Date'))
201
+ )
202
+
203
+ // TODO: unread count is always 0 for assignments (see CNVS-21227)
204
+ return streamItem ? streamItem.unread_count : 0
205
+ }
206
+
207
+ const calculateMenuOptions = () => {
208
+ const cardPosition = getCardPosition()
209
+ const isFirstCard = cardPosition === 0
210
+ const isLastCard = cardPosition === totalCards - 1
211
+ return {
212
+ canMoveLeft: !isFirstCard,
213
+ canMoveRight: !isLastCard,
214
+ canMoveToBeginning: !isFirstCard,
215
+ canMoveToEnd: !isLastCard,
216
+ }
217
+ }
218
+
219
+ const removeCourseFromFavorites = () => {
220
+ const url = `/api/v1/users/self/favorites/courses/${id}`
221
+ axios
222
+ .delete(url)
223
+ .then(response => {
224
+ if (response.status === 200) {
225
+ onConfirmUnfavorite(id)
226
+ }
227
+ })
228
+ .catch(() =>
229
+ showFlashError(I18n.t('We were unable to remove this course from your favorites.'))
230
+ )
231
+ }
232
+
233
+ const updatePublishedCourse = () => {
234
+ if (onPublishedCourse) onPublishedCourse(id)
235
+ }
236
+
237
+ // ===============
238
+ // RENDERING
239
+ // ===============
240
+
241
+ const linksForCard = () =>
242
+ links.map(link => {
243
+ if (link.hidden) return null
244
+
245
+ const screenReaderLabel = `${link.label} - ${nicknameInfo.nickname}`
246
+ return (
247
+ <DashboardCardAction
248
+ unreadCount={unreadCount(link.icon, course?.stream)}
249
+ iconClass={link.icon}
250
+ linkClass={link.css_class}
251
+ path={link.path}
252
+ screenReaderLabel={screenReaderLabel}
253
+ key={link.path}
254
+ />
255
+ )
256
+ })
257
+
258
+ const renderHeaderButton = () => {
259
+ const reorderingProps = {
260
+ handleMove,
261
+ currentPosition: getCardPosition(),
262
+ lastPosition: totalCards - 1,
263
+ menuOptions: calculateMenuOptions(),
264
+ }
265
+
266
+ return (
267
+ <div>
268
+ <div
269
+ className="ic-DashboardCard__header-button-bg"
270
+ style={{backgroundColor, opacity: hideColorOverlays ? 1 : 0}}
271
+ />
272
+ <DashboardCardMenu
273
+ afterUpdateColor={(c: string) => handleColorChange(`#${c}`)}
274
+ currentColor={backgroundColor}
275
+ nicknameInfo={nicknameInfo}
276
+ assetString={assetString}
277
+ onUnfavorite={handleUnfavorite}
278
+ isFavorited={isFavorited}
279
+ {...reorderingProps}
280
+ trigger={
281
+ <button
282
+ type="button"
283
+ className="Button Button--icon-action-rev ic-DashboardCard__header-button"
284
+ ref={c => {
285
+ settingsToggle.current = c
286
+ }}
287
+ >
288
+ <i className="icon-more" aria-hidden="true" />
289
+ <span className="screenreader-only">
290
+ {I18n.t('Choose a color or course nickname or move course card for %{course}', {
291
+ course: nicknameInfo.nickname,
292
+ })}
293
+ </span>
294
+ </button>
295
+ }
296
+ />
297
+ </div>
298
+ )
299
+ }
300
+
301
+ const dashboardCard = (
302
+ <div
303
+ className="ic-DashboardCard"
304
+ style={{opacity: isDragging ? 0 : 1}}
305
+ aria-label={originalName}
306
+ >
307
+ <div className="ic-DashboardCard__header" style={{backgroundColor}}>
308
+ <span className="screenreader-only">
309
+ {image
310
+ ? I18n.t('Course image for %{course}', {course: nicknameInfo.nickname})
311
+ : I18n.t('Course card color region for %{course}', {
312
+ course: nicknameInfo.nickname,
313
+ })}
314
+ </span>
315
+ <DashboardCardHeaderHero
316
+ image={image}
317
+ backgroundColor={backgroundColor}
318
+ hideColorOverlays={hideColorOverlays}
319
+ onClick={headerClick}
320
+ />
321
+ <a href={href} className="ic-DashboardCard__link">
322
+ <div className="ic-DashboardCard__header_content" style={{backgroundColor}}>
323
+ <h5
324
+ className="ic-DashboardCard__header-title ellipsis"
325
+ title={originalName}
326
+ style={{color: backgroundColor}}
327
+ >
328
+ <span style={{color: backgroundColor}}>{nicknameInfo.nickname}</span>
329
+ </h5>
330
+ <div
331
+ className="ic-DashboardCard__header-term ellipsis"
332
+ title={term}
333
+ style={{color: backgroundColor}}
334
+ >
335
+ {term || null}
336
+ </div>
337
+ {enrollmentType === 'ObserverEnrollment' && observee && (
338
+ <div
339
+ className="ic-DashboardCard__header-term ellipsis"
340
+ title={observee}
341
+ style={{color: backgroundColor}}
342
+ >
343
+ {I18n.t('Observing: %{observee}', {observee})}
344
+ </div>
345
+ )}
346
+ </div>
347
+ </a>
348
+ {!published && canChangeCoursePublishState && (
349
+ <PublishButton
350
+ courseNickname={nicknameInfo.nickname}
351
+ defaultView={defaultView}
352
+ pagesUrl={pagesUrl}
353
+ frontPageTitle={frontPageTitle}
354
+ courseId={id}
355
+ />
356
+ )}
357
+ {renderHeaderButton()}
358
+ </div>
359
+ <nav
360
+ className="ic-DashboardCard__action-container"
361
+ aria-label={I18n.t('Actions for %{course}', {course: nicknameInfo.nickname})}
362
+ style={{color: backgroundColor}}
363
+ >
364
+ {linksForCard()}
365
+ </nav>
366
+ </div>
367
+ )
368
+
369
+ return connectDragSource(connectDropTarget(dashboardCard))
370
+ }
371
+
372
+ export default DashboardCard
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Explorak5CanvasLogin
4
- VERSION = "3.2.0"
4
+ VERSION = "3.4.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: explorak5_canvas_login
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Explorak5
@@ -23,6 +23,7 @@ files:
23
23
  - lib/explorak5_canvas_login/app/views/login/_login_content.html.erb
24
24
  - lib/explorak5_canvas_login/app/views/login/_logout_confirm_content.html.erb
25
25
  - lib/explorak5_canvas_login/app/views/navbar/_new_nav_header.html.erb
26
+ - lib/explorak5_canvas_login/ui/dashboard-card/react/DashboardContent.tsx
26
27
  - lib/explorak5_canvas_login/version.rb
27
28
  homepage: https://github.com/kevin523523/explorak5_canvas_login
28
29
  licenses: []