فهرست منبع

Improve UI/UX install process (#5147)

* less buttons on step 1

* add form-group

* jump to next step as link not as button

* improve navigation bar HTML structure

* sync password-show button with extra.js

* fix CSS

* i18n: step 4: split text strings for help text (max chars default user)

* show menu button if mobile view

* improve header layout with empty div

* step 5: button in form-actions

* improve buttons in goup-controls

* Favicon added

* Button: Font color  hover btn-attention

* install check step: add subtitles

* fix .btn

* improve tabindex

* improve showPW_this()
maTh 3 سال پیش
والد
کامیت
27c7367534

+ 4 - 1
app/i18n/cz/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Obecná nastavení byla uložena.',
 	),
 	'congratulations' => 'Gratulujeme!',
-	'default_user' => 'Uživatelské jméno výchozího uživatele <small>(maximálně 16 alfanumerických znaků)</small>',
+	'default_user' => array(
+		'_' => 'Uživatelské jméno výchozího uživatele',
+		'max_char' => 'maximálně 16 alfanumerických znaků',
+	),
 	'fix_errors_before' => 'Opravte prosím všechny chyby před přechodem na další krok.',
 	'javascript_is_better' => 'Práce s FreshRSS je příjemnější se zapnutým JavaScript',
 	'js' => array(

+ 4 - 1
app/i18n/de/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Die allgemeine Konfiguration ist gespeichert worden.',
 	),
 	'congratulations' => 'Glückwunsch!',
-	'default_user' => 'Benutzername des Standardbenutzers <small>(maximal 16 alphanumerische Zeichen)</small>',
+	'default_user' => array(
+		'_' => 'Benutzername des Standardbenutzers',
+		'max_char' => 'maximal 16 alphanumerische Zeichen',
+	),
 	'fix_errors_before' => 'Bitte den Fehler korrigieren, bevor zum nächsten Schritt gesprungen wird.',
 	'javascript_is_better' => 'FreshRSS ist ansprechender mit aktiviertem JavaScript',
 	'js' => array(

+ 4 - 1
app/i18n/el/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Οι γενικές ρυθμίσεις αποθηκεύτηκαν.',
 	),
 	'congratulations' => 'Συγχαρητήρια!',
-	'default_user' => 'Όνομα χρήστη για τον προεπιλεγμένο χρήστη <small>(μέγιστο 16 αλφαριθμητικοί χαρακτήρες)</small>',
+	'default_user' => array(
+		'_' => 'Όνομα χρήστη για τον προεπιλεγμένο χρήστη',
+		'max_char' => 'μέγιστο 16 αλφαριθμητικοί χαρακτήρες',
+	),
 	'fix_errors_before' => 'Παρακαλούμε διορθώστε τα σφάλματα πριν συνεχίσετε στο επόμενο βήμα.',
 	'javascript_is_better' => 'Το FreshRSS είναι πιο ευχάριστο με ενεργοποιημένη την JavaScript',
 	'js' => array(

+ 4 - 1
app/i18n/en-us/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'General configuration has been saved.',	// IGNORE
 	),
 	'congratulations' => 'Congratulations!',	// IGNORE
-	'default_user' => 'Username of the default user <small>(maximum 16 alphanumeric characters)</small>',	// IGNORE
+	'default_user' => array(
+		'_' => 'Username of the default user',	// IGNORE
+		'max_char' => 'maximum 16 alphanumeric characters',	// IGNORE
+	),
 	'fix_errors_before' => 'Please fix errors before continuing to the next step.',	// IGNORE
 	'javascript_is_better' => 'FreshRSS is more pleasant with JavaScript enabled',	// IGNORE
 	'js' => array(

+ 4 - 1
app/i18n/en/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'General configuration has been saved.',
 	),
 	'congratulations' => 'Congratulations!',
-	'default_user' => 'Username of the default user <small>(maximum 16 alphanumeric characters)</small>',
+	'default_user' => array(
+		'_' => 'Username of the default user',
+		'max_char' => 'maximum 16 alphanumeric characters',
+	),
 	'fix_errors_before' => 'Please fix errors before continuing to the next step.',
 	'javascript_is_better' => 'FreshRSS is more pleasant with JavaScript enabled',
 	'js' => array(

+ 4 - 1
app/i18n/es/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'La configuración general se ha guardado.',
 	),
 	'congratulations' => '¡Enhorabuena!',
