mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-14 18:23:52 -04:00
Closes FreshRSS/FreshRSS#6522. Refresh of FreshRSS/FreshRSS#6590 by @sgzmd, which has been stalled with merge conflicts since 2024. All original commits are preserved as-is in the history (authorship intact); this PR adds a merge with current `edge` plus minor fixes. ## Summary Adds a user setting `show_title_unread` (default `true`, so existing behavior is preserved) that hides the unread article counter from both the tab title and the favicon overlay. A single toggle controls both, matching the request in #6522. ## Screenshots Toggle in display settings: <img width="320" height="127" alt="Display settings with new toggle" src="https://github.com/user-attachments/assets/fc78f825-161d-4b47-9b85-08e39554a4b1" /> Tab title and favicon when **enabled** (current behavior): <img width="253" height="42" alt="Tab title and favicon with unread counter" src="https://github.com/user-attachments/assets/57387600-72e0-4b22-b059-04b5bfea673a" /> Tab title and favicon when **disabled** (new behavior): <img width="254" height="40" alt="Tab title and favicon without unread counter" src="https://github.com/user-attachments/assets/93ac7997-dd4e-49bc-ab4a-74e4f0d2db1b" /> ## Changes on top of #6590 - Resolved merge conflicts with current `edge` (controllers, model, view, `config.default.php`, ~25 i18n files, plus the `zh-tw` -> `zh-TW` rename). - Replaced Czech text mistakenly placed in `de/conf.php` with an English `// TODO` marker so a German speaker can translate later. - Renamed JS context key `show_unread_favicon` -> `show_title_unread` to match the backend property and avoid a confusing dual-name for one setting. - Removed an unused duplicate of `show_title_unread` from `config.default.php` (the setting is read via `userConf()`, never `systemConf()`). - Gated the dynamic title rewrite in `incUnreadsFeed` (`p/scripts/main.js`) on the setting. Without this, marking an article read while the setting was off would re-add the `(N)` prefix to the tab title. - Escaped a stray apostrophe in the Occitan translation that broke parsing. - `make fix-all` re-sorted i18n keys and added `// TODO` placeholders for `fi`, `pt-PT`, `uk` (untranslated by the original PR). ## Test plan - [x] `make test-all` passes (620/620 PHPUnit, phpstan, phpcs, eslint, stylelint, markdownlint clean; `bin/typos` failed locally with a binary arch mismatch on macOS arm64 - unrelated to this change). - [x] Manually tested on a real instance: default behavior unchanged; toggling the setting hides both the tab title `(N) ` prefix and the favicon overlay; toggling back restores both; marking articles read while the setting is off does not bring the counter back; opening and closing an article preserves the user's choice. * Make showing the number of unread items in the title configurable. * Proposed approach to passing show_unread_favicon setting down to client-side code * Fixes and refactoring * Updating default config for the user. When user's config wasn't initialised we are copying it from `config-user.default.php` - if `show_title_unread` is not there, it is assumed to be false, whereas in `config.default.php` it's true by default. This results in inconsistency until user changes the field for the first time in Config->Display. * Adding translations. * fix: gate JS title rewrite + drop dead system config entry The original PR added show_title_unread to both config-user.default.php (read by userConf, the right place) and config.default.php (read by systemConf, never used here). Drop the system-level entry. Also: incUnreadsFeed dynamically rewrites document.title when articles are marked read/unread. That code path was not gated by the setting, so toggling the setting off and then marking an article read would re-add the (N) prefix to the tab title. Skip the document.title / prevTitle write when context.show_title_unread is false. * fix: drop README pollution from local make fix-all `make fix-all` regenerated the README translation tables on macOS, where the case-insensitive filesystem and an untracked local `app/i18n/nb/` directory caused the generator to emit `zh-tw` (lowercase) and an `nb` entry. Reset both README files to upstream/edge so CI can regenerate them cleanly. * fix: restore zh-TW/conf.php from edge (case-insensitive FS damage) The macOS case-insensitive filesystem caused the merge to overwrite upstream/edge's properly-translated zh-TW/conf.php with the older zh-tw/conf.php content from the PR side, regressing translation coverage from 94% to 71%. Reset the file to edge's content and re-add the show_title_unread Traditional Chinese translation. --------- Co-authored-by: sgzmd <sigizmund@gmail.com> Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr> Co-authored-by: Bjørn A. Andersen <polybjorn@users.noreply.github.com>
145 lines
5.4 KiB
PHP
145 lines
5.4 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* @property string $apiPasswordHash
|
|
* @property array{keep_period:string|false,keep_max:int|false,keep_min:int|false,keep_favourites:bool,keep_labels:bool,keep_unreads:bool} $archiving
|
|
* @property bool $auto_load_more
|
|
* @property bool $auto_remove_article
|
|
* @property bool $bottomline_date
|
|
* @property bool $bottomline_favorite
|
|
* @property bool $bottomline_link
|
|
* @property bool $bottomline_read
|
|
* @property bool $bottomline_sharing
|
|
* @property bool $bottomline_tags
|
|
* @property bool $bottomline_myLabels
|
|
* @property string $content_width
|
|
* @property-read int $default_state
|
|
* @property string $default_view
|
|
* @property string|bool $display_categories
|
|
* @property string $show_tags
|
|
* @property int $show_tags_max
|
|
* @property string $show_author_date
|
|
* @property string $show_feed_name
|
|
* @property string $show_article_icons
|
|
* @property bool $display_posts
|
|
* @property string $email_validation_token
|
|
* @property-read bool $enabled
|
|
* @property string $feverKey
|
|
* @property bool $hide_read_feeds
|
|
* @property int $html5_notif_timeout
|
|
* @property bool $html5_enable_notif
|
|
* @property int $good_notification_timeout
|
|
* @property int $bad_notification_timeout
|
|
* @property-read bool $is_admin
|
|
* @property int|null $keep_history_default
|
|
* @property string $language
|
|
* @property string $timezone
|
|
* @property bool $lazyload
|
|
* @property string $mail_login
|
|
* @property bool $mark_updated_article_unread
|
|
* @property array<string,bool|int> $mark_when
|
|
* @property int $max_posts_per_rss
|
|
* @property-read array<string,int> $limits
|
|
* @property int|null $old_entries
|
|
* @property bool $onread_jump_next
|
|
* @property string $passwordHash
|
|
* @property int $posts_per_page
|
|
* @property array<int,array{get?:string,name?:string,order?:string,search?:string,state?:int,url?:string,token?:string,
|
|
* shareRss?:bool,shareOpml?:bool,description?:string,imageUrl?:string}> $queries
|
|
* @property bool $reading_confirm
|
|
* @property int $since_hours_posts_per_rss
|
|
* @property bool $show_fav_unread
|
|
* @property bool $show_favicons
|
|
* @property bool $icons_as_emojis
|
|
* @property int $simplify_over_n_feeds
|
|
* @property bool $show_nav_buttons
|
|
* @property bool $show_title_unread
|
|
* @property bool $sidebar_hidden_by_default
|
|
* @property 'big'|'small'|'none' $mark_read_button
|
|
* @property 'ASC'|'DESC' $sort_order
|
|
* @property 'id'|'c.name'|'date'|'f.name'|'length'|'link'|'rand'|'title' $sort
|
|
* @property 'ASC'|'DESC' $secondary_sort_order
|
|
* @property 'id'|'date'|'link'|'title' $secondary_sort
|
|
* @property array<int,array<string,string>> $sharing
|
|
* @property array<string,string> $shortcuts
|
|
* @property bool $sides_close_article
|
|
* @property bool $sticky_post
|
|
* @property string $theme
|
|
* @property string $darkMode
|
|
* @property string $token
|
|
* @property bool $topline_date
|
|
* @property bool $topline_display_authors
|
|
* @property bool $topline_favorite
|
|
* @property bool $topline_myLabels
|
|
* @property bool $topline_sharing
|
|
* @property bool $topline_link
|
|
* @property bool $topline_read
|
|
* @property bool $topline_summary
|
|
* @property string $topline_website
|
|
* @property string $topline_thumbnail
|
|
* @property int $ttl_default
|
|
* @property int $dynamic_opml_ttl_default
|
|
* @property string $view_mode
|
|
* @property array<string,bool|int|string> $volatile
|
|
* @property array<string,array<string,mixed>> $extensions
|
|
* @property bool $retrieve_extension_list
|
|
* @property array<string> $send_referrer_allowlist
|
|
*/
|
|
final class FreshRSS_UserConfiguration extends Minz_Configuration {
|
|
use FreshRSS_FilterActionsTrait;
|
|
|
|
/** @throws Minz_FileNotExistException */
|
|
public static function init(string $config_filename, ?string $default_filename = null): FreshRSS_UserConfiguration {
|
|
parent::register('user', $config_filename, $default_filename);
|
|
try {
|
|
return parent::get('user');
|
|
} catch (Minz_ConfigurationNamespaceException $ex) {
|
|
FreshRSS::killApp($ex->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Access the default configuration for users.
|
|
* @throws Minz_FileNotExistException
|
|
*/
|
|
public static function default(): FreshRSS_UserConfiguration {
|
|
/** @var FreshRSS_UserConfiguration|null $default_user_conf */
|
|
static $default_user_conf = null;
|
|
if ($default_user_conf === null) {
|
|
$namespace = 'user_default';
|
|
FreshRSS_UserConfiguration::register($namespace, '_', FRESHRSS_PATH . '/config-user.default.php');
|
|
$default_user_conf = FreshRSS_UserConfiguration::get($namespace);
|
|
}
|
|
return $default_user_conf;
|
|
}
|
|
|
|
/**
|
|
* Register and return the configuration for a given user.
|
|
*
|
|
* Note this function has been created to generate temporary configuration
|
|
* objects. If you need a long-time configuration, please don't use this function.
|
|
*
|
|
* @param string $username the name of the user of which we want the configuration.
|
|
* @return FreshRSS_UserConfiguration|null object, or null if the configuration cannot be loaded.
|
|
* @throws Minz_ConfigurationNamespaceException
|
|
*/
|
|
public static function getForUser(string $username): ?FreshRSS_UserConfiguration {
|
|
if (!FreshRSS_user_Controller::checkUsername($username)) {
|
|
return null;
|
|
}
|
|
$namespace = 'user_' . $username;
|
|
try {
|
|
FreshRSS_UserConfiguration::register($namespace,
|
|
USERS_PATH . '/' . $username . '/config.php',
|
|
FRESHRSS_PATH . '/config-user.default.php');
|
|
} catch (Minz_FileNotExistException $e) {
|
|
Minz_Log::warning($e->getMessage(), ADMIN_LOG);
|
|
return null;
|
|
}
|
|
|
|
$user_conf = FreshRSS_UserConfiguration::get($namespace);
|
|
return $user_conf;
|
|
}
|
|
}
|