Skip to content

Commit a41f48b

Browse files
committed
Add type annotations to login/index.js
Fix several ts-expect-error fix HTMLInputElement errors fix anys
1 parent 00f9af6 commit a41f48b

1 file changed

Lines changed: 97 additions & 39 deletions

File tree

  • src/apps/legacy/controllers/session/login

src/apps/legacy/controllers/session/login/index.js

Lines changed: 97 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// @ts-nocheck
21
import DOMPurify from 'dompurify';
32
import markdownIt from 'markdown-it';
43

@@ -23,17 +22,35 @@ import { getDefaultBackgroundClass } from 'components/cardbuilder/utils/builder'
2322

2423
import './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+
2632
const 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+
*/
2841
function 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+
*/
5273
function 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+
*/
116146
function 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+
*/
121157
function 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+
*/
140184
function 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+
*/
189237
export 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

Comments
 (0)