-	'default_user' => 'Nombre de usuario para el usuario por defecto <small>(máximo de 16 caracteres alfanuméricos)</small>',
+	'default_user' => array(
+		'_' => 'Nombre de usuario para el usuario por defecto',
+		'max_char' => 'máximo de 16 caracteres alfanuméricos',
+	),
 	'fix_errors_before' => 'Por favor, soluciona los errores detectados antes de proceder con el siguiente paso.',
 	'javascript_is_better' => 'FreshRSS funciona mejor con JavaScript activado',
 	'js' => array(

+ 4 - 1
app/i18n/fr/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'La configuration générale a été enregistrée.',
 	),
 	'congratulations' => 'Félicitations !',
-	'default_user' => 'Nom de l’utilisateur par défaut <small>(16 caractères alphanumériques maximum)</small>',
+	'default_user' => array(
+		'_' => 'Nom de l’utilisateur par défaut',
+		'max_char' => '16 caractères alphanumériques maximum',
+	),
 	'fix_errors_before' => 'Veuillez corriger les erreurs avant de passer à l’étape suivante.',
 	'javascript_is_better' => 'FreshRSS est plus agréable à utiliser avec JavaScript activé',
 	'js' => array(

+ 4 - 1
app/i18n/he/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'ההגדרות הכלליות נשמרו.',
 	),
 	'congratulations' => 'מזל טוב!',
-	'default_user' => 'שם המשתמש של משתמש ברירת המחדל <small>(לכל היותר 16 תווים אלפאנומריים)</small>',
+	'default_user' => array(
+		'_' => 'שם המשתמש של משתמש ברירת המחדל',
+		'max_char' => 'לכל היותר 16 תווים אלפאנומריים',
+	),
 	'fix_errors_before' => 'יש לתקן את השגיאות לפני המעבר לשלב הבא.',
 	'javascript_is_better' => 'FreshRSS מעדיף שתאפשרו JavaScript',
 	'js' => array(

+ 4 - 1
app/i18n/id/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'General configuration has been saved.',	// TODO
 	),
 	'congratulations' => 'Congratulations!',	// TODO
-	'default_user' => 'Username of the default user <small>(maximum 16 alphanumeric characters)</small>',	// TODO
+	'default_user' => array(
+		'_' => 'Username of the default user',	// TODO
+		'max_char' => 'maximum 16 alphanumeric characters',	// TODO
+	),
 	'fix_errors_before' => 'Please fix errors before continuing to the next step.',	// TODO
 	'javascript_is_better' => 'FreshRSS is more pleasant with JavaScript enabled',	// TODO
 	'js' => array(

+ 4 - 1
app/i18n/it/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Configurazioni generali salvate.',
 	),
 	'congratulations' => 'Congratulazione!',
-	'default_user' => 'Username utente predefinito <small>(massimo 16 caratteri alfanumerici)</small>',
+	'default_user' => array(
+		'_' => 'Username utente predefinito',
+		'max_char' => 'massimo 16 caratteri alfanumerici',
+	),
 	'fix_errors_before' => 'Per favore correggi gli errori prima di passare al passaggio successivo.',
 	'javascript_is_better' => 'FreshRSS funziona meglio con JavaScript abilitato',
 	'js' => array(

+ 4 - 1
app/i18n/ja/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => '一般設定は保存されました。',
 	),
 	'congratulations' => 'おめでとうございます!',
-	'default_user' => 'デフォルトのユーザー名 <small>(最大16文字の英数字)</small>',
+	'default_user' => array(
+		'_' => 'デフォルトのユーザー名',
+		'max_char' => '最大16文字の英数字',
+	),
 	'fix_errors_before' => 'エラーを次のステップへ移る前に修正してください。',
 	'javascript_is_better' => 'FreshRSS はJavascriptが有効だとより快適にご利用いただけます。',
 	'js' => array(

+ 4 - 1
app/i18n/ko/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => '일반 설정이 저장되었습니다.',
 	),
 	'congratulations' => '축하합니다!',
-	'default_user' => '기본 사용자 이름<small>(알파벳과 숫자를 포함할 수 있고 최대 16 글자)</small>',
+	'default_user' => array(
+		'_' => '기본 사용자 이름',
+		'max_char' => '알파벳과 숫자를 포함할 수 있고 최대 16 글자',
+	),
 	'fix_errors_before' => '다음 단계로 가기 전에 문제를 해결하세요.',
 	'javascript_is_better' => 'FreshRSS는 자바스크립트를 사용할 때 더욱 쾌적하고 강력합니다',
 	'js' => array(

+ 4 - 1
app/i18n/nl/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Algemene configuratie is opgeslagen.',
 	),
 	'congratulations' => 'Gefeliciteerd!',
