1- // @ts -nocheck
21import DOMPurify from 'dompurify' ;
32import markdownIt from 'markdown-it' ;
43
@@ -23,17 +22,35 @@ import { getDefaultBackgroundClass } from 'components/cardbuilder/utils/builder'
2322
2423import './login.scss' ;
2524
25+ /**
26+ * @typedef {import('jellyfin-apiclient').ApiClient } ApiClient
27+ * @typedef {import('@jellyfin/sdk/lib/generated-client').AuthenticationResult } AuthenticationResult
28+ * @typedef {import('@jellyfin/sdk/lib/generated-client').UserDto } UserDto
29+ * @typedef {import('@jellyfin/sdk/lib/generated-client').BrandingOptionsDto } BrandingOptionsDto
30+ */
31+
2632const enableFocusTransform = ! browser . slow && ! browser . edge ;
2733
34+ /**
35+ * @param {HTMLElement } page
36+ * @param {ApiClient } apiClient
37+ * @param {string } url
38+ * @param {string } username
39+ * @param {string } password
40+ */
2841function authenticateUserByName ( page , apiClient , url , username , password ) {
2942 loading . show ( ) ;
30- apiClient . authenticateUserByName ( username , password ) . then ( function ( result ) {
31- const user = result . User ;
43+ apiClient . authenticateUserByName ( username , password ) . then (
44+ /** @param {AuthenticationResult } result */
45+ function ( result ) {
46+ const user = /** @type {UserDto } */ ( result . User ) ;
3247 loading . hide ( ) ;
3348
3449 onLoginSuccessful ( user . Id , result . AccessToken , apiClient , url ) ;
35- } , function ( response ) {
36- page . querySelector ( '#txtManualPassword' ) . value = '' ;
50+ } ,
51+ /** @param {{ status: number } } response */
52+ function ( response ) {
53+ /** @type {HTMLInputElement } */ ( page . querySelector ( '#txtManualPassword' ) ) . value = '' ;
3754 loading . hide ( ) ;
3855
3956 const UnauthorizedOrForbidden = [ 401 , 403 ] ;
@@ -49,8 +66,14 @@ function authenticateUserByName(page, apiClient, url, username, password) {
4966 } ) ;
5067}
5168
69+ /**
70+ * @param {ApiClient } apiClient
71+ * @param {string } targetUrl
72+ */
5273function authenticateQuickConnect ( apiClient , targetUrl ) {
5374 const url = apiClient . getUrl ( '/QuickConnect/Initiate' ) ;
75+ // TODO: I don't think the `true` in ajax() does anything here since it's never used in src/utils/fetch.js:84
76+ // @ts -expect-error
5477 apiClient . ajax ( { type : 'POST' , url } , true ) . then ( res => res . json ( ) ) . then ( function ( json ) {
5578 if ( ! json . Secret || ! json . Code ) {
5679 console . error ( 'Malformed quick connect response' , json ) ;
@@ -82,7 +105,8 @@ function authenticateQuickConnect(apiClient, targetUrl) {
82105 }
83106
84107 const result = await apiClient . quickConnect ( data . Secret ) ;
85- onLoginSuccessful ( result . User . Id , result . AccessToken , apiClient , targetUrl ) ;
108+ const user = /** @type {UserDto } */ ( result . User ) ;
109+ onLoginSuccessful ( user . Id , result . AccessToken , apiClient , targetUrl ) ;
86110 } , function ( e ) {
87111 clearInterval ( interval ) ;
88112
@@ -113,30 +137,50 @@ function authenticateQuickConnect(apiClient, targetUrl) {
113137 } ) ;
114138}
115139
140+ /**
141+ * @param {string | null | undefined } id
142+ * @param {string | null | undefined } accessToken
143+ * @param {ApiClient } apiClient
144+ * @param {string } url
145+ */
116146function onLoginSuccessful ( id , accessToken , apiClient , url ) {
117147 Dashboard . onServerChanged ( id , accessToken , apiClient ) ;
118148 Dashboard . navigate ( url || 'home' ) ;
119149}
120150
151+ /**
152+ * Hide the user cards (i.e., the '.visualLoginForm') and show the manual form
153+ * @param {HTMLElement } context
154+ * @param {boolean } showCancel
155+ * @param {boolean? } [focusPassword]
156+ */
121157function showManualForm ( context , showCancel , focusPassword ) {
122- context . querySelector ( '.chkRememberLogin' ) . checked = appSettings . enableAutoLogin ( ) ;
123- context . querySelector ( '.manualLoginForm' ) . classList . remove ( 'hide' ) ;
124- context . querySelector ( '.visualLoginForm' ) . classList . add ( 'hide' ) ;
125- context . querySelector ( '.btnManual' ) . classList . add ( 'hide' ) ;
158+ /** @type { HTMLInputElement }*/ ( context . querySelector ( '.chkRememberLogin' ) ) . checked = appSettings . enableAutoLogin ( ) ;
159+ /** @type { Element } */ ( context . querySelector ( '.manualLoginForm' ) ) . classList . remove ( 'hide' ) ;
160+ /** @type { Element } */ ( context . querySelector ( '.visualLoginForm' ) ) . classList . add ( 'hide' ) ;
161+ /** @type { Element } */ ( context . querySelector ( '.btnManual' ) ) . classList . add ( 'hide' ) ;
126162
127163 if ( focusPassword ) {
128- context . querySelector ( '#txtManualPassword' ) . focus ( ) ;
164+ /** @type { HTMLInputElement }*/ ( context . querySelector ( '#txtManualPassword' ) ) . focus ( ) ;
129165 } else {
130- context . querySelector ( '#txtManualName' ) . focus ( ) ;
166+ /** @type { HTMLInputElement }*/ ( context . querySelector ( '#txtManualName' ) ) . focus ( ) ;
131167 }
132168
133169 if ( showCancel ) {
134- context . querySelector ( '.btnCancel' ) . classList . remove ( 'hide' ) ;
170+ /** @type { Element } */ ( context . querySelector ( '.btnCancel' ) ) . classList . remove ( 'hide' ) ;
135171 } else {
136- context . querySelector ( '.btnCancel' ) . classList . add ( 'hide' ) ;
172+ /** @type { Element } */ ( context . querySelector ( '.btnCancel' ) ) . classList . add ( 'hide' ) ;
137173 }
138174}
139175
176+ /**
177+ * Transform UserDto objects into html and inject them into the user div (#divUsers).
178+ * This creates the user icons for users that are not hidden on the login screen.
179+ *
180+ * @param {HTMLElement } context
181+ * @param {ApiClient } apiClient
182+ * @param {UserDto[] } users
183+ */
140184function loadUserList ( context , apiClient , users ) {
141185 let html = '' ;
142186
@@ -161,7 +205,7 @@ function loadUserList(context, apiClient, users) {
161205 let imgUrl ;
162206
163207 if ( user . PrimaryImageTag ) {
164- imgUrl = apiClient . getUserImageUrl ( user . Id , {
208+ imgUrl = apiClient . getUserImageUrl ( /** @type { string } */ ( user . Id ) , {
165209 width : 300 ,
166210 tag : user . PrimaryImageTag ,
167211 type : 'Primary'
@@ -183,20 +227,27 @@ function loadUserList(context, apiClient, users) {
183227 html += '</button>' ;
184228 }
185229
186- context . querySelector ( '#divUsers' ) . innerHTML = html ;
230+ /** @type { Element } */ ( context . querySelector ( '#divUsers' ) ) . innerHTML = html ;
187231}
188232
233+ /**
234+ * @param {HTMLElement } view
235+ * @param {Object.<string, string> } params
236+ */
189237export default function ( view , params ) {
238+ /** @returns {ApiClient } */
190239 function getApiClient ( ) {
191240 const serverId = params . serverid ;
192241
193242 if ( serverId ) {
194243 return ServerConnections . getOrCreateApiClient ( serverId ) ;
195244 }
196245
246+ // @ts -expect-error
197247 return ApiClient ;
198248 }
199249
250+ /** @returns {string } */
200251 function getTargetUrl ( ) {
201252 if ( params . url ) {
202253 try {
@@ -210,56 +261,63 @@ export default function (view, params) {
210261 }
211262
212263 function showVisualForm ( ) {
213- view . querySelector ( '.visualLoginForm' ) . classList . remove ( 'hide' ) ;
214- view . querySelector ( '.manualLoginForm' ) . classList . add ( 'hide' ) ;
215- view . querySelector ( '.btnManual' ) . classList . remove ( 'hide' ) ;
264+ /** @type { Element } */ ( view . querySelector ( '.visualLoginForm' ) ) . classList . remove ( 'hide' ) ;
265+ /** @type { Element } */ ( view . querySelector ( '.manualLoginForm' ) ) . classList . add ( 'hide' ) ;
266+ /** @type { Element } */ ( view . querySelector ( '.btnManual' ) ) . classList . remove ( 'hide' ) ;
216267
217268 import ( 'components/autoFocuser' ) . then ( ( { default : autoFocuser } ) => {
218269 autoFocuser . autoFocus ( view ) ;
219270 } ) ;
220271 }
221272
222- view . querySelector ( '#divUsers' ) . addEventListener ( 'click' , function ( e ) {
223- const card = dom . parentWithClass ( e . target , 'card' ) ;
273+ // On user selected one of the user cards
274+ /** @type {Element } */ ( view . querySelector ( '#divUsers' ) ) . addEventListener ( 'click' , function ( e ) {
275+ const card = dom . parentWithClass ( /** @type {HTMLElement } */ ( e . target ) , 'card' ) ;
224276 const cardContent = card ? card . querySelector ( '.cardContent' ) : null ;
225277
226278 if ( cardContent ) {
227279 const context = view ;
228280 const id = cardContent . getAttribute ( 'data-userid' ) ;
229- const name = cardContent . getAttribute ( 'data-username' ) ;
281+ const name = /** @type { string } */ ( cardContent . getAttribute ( 'data-username' ) ) ;
230282 const haspw = cardContent . getAttribute ( 'data-haspw' ) ;
231283
232284 if ( id === 'manual' ) {
233- context . querySelector ( '#txtManualName' ) . value = '' ;
285+ /** @type { HTMLInputElement } */ ( context . querySelector ( '#txtManualName' ) ) . value = '' ;
234286 showManualForm ( context , true ) ;
235287 } else if ( haspw == 'false' ) {
236288 authenticateUserByName ( context , getApiClient ( ) , getTargetUrl ( ) , name , '' ) ;
237289 } else {
238- context . querySelector ( '#txtManualName' ) . value = name ;
239- context . querySelector ( '#txtManualPassword' ) . value = '' ;
290+ /** @type { HTMLInputElement } */ ( context . querySelector ( '#txtManualName' ) ) . value = name ;
291+ /** @type { HTMLInputElement } */ ( context . querySelector ( '#txtManualPassword' ) ) . value = '' ;
240292 showManualForm ( context , true , true ) ;
241293 }
242294 }
243295 } ) ;
244- view . querySelector ( '.manualLoginForm' ) . addEventListener ( 'submit' , function ( e ) {
245- appSettings . enableAutoLogin ( view . querySelector ( '.chkRememberLogin' ) . checked ) ;
246- authenticateUserByName ( view , getApiClient ( ) , getTargetUrl ( ) , view . querySelector ( '#txtManualName' ) . value , view . querySelector ( '#txtManualPassword' ) . value ) ;
296+ /** @type {Element } */ ( view . querySelector ( '.manualLoginForm' ) ) . addEventListener ( 'submit' , function ( e ) {
297+ appSettings . enableAutoLogin ( /** @type {HTMLInputElement } */ ( view . querySelector ( '.chkRememberLogin' ) ) . checked ) ;
298+ authenticateUserByName (
299+ view ,
300+ getApiClient ( ) ,
301+ getTargetUrl ( ) ,
302+ /** @type {HTMLInputElement } */ ( view . querySelector ( '#txtManualName' ) ) . value ,
303+ /** @type {HTMLInputElement } */ ( view . querySelector ( '#txtManualPassword' ) ) . value
304+ ) ;
247305 e . preventDefault ( ) ;
248306 return false ;
249307 } ) ;
250- view . querySelector ( '.btnForgotPassword' ) . addEventListener ( 'click' , function ( ) {
308+ /** @type { Element } */ ( view . querySelector ( '.btnForgotPassword' ) ) . addEventListener ( 'click' , function ( ) {
251309 Dashboard . navigate ( 'forgotpassword' ) ;
252310 } ) ;
253- view . querySelector ( '.btnCancel' ) . addEventListener ( 'click' , showVisualForm ) ;
254- view . querySelector ( '.btnQuick' ) . addEventListener ( 'click' , function ( ) {
311+ /** @type { Element } */ ( view . querySelector ( '.btnCancel' ) ) . addEventListener ( 'click' , showVisualForm ) ;
312+ /** @type { Element } */ ( view . querySelector ( '.btnQuick' ) ) . addEventListener ( 'click' , function ( ) {
255313 authenticateQuickConnect ( getApiClient ( ) , getTargetUrl ( ) ) ;
256314 return false ;
257315 } ) ;
258- view . querySelector ( '.btnManual' ) . addEventListener ( 'click' , function ( ) {
259- view . querySelector ( '#txtManualName' ) . value = '' ;
316+ /** @type { Element } */ ( view . querySelector ( '.btnManual' ) ) . addEventListener ( 'click' , function ( ) {
317+ /** @type { HTMLInputElement } */ ( view . querySelector ( '#txtManualName' ) ) . value = '' ;
260318 showManualForm ( view , true ) ;
261319 } ) ;
262- view . querySelector ( '.btnSelectServer' ) . addEventListener ( 'click' , function ( ) {
320+ /** @type { Element } */ ( view . querySelector ( '.btnSelectServer' ) ) . addEventListener ( 'click' , function ( ) {
263321 Dashboard . selectServer ( ) ;
264322 } ) ;
265323
@@ -268,15 +326,15 @@ export default function (view, params) {
268326 libraryMenu . setTransparentMenu ( true ) ;
269327
270328 if ( ! appHost . supports ( AppFeature . MultiServer ) ) {
271- view . querySelector ( '.btnSelectServer' ) . classList . add ( 'hide' ) ;
329+ /** @type { Element } */ ( view . querySelector ( '.btnSelectServer' ) ) . classList . add ( 'hide' ) ;
272330 }
273331
274332 const apiClient = getApiClient ( ) ;
275333
276334 apiClient . getQuickConnect ( 'Enabled' )
277335 . then ( enabled => {
278336 if ( enabled === true ) {
279- view . querySelector ( '.btnQuick' ) . classList . remove ( 'hide' ) ;
337+ /** @type { Element } */ ( view . querySelector ( '.btnQuick' ) ) . classList . remove ( 'hide' ) ;
280338 }
281339 } )
282340 . catch ( ( ) => {
@@ -288,14 +346,14 @@ export default function (view, params) {
288346 showVisualForm ( ) ;
289347 loadUserList ( view , apiClient , users ) ;
290348 } else {
291- view . querySelector ( '#txtManualName' ) . value = '' ;
349+ /** @type { HTMLInputElement } */ ( view . querySelector ( '#txtManualName' ) ) . value = '' ;
292350 showManualForm ( view , false , false ) ;
293351 }
294352 } ) . catch ( ) . then ( function ( ) {
295353 loading . hide ( ) ;
296354 } ) ;
297- apiClient . getJSON ( apiClient . getUrl ( 'Branding/Configuration' ) ) . then ( function ( options ) {
298- const loginDisclaimer = view . querySelector ( '.loginDisclaimer' ) ;
355+ apiClient . getJSON ( apiClient . getUrl ( 'Branding/Configuration' ) ) . then ( /** @param { BrandingOptionsDto } options */ function ( options ) {
356+ const loginDisclaimer = /** @type { Element } */ ( view . querySelector ( '.loginDisclaimer' ) ) ;
299357
300358 // eslint-disable-next-line sonarjs/disabled-auto-escaping
301359 loginDisclaimer . innerHTML = DOMPurify . sanitize ( markdownIt ( { html : true } ) . render ( options . LoginDisclaimer || '' ) ) ;
0 commit comments