-	'default_user' => 'Gebruikersnaam van de standaardgebruiker <small>(maximaal 16 alfanumerieke tekens)</small>',
+	'default_user' => array(
+		'_' => 'Gebruikersnaam van de standaardgebruiker',
+		'max_char' => 'maximaal 16 alfanumerieke tekens',
+	),
 	'fix_errors_before' => 'Repareer fouten alvorens U naar de volgende stap gaat.',
 	'javascript_is_better' => 'FreshRSS werkt beter JavaScript ingeschakeld',
 	'js' => array(

+ 4 - 1
app/i18n/oc/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'La configuracion generala es enregistrada.',
 	),
 	'congratulations' => 'Òsca !',
-	'default_user' => 'Nom d’utilizaire per defaut <small>16 caractèrs alfanumerics maximum)</small>',
+	'default_user' => array(
+		'_' => 'Nom d’utilizaire per defaut',
+		'max_char' => '16 caractèrs alfanumerics maximum',
+	),
 	'fix_errors_before' => 'Mercés de corregir las errors seguentas abans de contunhar.',
 	'javascript_is_better' => 'FreshRSS es mai agradable amb lo JavaScript activat',
 	'js' => array(

+ 4 - 1
app/i18n/pl/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'General configuration has been saved.',	// TODO
 	),
 	'congratulations' => 'Congratulations!',	// TODO
-	'default_user' => 'Username of the default user <small>(maximum 16 alphanumeric characters)</small>',	// TODO
+	'default_user' => array(
+		'_' => 'Username of the default user',	// TODO
+		'max_char' => 'maximum 16 alphanumeric characters',	// TODO
+	),
 	'fix_errors_before' => 'Please fix errors before continuing to the next step.',	// TODO
 	'javascript_is_better' => 'FreshRSS is more pleasant with JavaScript enabled',	// TODO
 	'js' => array(

+ 4 - 1
app/i18n/pt-br/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Configurações gerais foram salvas.',
 	),
 	'congratulations' => 'Parabéns!',
-	'default_user' => 'Usuário do usuário padrão <small>(máximo de 16 caracteres alfanuméricos)</small>',
+	'default_user' => array(
+		'_' => 'Usuário do usuário padrão',
+		'max_char' => 'máximo de 16 caracteres alfanuméricos',
+	),
 	'fix_errors_before' => 'Por favor solucione os erros antes de ir para o próximo passo.',
 	'javascript_is_better' => 'O FreshRSS é mais agradável com o JavaScript ativo',
 	'js' => array(

+ 4 - 1
app/i18n/ru/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Общие настройки сохранены.',
 	),
 	'congratulations' => 'Поздравляем!',
-	'default_user' => 'Имя пользователя по умолчанию <small>(не более 16 буквенно-цифровых символов)</small>',
+	'default_user' => array(
+		'_' => 'Имя пользователя по умолчанию',
+		'max_char' => 'не более 16 буквенно-цифровых символов',
+	),
 	'fix_errors_before' => 'Пожалуйста, исправьте ошибки, прежде чем перейти к следующему шагу.',
 	'javascript_is_better' => 'Пользоваться FreshRSS приятнее с включённым JavaScript',
 	'js' => array(

+ 4 - 1
app/i18n/sk/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Hlavné nastavenia boli uložené.',
 	),
 	'congratulations' => 'Nastavenia!',
-	'default_user' => 'Hlavné používateľské meno <small>(najviac 16 alfanumerických znakov)</small>',
+	'default_user' => array(
+		'_' => 'Hlavné používateľské meno',
+		'max_char' => 'najviac 16 alfanumerických znakov',
+	),
 	'fix_errors_before' => 'Prosím, pred pokračovaním opravte chyby.',
 	'javascript_is_better' => 'FreshRSS si užijete viac, keď povolíte JavaScript',
 	'js' => array(

+ 4 - 1
app/i18n/tr/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => 'Genel yapılandırma ayarları kayıt edildi.',
 	),
 	'congratulations' => 'Tebrikler!',
-	'default_user' => 'Öntanımlı kullanıcı adı <small>(en fazla 16 alfanümerik karakter)</small>',
+	'default_user' => array(
+		'_' => 'Öntanımlı kullanıcı adı',
+		'max_char' => 'en fazla 16 alfanümerik karakter',
+	),
 	'fix_errors_before' => 'Lütfen sonraki adıma geçmek için hataları düzeltin.',
 	'javascript_is_better' => 'FreshRSS JavaScript ile daha işlevseldir',
 	'js' => array(

+ 4 - 1
app/i18n/zh-cn/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => '常规配置已保存',
 	),
 	'congratulations' => '恭喜!',
-	'default_user' => '默认用户名 <small>(最多 16 个数字或字母)</small>',
+	'default_user' => array(
+		'_' => '默认用户名',
+		'max_char' => '最多 16 个数字或字母',
+	),
 	'fix_errors_before' => '请在继续下一步前修复错误',
 	'javascript_is_better' => '启用 JavaScript 会使 FreshRSS 工作得更好',
 	'js' => array(

+ 4 - 1
app/i18n/zh-tw/install.php

@@ -110,7 +110,10 @@ return array(
 		'ok' => '常規配置已保存',
 	),
 	'congratulations' => '恭喜!',
-	'default_user' => '默認用戶名 <small>(最多 16 個數字或字母)</small>',
+	'default_user' => array(
+		'_' => '默認用戶名',
+		'max_char' => '最多 16 個數字或字母',
+	),
 	'fix_errors_before' => '請在繼續下一步前修復錯誤',
 	'javascript_is_better' => '啟用 JavaScript 會使 FreshRSS 工作得更好',
 	'js' => array(

+ 91 - 55
app/install.php

@@ -364,9 +364,8 @@ function printStep0() {
 		<div class="form-group form-actions">
 			<div class="group-controls">
 				<button type="submit" class="btn btn-important" tabindex="2" ><?= _t('gen.action.submit') ?></button>
-				<button type="reset" class="btn" tabindex="3" ><?= _t('gen.action.cancel') ?></button>
 				<?php if ($s0['all'] == 'ok') { ?>
-				<a class="btn btn-important next-step" href="?step=1" tabindex="4" ><?= _t('install.action.next_step') ?></a>
+				<a class="next-step" href="?step=1" tabindex="4" ><?= _t('install.action.next_step') ?></a>
 				<?php } ?>
 			</div>
 		</div>
@@ -405,6 +404,7 @@ function printStep1() {
 	$res = checkRequirements();
 	$processUsername = getProcessUsername();
 ?>
+	<h2><?= _t('admin.check_install.php') ?></h2>
 	<noscript><p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.attention') ?></span> <?= _t('install.javascript_is_better') ?></p></noscript>
 
 	<?php
@@ -423,6 +423,9 @@ function printStep1() {
 	printStep1Template('xml', $res['xml']);
 	printStep1Template('mbstring', $res['mbstring']);
 	printStep1Template('fileinfo', $res['fileinfo']);
+	?>
+	<h2><?= _t('admin.check_install.files') ?></h2>
+	<?php
 	printStep1Template('data', $res['data'], [DATA_PATH, $processUsername]);
 	printStep1Template('cache', $res['cache'], [CACHE_PATH, $processUsername]);
 	printStep1Template('tmp', $res['tmp'], [TMP_PATH, $processUsername]);
@@ -433,20 +436,32 @@ function printStep1() {
 	<?php if (freshrss_already_installed() && $res['all'] == 'ok') { ?>
 	<p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.attention') ?></span> <?= _t('install.check.already_installed') ?></p>
 
-	<form action="index.php?step=1" method="post">
-		<input type="hidden" name="freshrss-keep-install" value="1" />
-		<button type="submit" class="btn btn-important next-step" tabindex="1" ><?= _t('install.action.keep_install') ?></button>
-		<a class="btn btn-attention next-step confirm" data-str-confirm="<?= _t('install.js.confirm_reinstall') ?>"
-			href="?step=2" tabindex="2" ><?= _t('install.action.reinstall') ?></a>
-	</form>
+	<div class="form-group form-actions">
+		<div class="group-controls">
+			<form action="index.php?step=1" method="post">
+				<input type="hidden" name="freshrss-keep-install" value="1" />
+				<button type="submit" class="btn btn-important" tabindex="1"><?= _t('install.action.keep_install') ?></button>
+				<a class="btn btn-attention confirm" data-str-confirm="<?= _t('install.js.confirm_reinstall') ?>"
+					href="?step=2" tabindex="2"><?= _t('install.action.reinstall') ?></a>
+			</form>
+		</div>
+	</div>
 
 	<?php } elseif ($res['all'] == 'ok') { ?>
-	<a class="btn btn-important next-step" href="?step=2" tabindex="1" ><?= _t('install.action.next_step') ?></a>
+	<div class="form-group form-actions">
+		<div class="group-controls">
+			<a class="btn btn-important" href="?step=2" tabindex="1"><?= _t('install.action.next_step') ?></a>
+		</div>
+	</div>
 	<?php } else { ?>
 	<p class="alert alert-error"><?= _t('install.action.fix_errors_before') ?></p>
-	<a id="actualize" class="btn" href="./index.php?step=1" title="<?= _t('install.check.reload') ?>">
-		<img class="icon" src="../themes/icons/refresh.svg" alt="🔃" loading="lazy" />
-	</a>
+	<div class="form-group form-actions">
+		<div class="group-controls">
+			<a id="actualize" class="btn" href="./index.php?step=1" title="<?= _t('install.check.reload') ?>" tabindex="1">
+				<img class="icon" src="../themes/icons/refresh.svg" alt="🔃" loading="lazy" />
+			</a>
+		</div>
+	</div>
 	<?php } ?>
 <?php
 }
@@ -539,7 +554,7 @@ function printStep2() {
 				<button type="submit" class="btn btn-important" tabindex="8" ><?= _t('gen.action.submit') ?></button>
 				<button type="reset" class="btn" tabindex="9" ><?= _t('gen.action.cancel') ?></button>
 				<?php if ($s2['all'] == 'ok') { ?>
-				<a class="btn btn-important next-step" href="?step=3" tabindex="10" ><?= _t('install.action.next_step') ?></a>
+				<a class="next-step" href="?step=3" tabindex="10" ><?= _t('install.action.next_step') ?></a>
 				<?php } ?>
 			</div>
 		</div>
@@ -568,14 +583,15 @@ function printStep3() {
 			<div class="group-controls">
 				<input type="text" id="default_user" name="default_user" autocomplete="username" required="required" size="16"
 					pattern="<?= FreshRSS_user_Controller::USERNAME_PATTERN ?>" value="<?= isset($_SESSION['default_user']) ? $_SESSION['default_user'] : '' ?>"
-					placeholder="<?= httpAuthUser() == '' ? 'alice' : httpAuthUser() ?>" tabindex="3" />
+					placeholder="<?= httpAuthUser() == '' ? 'alice' : httpAuthUser() ?>" tabindex="1" />
+				<p class="help"><?= _i('help') ?> <?= _t('install.default_user.max_char') ?></p>
 			</div>
 		</div>
 
 		<div class="form-group">
 			<label class="group-name" for="auth_type"><?= _t('install.auth.type') ?></label>
 			<div class="group-controls">
-				<select id="auth_type" name="auth_type" required="required" tabindex="4">
+				<select id="auth_type" name="auth_type" required="required" tabindex="2">
 					<option value="form"<?= $auth_type === 'form' || (no_auth($auth_type) && cryptAvailable()) ? ' selected="selected"' : '',
 						cryptAvailable() ? '' : ' disabled="disabled"' ?>><?= _t('install.auth.form') ?></option>
 					<option value="http_auth"<?= $auth_type === 'http_auth' ? ' selected="selected"' : '',
@@ -591,8 +607,8 @@ function printStep3() {
 			<div class="group-controls">
 				<div class="stick">
 					<input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}"
-						autocomplete="off" <?= $auth_type === 'form' ? ' required="required"' : '' ?> tabindex="5" />
-					<button type="button" class="btn toggle-password" data-toggle="passwordPlain"><?= FreshRSS_Themes::icon('key') ?></button>
+						autocomplete="off" <?= $auth_type === 'form' ? ' required="required"' : '' ?> tabindex="3" />
+					<button type="button" class="btn toggle-password" data-toggle="passwordPlain" tabindex="4"><?= FreshRSS_Themes::icon('key') ?></button>
 				</div>
 				<p class="help"><?= _i('help') ?> <?= _t('install.auth.password_format') ?></p>
 				<noscript><b><?= _t('gen.js.should_be_activated') ?></b></noscript>
@@ -601,10 +617,10 @@ function printStep3() {
 
 		<div class="form-group form-actions">
 			<div class="group-controls">
-				<button type="submit" class="btn btn-important" tabindex="7" ><?= _t('gen.action.submit') ?></button>
-				<button type="reset" class="btn" tabindex="8" ><?= _t('gen.action.cancel') ?></button>
+				<button type="submit" class="btn btn-important" tabindex="5" ><?= _t('gen.action.submit') ?></button>
+				<button type="reset" class="btn" tabindex="6" ><?= _t('gen.action.cancel') ?></button>
 				<?php if ($s3['all'] == 'ok') { ?>
-				<a class="btn btn-important next-step" href="?step=4" tabindex="9" ><?= _t('install.action.next_step') ?></a>
+				<a class="next-step" href="?step=4" tabindex="7" ><?= _t('install.action.next_step') ?></a>
 				<?php } ?>
 			</div>
 		</div>
@@ -615,7 +631,11 @@ function printStep3() {
 function printStep4() {
 ?>
 	<p class="alert alert-success"><span class="alert-head"><?= _t('install.congratulations') ?></span> <?= _t('install.ok') ?></p>
-	<a class="btn btn-important next-step" href="?step=5" tabindex="1"><?= _t('install.action.finish') ?></a>
+	<div class="form-group form-actions">
+		<div class="group-controls">
+			<a class="btn btn-important" href="?step=5" tabindex="1"><?= _t('install.action.finish') ?></a>
+		</div>
+	</div>
 <?php
 }
 
@@ -668,6 +688,12 @@ if (_t('gen.dir') === 'rtl') {
 		<title><?= _t('install.title') ?>: <?= _t('install.step', STEP + 1) ?></title>
 		<link rel="stylesheet" href="../themes/base-theme/frss.css?<?= @filemtime(PUBLIC_PATH . '/themes/base-theme/frss.css') ?>" />
 		<link rel="stylesheet" href="../themes/Origine/origine.css?<?= @filemtime(PUBLIC_PATH . '/themes/Origine/origine.css') ?>" />
+		<link rel="shortcut icon" id="favicon" type="image/x-icon" sizes="16x16 64x64" href="../favicon.ico" />
+		<link rel="icon msapplication-TileImage apple-touch-icon" type="image/png" sizes="256x256" href="../themes/icons/favicon-256.png" />
+		<link rel="apple-touch-icon" href="../themes/icons/apple-touch-icon.png" />
+		<meta name="apple-mobile-web-app-capable" content="yes" />
+		<meta name="apple-mobile-web-app-status-bar-style" content="black" />
+		<meta name="apple-mobile-web-app-title" content="FreshRSS">
 		<meta name="robots" content="noindex,nofollow" />
 	</head>
 	<body>
@@ -680,45 +706,55 @@ if (_t('gen.dir') === 'rtl') {
 			</a>
 		</div>
 	</div>
+	<div class="item"></div>
+	<div class="item configure">
+		<a class="btn only-mobile" href="#aside"><?= _i('view-normal') ?></a>
+	</div>
 </header>
 
 <div id="global">
-	<nav class="nav nav-list aside">
-		<div class="nav-header"><?= _t('install.steps') ?></div>
-		<ol>
-			<li class="item<?= STEP == 0 ? ' active' : '' ?>">
-				<a href="?step=0" title="<?= _t('install.step', 0) ?>: <?= _t('install.language') ?>"><?= _t('install.language') ?></a>
-			</li>
-			<li class="item<?= STEP == 1 ? ' active' : '' ?>">
-				<?php if (STEP > 0) {?>
-				<a href="?step=1" title="<?= _t('install.step', 1) ?>: <?= _t('install.check') ?>"><?= _t('install.check') ?></a>
-				<?php } else { ?>
-				<span><?= _t('install.check') ?></span>
-				<?php } ?>
-			</li>
-			<li class="item<?= STEP == 2 ? ' active' : '' ?>">
-				<?php if (STEP > 1) {?>
-				<a href="?step=2" title="<?= _t('install.step', 2) ?>: <?= _t('install.bdd.conf') ?>"><?= _t('install.bdd.conf') ?></a>
-				<?php } else { ?>
-				<span><?= _t('install.bdd.conf') ?></span>
-				<?php } ?>
-			</li>
-			<li class="item<?= STEP == 3 ? ' active' : '' ?>">
-				<?php if (STEP > 2) {?>
-				<a href="?step=3" title="<?= _t('install.step', 3) ?>: <?= _t('install.conf') ?>"><?= _t('install.conf') ?></a>
-				<?php } else { ?>
-				<span><?= _t('install.conf') ?></span>
-				<?php } ?>
-			</li>
-			<li class="item<?= STEP == 4 ? ' active' : '' ?>">
-				<?php if (STEP > 3) {?>
-				<a href="?step=4" title="<?= _t('install.step', 4) ?>: <?= _t('install.this_is_the_end') ?>"><?= _t('install.this_is_the_end') ?></a>
-				<?php } else { ?>
-				<span><?= _t('install.this_is_the_end') ?></span>
-				<?php } ?>
+	<nav class="nav nav-list aside" id="aside">
+		<a class="toggle_aside" href="#close"><img class="icon" src="../themes/icons/close.svg" loading="lazy" alt="❌"></a>
+		<ul>
+			<li class="item nav-section">
+				<div class="nav-header"><?= _t('install.steps') ?></div>
+				<ol>
+					<li class="item<?= STEP == 0 ? ' active' : '' ?>">
+						<a href="?step=0" title="<?= _t('install.step', 0) ?>: <?= _t('install.language') ?>"><?= _t('install.language') ?></a>
+					</li>
+					<li class="item<?= STEP == 1 ? ' active' : '' ?>">
+						<?php if (STEP > 0) {?>
+						<a href="?step=1" title="<?= _t('install.step', 1) ?>: <?= _t('install.check') ?>"><?= _t('install.check') ?></a>
+						<?php } else { ?>
+						<span><?= _t('install.check') ?></span>
+						<?php } ?>
+					</li>
+					<li class="item<?= STEP == 2 ? ' active' : '' ?>">
+						<?php if (STEP > 1) {?>
+						<a href="?step=2" title="<?= _t('install.step', 2) ?>: <?= _t('install.bdd.conf') ?>"><?= _t('install.bdd.conf') ?></a>
+						<?php } else { ?>
+						<span><?= _t('install.bdd.conf') ?></span>
+						<?php } ?>
+					</li>
+					<li class="item<?= STEP == 3 ? ' active' : '' ?>">
+						<?php if (STEP > 2) {?>
+						<a href="?step=3" title="<?= _t('install.step', 3) ?>: <?= _t('install.conf') ?>"><?= _t('install.conf') ?></a>
+						<?php } else { ?>
+						<span><?= _t('install.conf') ?></span>
+						<?php } ?>
+					</li>
+					<li class="item<?= STEP == 4 ? ' active' : '' ?>">
+						<?php if (STEP > 3) {?>
+						<a href="?step=4" title="<?= _t('install.step', 4) ?>: <?= _t('install.this_is_the_end') ?>"><?= _t('install.this_is_the_end') ?></a>
+						<?php } else { ?>
+						<span><?= _t('install.this_is_the_end') ?></span>
+						<?php } ?>
+					</li>
+				</ol>
 			</li>
-		</ol>
+		</ul>
 	</nav>
+	<a class="close-aside" href="#close">❌</a>
 
 	<main class="post">
 		<h1><?= _t('install.title') ?>: <?= _t('install.step', STEP + 1) ?></h1>

+ 2 - 4
p/scripts/extra.js

@@ -84,14 +84,12 @@ function init_crypto_form() {
 // <show password>
 let timeoutHide;
 
-function showPW_this(ev) {
+function showPW_this() {
 	const id_passwordField = this.getAttribute('data-toggle');
 	if (this.classList.contains('active')) {
 		hidePW(id_passwordField);
 	} else {
-		if (ev.type === 'click' || ev.buttons || ev.key === ' ' || ev.key.toUpperCase() === 'ENTER') {
-			showPW(id_passwordField);
-		}
+		showPW(id_passwordField);
 	}
 	return false;
 }

+ 29 - 12
p/scripts/install.js

@@ -1,26 +1,43 @@
 // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
 'use strict';
 
-function show_password(ev) {
-	const button = ev.currentTarget;
-	const passwordField = document.getElementById(button.getAttribute('data-toggle'));
+let timeoutHide;
+
+function showPW_this() {
+	const id_passwordField = this.getAttribute('data-toggle');
+	if (this.classList.contains('active')) {
+		hidePW(id_passwordField);
+	} else {
+		showPW(id_passwordField);
+	}
+	return false;
+}
+
+function showPW(id_passwordField) {
+	const passwordField = document.getElementById(id_passwordField);
 	passwordField.setAttribute('type', 'text');
-	button.className += ' active';
+	passwordField.nextElementSibling.classList.add('active');
+	clearTimeout(timeoutHide);
+	timeoutHide = setTimeout(function () { hidePW(id_passwordField); }, 5000);
 	return false;
 }
-function hide_password(ev) {
-	const button = ev.currentTarget;
-	const passwordField = document.getElementById(button.getAttribute('data-toggle'));
+
+function hidePW(id_passwordField) {
+	clearTimeout(timeoutHide);
+	const passwordField = document.getElementById(id_passwordField);
 	passwordField.setAttribute('type', 'password');
-	button.className = button.className.replace(/(?:^|\s)active(?!\S)/g, '');
+	passwordField.nextElementSibling.classList.remove('active');
 	return false;
 }
-const toggles = document.getElementsByClassName('toggle-password');
-for (let i = 0; i < toggles.length; i++) {
-	toggles[i].addEventListener('mousedown', show_password);
-	toggles[i].addEventListener('mouseup', hide_password);
+
+function init_password_observers(parent) {
+	parent.querySelectorAll('.toggle-password').forEach(function (btn) {
+		btn.addEventListener('click', showPW_this);
+	});
 }
 
+init_password_observers(document.body);
+
 const auth_type = document.getElementById('auth_type');
 function auth_type_change() {
 	if (auth_type) {

+ 11 - 1
p/themes/Origine/origine.css

@@ -190,7 +190,12 @@ form th {
 
 .form-group .group-controls {
 	min-height: 25px;
-	padding: 10px 0;
+	padding: 0.5rem 0;
+}
+
+.form-group.form-actions .group-controls .btn {
+	margin-top: 0.25rem;
+	margin-bottom: 0.25rem;
 }
 
 .form-group .group-controls label {
@@ -349,6 +354,10 @@ a:hover .icon {
 	background-image: linear-gradient(to bottom, var(--attention-background-color-gradient1-hover), var(--attention-background-color-gradient2-hover));
 }
 
+a.btn-attention:hover {
+	color: var(--font-color-light);
+}
+
 .btn-attention:active {
 	background-color: var(--attention-background-color-active);
 	box-shadow: none;
@@ -381,6 +390,7 @@ a:hover .icon {
 }
 
 .nav-list .item > a,
+.nav-list .item > span,
 .nav-list .item > div {
 	padding: 0 1rem;
 }

+ 11 - 1
p/themes/Origine/origine.rtl.css

@@ -190,7 +190,12 @@ form th {
 
 .form-group .group-controls {
 	min-height: 25px;
-	padding: 10px 0;
+	padding: 0.5rem 0;
+}
+
+.form-group.form-actions .group-controls .btn {
+	margin-top: 0.25rem;
+	margin-bottom: 0.25rem;
 }
 
 .form-group .group-controls label {
@@ -349,6 +354,10 @@ a:hover .icon {
 	background-image: linear-gradient(to bottom, var(--attention-background-color-gradient1-hover), var(--attention-background-color-gradient2-hover));
 }
 
+a.btn-attention:hover {
+	color: var(--font-color-light);
+}
+
 .btn-attention:active {
 	background-color: var(--attention-background-color-active);
 	box-shadow: none;
@@ -381,6 +390,7 @@ a:hover .icon {
 }
 
 .nav-list .item > a,
+.nav-list .item > span,
 .nav-list .item > div {
 	padding: 0 1rem;
 }

+ 6 - 0
p/themes/base-theme/frss.css

@@ -738,6 +738,12 @@ input[type="checkbox"]:focus-visible {
 	width: 100%
 }
 
+.group-controls .next-step {
+	display: inline-block;
+	padding-top: 6px;
+	padding-bottom: 6px;
+}
+
 .alert-head {
 	margin: 0;
 	font-weight: bold;

+ 6 - 0
p/themes/base-theme/frss.rtl.css

@@ -738,6 +738,12 @@ input[type="checkbox"]:focus-visible {
 	width: 100%
 }
 
+.group-controls .next-step {
+	display: inline-block;
+	padding-top: 6px;
+	padding-bottom: 6px;
+}
+
 .alert-head {
 	margin: 0;
 	font-weight: bold;