Add option to enable/disable keyboard shortcuts

This commit is contained in:
Frédéric Guillot 2019-04-28 18:20:46 -07:00 committed by fguillot
parent 3c8cc0b2b6
commit 4295a86e55
24 changed files with 183 additions and 112 deletions

View File

@ -12,7 +12,7 @@ import (
"miniflux.app/logger" "miniflux.app/logger"
) )
const schemaVersion = 22 const schemaVersion = 23
// Migrate executes database migrations. // Migrate executes database migrations.
func Migrate(db *sql.DB) { func Migrate(db *sql.DB) {

View File

@ -146,6 +146,7 @@ update entries set document_vectors = to_tsvector(substring(title || ' ' || coal
create index document_vectors_idx on entries using gin(document_vectors);`, create index document_vectors_idx on entries using gin(document_vectors);`,
"schema_version_21": `alter table feeds add column user_agent text default '';`, "schema_version_21": `alter table feeds add column user_agent text default '';`,
"schema_version_22": `update entries set document_vectors = setweight(to_tsvector(substring(coalesce(title, '') for 1000000)), 'A') || setweight(to_tsvector(substring(coalesce(content, '') for 1000000)), 'B');`, "schema_version_22": `update entries set document_vectors = setweight(to_tsvector(substring(coalesce(title, '') for 1000000)), 'A') || setweight(to_tsvector(substring(coalesce(content, '') for 1000000)), 'B');`,
"schema_version_23": `alter table users add column keyboard_shortcuts boolean default 't';`,
"schema_version_3": `create table tokens ( "schema_version_3": `create table tokens (
id text not null, id text not null,
value text not null, value text not null,
@ -196,6 +197,7 @@ var SqlMapChecksums = map[string]string{
"schema_version_20": "5d414c0cfc0da2863c641079afa58b7ff42dccb0f0e01c822ad435c3e3aa9201", "schema_version_20": "5d414c0cfc0da2863c641079afa58b7ff42dccb0f0e01c822ad435c3e3aa9201",
"schema_version_21": "77da01ee38918ff4fe33985fbb20ed3276a717a7584c2ca9ebcf4d4ab6cb6910", "schema_version_21": "77da01ee38918ff4fe33985fbb20ed3276a717a7584c2ca9ebcf4d4ab6cb6910",
"schema_version_22": "51ed5fbcae9877e57274511f0ef8c61d254ebd78dfbcbc043a2acd30f4c93ca3", "schema_version_22": "51ed5fbcae9877e57274511f0ef8c61d254ebd78dfbcbc043a2acd30f4c93ca3",
"schema_version_23": "cb3512d328436447f114e305048c0daa8af7505cfe5eab02778b0de1156081b2",
"schema_version_3": "a54745dbc1c51c000f74d4e5068f1e2f43e83309f023415b1749a47d5c1e0f12", "schema_version_3": "a54745dbc1c51c000f74d4e5068f1e2f43e83309f023415b1749a47d5c1e0f12",
"schema_version_4": "216ea3a7d3e1704e40c797b5dc47456517c27dbb6ca98bf88812f4f63d74b5d9", "schema_version_4": "216ea3a7d3e1704e40c797b5dc47456517c27dbb6ca98bf88812f4f63d74b5d9",
"schema_version_5": "46397e2f5f2c82116786127e9f6a403e975b14d2ca7b652a48cd1ba843e6a27c", "schema_version_5": "46397e2f5f2c82116786127e9f6a403e975b14d2ca7b652a48cd1ba843e6a27c",

View File

@ -0,0 +1 @@
alter table users add column keyboard_shortcuts boolean default 't';

View File

@ -221,6 +221,7 @@ var translations = map[string]string{
"form.prefs.label.entry_sorting": "Sortierung der Artikel", "form.prefs.label.entry_sorting": "Sortierung der Artikel",
"form.prefs.select.older_first": "Älteste Artikel zuerst", "form.prefs.select.older_first": "Älteste Artikel zuerst",
"form.prefs.select.recent_first": "Neueste Artikel zuerst", "form.prefs.select.recent_first": "Neueste Artikel zuerst",
"form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren",
"form.import.label.file": "OPML Datei", "form.import.label.file": "OPML Datei",
"form.integration.fever_activate": "Fever API aktivieren", "form.integration.fever_activate": "Fever API aktivieren",
"form.integration.fever_username": "Fever Benutzername", "form.integration.fever_username": "Fever Benutzername",
@ -515,6 +516,7 @@ var translations = map[string]string{
"form.prefs.label.entry_sorting": "Entry Sorting", "form.prefs.label.entry_sorting": "Entry Sorting",
"form.prefs.select.older_first": "Older entries first", "form.prefs.select.older_first": "Older entries first",
"form.prefs.select.recent_first": "Recent entries first", "form.prefs.select.recent_first": "Recent entries first",
"form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts",
"form.import.label.file": "OPML file", "form.import.label.file": "OPML file",
"form.integration.fever_activate": "Activate Fever API", "form.integration.fever_activate": "Activate Fever API",
"form.integration.fever_username": "Fever Username", "form.integration.fever_username": "Fever Username",
@ -789,6 +791,7 @@ var translations = map[string]string{
"form.prefs.label.entry_sorting": "Clasificación de entradas", "form.prefs.label.entry_sorting": "Clasificación de entradas",
"form.prefs.select.older_first": "Entradas más viejas primero", "form.prefs.select.older_first": "Entradas más viejas primero",
"form.prefs.select.recent_first": "Entradas recientes primero", "form.prefs.select.recent_first": "Entradas recientes primero",
"form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado",
"form.import.label.file": "Archivo OPML", "form.import.label.file": "Archivo OPML",
"form.integration.fever_activate": "Activar API de Fever", "form.integration.fever_activate": "Activar API de Fever",
"form.integration.fever_username": "Nombre de usuario de Fever", "form.integration.fever_username": "Nombre de usuario de Fever",
@ -1063,6 +1066,7 @@ var translations = map[string]string{
"form.prefs.label.entry_sorting": "Ordre des éléments", "form.prefs.label.entry_sorting": "Ordre des éléments",
"form.prefs.select.older_first": "Ancien éléments en premier", "form.prefs.select.older_first": "Ancien éléments en premier",
"form.prefs.select.recent_first": "Éléments récents en premier", "form.prefs.select.recent_first": "Éléments récents en premier",
"form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier",
"form.import.label.file": "Fichier OPML", "form.import.label.file": "Fichier OPML",
"form.integration.fever_activate": "Activer l'API de Fever", "form.integration.fever_activate": "Activer l'API de Fever",
"form.integration.fever_username": "Nom d'utilisateur pour l'API de Fever", "form.integration.fever_username": "Nom d'utilisateur pour l'API de Fever",
@ -1357,6 +1361,7 @@ var translations = map[string]string{
"form.prefs.label.entry_sorting": "Ordinamento articoli", "form.prefs.label.entry_sorting": "Ordinamento articoli",
"form.prefs.select.older_first": "Prima i più recenti", "form.prefs.select.older_first": "Prima i più recenti",
"form.prefs.select.recent_first": "Prima i più vecchi", "form.prefs.select.recent_first": "Prima i più vecchi",
"form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera",
"form.import.label.file": "File OPML", "form.import.label.file": "File OPML",
"form.integration.fever_activate": "Abilita l'API di Fever", "form.integration.fever_activate": "Abilita l'API di Fever",
"form.integration.fever_username": "Nome utente dell'account Fever", "form.integration.fever_username": "Nome utente dell'account Fever",
@ -1631,6 +1636,7 @@ var translations = map[string]string{
"form.prefs.label.entry_sorting": "Volgorde van items", "form.prefs.label.entry_sorting": "Volgorde van items",
"form.prefs.select.older_first": "Oudere items eerst", "form.prefs.select.older_first": "Oudere items eerst",
"form.prefs.select.recent_first": "Recente items eerst", "form.prefs.select.recent_first": "Recente items eerst",
"form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in",
"form.import.label.file": "OPML-bestand", "form.import.label.file": "OPML-bestand",
"form.integration.fever_activate": "Activeer Fever API", "form.integration.fever_activate": "Activeer Fever API",
"form.integration.fever_username": "Fever gebruikersnaam", "form.integration.fever_username": "Fever gebruikersnaam",
@ -1924,6 +1930,7 @@ var translations = map[string]string{
"form.prefs.label.theme": "Wygląd", "form.prefs.label.theme": "Wygląd",
"form.prefs.label.entry_sorting": "Sortowanie artykułów", "form.prefs.label.entry_sorting": "Sortowanie artykułów",
"form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze", "form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze",
"form.prefs.label.keyboard_shortcuts": "Włącz skróty klawiaturowe",
"form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze", "form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze",
"form.import.label.file": "Plik OPML", "form.import.label.file": "Plik OPML",
"form.integration.fever_activate": "Aktywuj Fever API", "form.integration.fever_activate": "Aktywuj Fever API",
@ -2225,6 +2232,7 @@ var translations = map[string]string{
"form.prefs.label.entry_sorting": "Сортировка записей", "form.prefs.label.entry_sorting": "Сортировка записей",
"form.prefs.select.older_first": "Сначала старые записи", "form.prefs.select.older_first": "Сначала старые записи",
"form.prefs.select.recent_first": "Сначала последние записи", "form.prefs.select.recent_first": "Сначала последние записи",
"form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",
"form.import.label.file": "OPML файл", "form.import.label.file": "OPML файл",
"form.integration.fever_activate": "Активировать Fever API", "form.integration.fever_activate": "Активировать Fever API",
"form.integration.fever_username": "Имя пользователя Fever", "form.integration.fever_username": "Имя пользователя Fever",
@ -2503,6 +2511,7 @@ var translations = map[string]string{
"form.prefs.label.entry_sorting": "内容排序", "form.prefs.label.entry_sorting": "内容排序",
"form.prefs.select.older_first": "旧->新", "form.prefs.select.older_first": "旧->新",
"form.prefs.select.recent_first": "新->旧", "form.prefs.select.recent_first": "新->旧",
"form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",
"form.import.label.file": "OPML 文件", "form.import.label.file": "OPML 文件",
"form.integration.fever_activate": "启用 Fever API", "form.integration.fever_activate": "启用 Fever API",
"form.integration.fever_username": "Fever 用户名", "form.integration.fever_username": "Fever 用户名",
@ -2574,13 +2583,13 @@ var translations = map[string]string{
} }
var translationsChecksums = map[string]string{ var translationsChecksums = map[string]string{
"de_DE": "99892a072e1052e288165485cc0d3d4bdbce5edc237bb4686e6747b26681131c", "de_DE": "24812dcba285e56e042486c7f3d6fd9b915b37dc8473640d5099bade94e702b2",
"en_US": "223117ae0315b5190e57a45a402bb63f17d5abdb4f904e9fd9cb7e91a921caab", "en_US": "491b3765c7e7b1a3e49b265bf3358a48b6d9aeea5340777a38957ad216101b9d",
"es_ES": "e9f14f655d3dbf524d5a95a317cda969210df81b874749c393e475298739b4b9", "es_ES": "89b84d2505fc27d3f75b2622eae78373c6ce465dc180e1e5bf2a4aca25f73f2e",
"fr_FR": "c4be5bb9addb3f42db66782e3f1b5a9b16b4dae4b070032b2caeb926a15ac0a3", "fr_FR": "ac80056831e39c48b47d54299ff112fd9f5e35d14fa248b6a5ae049045cf1537",
"it_IT": "a60374bc34e7a0769ea764a7b053c2b47feedd6a677ed0e4b5cd16bb98369d7f", "it_IT": "b3521ffb2f56810568bc2317846f2dd16dad77b76dadec5990598af2a6e49403",
"nl_NL": "28dfa405a69e7864e687ca5359ca0359de76aa1b4c5d01b9495cd98824f147a2", "nl_NL": "7d095d9c8915e7ae79d28d35793cb6fce04c867e35b9b4a956da45a7f1a0925c",
"pl_PL": "032b7ad1f29e42376bcbadcddc00ffbedfa61247e3e04c3bbe2c4796fc306cea", "pl_PL": "d99b8dcf56f5672e261b231f01cdf5e17f9c3aa422c798f994aa480a1d9a92e6",
"ru_RU": "ff03f2945a8e453a499394220b83b95199b976ba5351d0fbf726f1d1b0a9a6e5", "ru_RU": "4c135a56164be9223d87c50d738d8096c9e732c8ec51ee4a7c7db09ee634837a",
"zh_CN": "2440ad576beb46783f8f5d553bf842748fd3b665132b95834f745e49456a0021", "zh_CN": "c1481025b98c282b284aae12e61d5f2356f4d83797d25a596afb67c3e170c6bf",
} }

View File

@ -216,6 +216,7 @@
"form.prefs.label.entry_sorting": "Sortierung der Artikel", "form.prefs.label.entry_sorting": "Sortierung der Artikel",
"form.prefs.select.older_first": "Älteste Artikel zuerst", "form.prefs.select.older_first": "Älteste Artikel zuerst",
"form.prefs.select.recent_first": "Neueste Artikel zuerst", "form.prefs.select.recent_first": "Neueste Artikel zuerst",
"form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren",
"form.import.label.file": "OPML Datei", "form.import.label.file": "OPML Datei",
"form.integration.fever_activate": "Fever API aktivieren", "form.integration.fever_activate": "Fever API aktivieren",
"form.integration.fever_username": "Fever Benutzername", "form.integration.fever_username": "Fever Benutzername",

View File

@ -216,6 +216,7 @@
"form.prefs.label.entry_sorting": "Entry Sorting", "form.prefs.label.entry_sorting": "Entry Sorting",
"form.prefs.select.older_first": "Older entries first", "form.prefs.select.older_first": "Older entries first",
"form.prefs.select.recent_first": "Recent entries first", "form.prefs.select.recent_first": "Recent entries first",
"form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts",
"form.import.label.file": "OPML file", "form.import.label.file": "OPML file",
"form.integration.fever_activate": "Activate Fever API", "form.integration.fever_activate": "Activate Fever API",
"form.integration.fever_username": "Fever Username", "form.integration.fever_username": "Fever Username",

View File

@ -216,6 +216,7 @@
"form.prefs.label.entry_sorting": "Clasificación de entradas", "form.prefs.label.entry_sorting": "Clasificación de entradas",
"form.prefs.select.older_first": "Entradas más viejas primero", "form.prefs.select.older_first": "Entradas más viejas primero",
"form.prefs.select.recent_first": "Entradas recientes primero", "form.prefs.select.recent_first": "Entradas recientes primero",
"form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado",
"form.import.label.file": "Archivo OPML", "form.import.label.file": "Archivo OPML",
"form.integration.fever_activate": "Activar API de Fever", "form.integration.fever_activate": "Activar API de Fever",
"form.integration.fever_username": "Nombre de usuario de Fever", "form.integration.fever_username": "Nombre de usuario de Fever",

View File

@ -216,6 +216,7 @@
"form.prefs.label.entry_sorting": "Ordre des éléments", "form.prefs.label.entry_sorting": "Ordre des éléments",
"form.prefs.select.older_first": "Ancien éléments en premier", "form.prefs.select.older_first": "Ancien éléments en premier",
"form.prefs.select.recent_first": "Éléments récents en premier", "form.prefs.select.recent_first": "Éléments récents en premier",
"form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier",
"form.import.label.file": "Fichier OPML", "form.import.label.file": "Fichier OPML",
"form.integration.fever_activate": "Activer l'API de Fever", "form.integration.fever_activate": "Activer l'API de Fever",
"form.integration.fever_username": "Nom d'utilisateur pour l'API de Fever", "form.integration.fever_username": "Nom d'utilisateur pour l'API de Fever",

View File

@ -216,6 +216,7 @@
"form.prefs.label.entry_sorting": "Ordinamento articoli", "form.prefs.label.entry_sorting": "Ordinamento articoli",
"form.prefs.select.older_first": "Prima i più recenti", "form.prefs.select.older_first": "Prima i più recenti",
"form.prefs.select.recent_first": "Prima i più vecchi", "form.prefs.select.recent_first": "Prima i più vecchi",
"form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera",
"form.import.label.file": "File OPML", "form.import.label.file": "File OPML",
"form.integration.fever_activate": "Abilita l'API di Fever", "form.integration.fever_activate": "Abilita l'API di Fever",
"form.integration.fever_username": "Nome utente dell'account Fever", "form.integration.fever_username": "Nome utente dell'account Fever",

View File

@ -216,6 +216,7 @@
"form.prefs.label.entry_sorting": "Volgorde van items", "form.prefs.label.entry_sorting": "Volgorde van items",
"form.prefs.select.older_first": "Oudere items eerst", "form.prefs.select.older_first": "Oudere items eerst",
"form.prefs.select.recent_first": "Recente items eerst", "form.prefs.select.recent_first": "Recente items eerst",
"form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in",
"form.import.label.file": "OPML-bestand", "form.import.label.file": "OPML-bestand",
"form.integration.fever_activate": "Activeer Fever API", "form.integration.fever_activate": "Activeer Fever API",
"form.integration.fever_username": "Fever gebruikersnaam", "form.integration.fever_username": "Fever gebruikersnaam",

View File

@ -217,6 +217,7 @@
"form.prefs.label.theme": "Wygląd", "form.prefs.label.theme": "Wygląd",
"form.prefs.label.entry_sorting": "Sortowanie artykułów", "form.prefs.label.entry_sorting": "Sortowanie artykułów",
"form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze", "form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze",
"form.prefs.label.keyboard_shortcuts": "Włącz skróty klawiaturowe",
"form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze", "form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze",
"form.import.label.file": "Plik OPML", "form.import.label.file": "Plik OPML",
"form.integration.fever_activate": "Aktywuj Fever API", "form.integration.fever_activate": "Aktywuj Fever API",

View File

@ -218,6 +218,7 @@
"form.prefs.label.entry_sorting": "Сортировка записей", "form.prefs.label.entry_sorting": "Сортировка записей",
"form.prefs.select.older_first": "Сначала старые записи", "form.prefs.select.older_first": "Сначала старые записи",
"form.prefs.select.recent_first": "Сначала последние записи", "form.prefs.select.recent_first": "Сначала последние записи",
"form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",
"form.import.label.file": "OPML файл", "form.import.label.file": "OPML файл",
"form.integration.fever_activate": "Активировать Fever API", "form.integration.fever_activate": "Активировать Fever API",
"form.integration.fever_username": "Имя пользователя Fever", "form.integration.fever_username": "Имя пользователя Fever",

View File

@ -214,6 +214,7 @@
"form.prefs.label.entry_sorting": "内容排序", "form.prefs.label.entry_sorting": "内容排序",
"form.prefs.select.older_first": "旧->新", "form.prefs.select.older_first": "旧->新",
"form.prefs.select.recent_first": "新->旧", "form.prefs.select.recent_first": "新->旧",
"form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",
"form.import.label.file": "OPML 文件", "form.import.label.file": "OPML 文件",
"form.integration.fever_activate": "启用 Fever API", "form.integration.fever_activate": "启用 Fever API",
"form.integration.fever_username": "Fever 用户名", "form.integration.fever_username": "Fever 用户名",

View File

@ -13,16 +13,17 @@ import (
// User represents a user in the system. // User represents a user in the system.
type User struct { type User struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin"`
Theme string `json:"theme"` Theme string `json:"theme"`
Language string `json:"language"` Language string `json:"language"`
Timezone string `json:"timezone"` Timezone string `json:"timezone"`
EntryDirection string `json:"entry_sorting_direction"` EntryDirection string `json:"entry_sorting_direction"`
LastLoginAt *time.Time `json:"last_login_at,omitempty"` KeyboardShortcuts bool `json:"keyboard_shortcuts"`
Extra map[string]string `json:"extra"` LastLoginAt *time.Time `json:"last_login_at,omitempty"`
Extra map[string]string `json:"extra"`
} }
// NewUser returns a new User. // NewUser returns a new User.

View File

@ -67,11 +67,14 @@ func (s *Storage) CreateUser(user *model.User) (err error) {
} }
} }
query := `INSERT INTO users query := `
(username, password, is_admin, extra) INSERT INTO users
(username, password, is_admin, extra)
VALUES VALUES
(LOWER($1), $2, $3, $4) (LOWER($1), $2, $3, $4)
RETURNING id, username, is_admin, language, theme, timezone, entry_direction` RETURNING
id, username, is_admin, language, theme, timezone, entry_direction, keyboard_shortcuts
`
err = s.db.QueryRow(query, user.Username, password, user.IsAdmin, extra).Scan( err = s.db.QueryRow(query, user.Username, password, user.IsAdmin, extra).Scan(
&user.ID, &user.ID,
@ -81,6 +84,7 @@ func (s *Storage) CreateUser(user *model.User) (err error) {
&user.Theme, &user.Theme,
&user.Timezone, &user.Timezone,
&user.EntryDirection, &user.EntryDirection,
&user.KeyboardShortcuts,
) )
if err != nil { if err != nil {
return fmt.Errorf("unable to create user: %v", err) return fmt.Errorf("unable to create user: %v", err)
@ -121,15 +125,19 @@ func (s *Storage) UpdateUser(user *model.User) error {
return err return err
} }
query := `UPDATE users SET query := `
username=LOWER($1), UPDATE users SET
password=$2, username=LOWER($1),
is_admin=$3, password=$2,
theme=$4, is_admin=$3,
language=$5, theme=$4,
timezone=$6, language=$5,
entry_direction=$7 timezone=$6,
WHERE id=$8` entry_direction=$7,
keyboard_shortcuts=$8
WHERE
id=$9
`
_, err = s.db.Exec( _, err = s.db.Exec(
query, query,
@ -140,20 +148,25 @@ func (s *Storage) UpdateUser(user *model.User) error {
user.Language, user.Language,
user.Timezone, user.Timezone,
user.EntryDirection, user.EntryDirection,
user.KeyboardShortcuts,
user.ID, user.ID,
) )
if err != nil { if err != nil {
return fmt.Errorf("unable to update user: %v", err) return fmt.Errorf("unable to update user: %v", err)
} }
} else { } else {
query := `UPDATE users SET query := `
username=LOWER($1), UPDATE users SET
is_admin=$2, username=LOWER($1),
theme=$3, is_admin=$2,
language=$4, theme=$3,
timezone=$5, language=$4,
entry_direction=$6 timezone=$5,
WHERE id=$7` entry_direction=$6,
keyboard_shortcuts=$7
WHERE
id=$8
`
_, err := s.db.Exec( _, err := s.db.Exec(
query, query,
@ -163,6 +176,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
user.Language, user.Language,
user.Timezone, user.Timezone,
user.EntryDirection, user.EntryDirection,
user.KeyboardShortcuts,
user.ID, user.ID,
) )
@ -188,10 +202,15 @@ func (s *Storage) UserLanguage(userID int64) (language string) {
// UserByID finds a user by the ID. // UserByID finds a user by the ID.
func (s *Storage) UserByID(userID int64) (*model.User, error) { func (s *Storage) UserByID(userID int64) (*model.User, error) {
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByID] userID=%d", userID)) defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByID] userID=%d", userID))
query := `SELECT query := `
id, username, is_admin, theme, language, timezone, entry_direction, last_login_at, extra SELECT
FROM users id, username, is_admin, theme, language, timezone, entry_direction, keyboard_shortcuts,
WHERE id = $1` last_login_at, extra
FROM
users
WHERE
id = $1
`
return s.fetchUser(query, userID) return s.fetchUser(query, userID)
} }
@ -199,10 +218,15 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) {
// UserByUsername finds a user by the username. // UserByUsername finds a user by the username.
func (s *Storage) UserByUsername(username string) (*model.User, error) { func (s *Storage) UserByUsername(username string) (*model.User, error) {
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByUsername] username=%s", username)) defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByUsername] username=%s", username))
query := `SELECT query := `
id, username, is_admin, theme, language, timezone, entry_direction, last_login_at, extra SELECT
FROM users id, username, is_admin, theme, language, timezone, entry_direction, keyboard_shortcuts,
WHERE username=LOWER($1)` last_login_at, extra
FROM
users
WHERE
username=LOWER($1)
`
return s.fetchUser(query, username) return s.fetchUser(query, username)
} }
@ -210,10 +234,15 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) {
// UserByExtraField finds a user by an extra field value. // UserByExtraField finds a user by an extra field value.
func (s *Storage) UserByExtraField(field, value string) (*model.User, error) { func (s *Storage) UserByExtraField(field, value string) (*model.User, error) {
defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByExtraField] field=%s", field)) defer timer.ExecutionTime(time.Now(), fmt.Sprintf("[Storage:UserByExtraField] field=%s", field))
query := `SELECT query := `
id, username, is_admin, theme, language, timezone, entry_direction, last_login_at, extra SELECT
FROM users id, username, is_admin, theme, language, timezone, entry_direction, keyboard_shortcuts,
WHERE extra->$1=$2` last_login_at, extra
FROM
users
WHERE
extra->$1=$2
`
return s.fetchUser(query, field, value) return s.fetchUser(query, field, value)
} }
@ -230,6 +259,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err
&user.Language, &user.Language,
&user.Timezone, &user.Timezone,
&user.EntryDirection, &user.EntryDirection,
&user.KeyboardShortcuts,
&user.LastLoginAt, &user.LastLoginAt,
&extra, &extra,
) )
@ -275,9 +305,12 @@ func (s *Storage) Users() (model.Users, error) {
defer timer.ExecutionTime(time.Now(), "[Storage:Users]") defer timer.ExecutionTime(time.Now(), "[Storage:Users]")
query := ` query := `
SELECT SELECT
id, username, is_admin, theme, language, timezone, entry_direction, last_login_at, extra id, username, is_admin, theme, language, timezone, entry_direction, keyboard_shortcuts,
FROM users last_login_at, extra
ORDER BY username ASC` FROM
users
ORDER BY username ASC
`
rows, err := s.db.Query(query) rows, err := s.db.Query(query)
if err != nil { if err != nil {
@ -297,6 +330,7 @@ func (s *Storage) Users() (model.Users, error) {
&user.Language, &user.Language,
&user.Timezone, &user.Timezone,
&user.EntryDirection, &user.EntryDirection,
&user.KeyboardShortcuts,
&user.LastLoginAt, &user.LastLoginAt,
&extra, &extra,
) )

View File

@ -112,7 +112,9 @@ var templateCommonMap = map[string]string{
<script type="text/javascript" src="{{ route "javascript" "name" "app" }}?{{ .app_js_checksum }}" defer></script> <script type="text/javascript" src="{{ route "javascript" "name" "app" }}?{{ .app_js_checksum }}" defer></script>
<script type="text/javascript" src="{{ route "javascript" "name" "sw" }}?{{ .sw_js_checksum }}" defer id="service-worker-script"></script> <script type="text/javascript" src="{{ route "javascript" "name" "sw" }}?{{ .sw_js_checksum }}" defer id="service-worker-script"></script>
</head> </head>
<body data-entries-status-url="{{ route "updateEntriesStatus" }}"> <body
data-entries-status-url="{{ route "updateEntriesStatus" }}"
{{ if .user }}{{ if not .user.KeyboardShortcuts }}data-disable-keyboard-shortcuts="true"{{ end }}{{ end }}>
{{ if .user }} {{ if .user }}
<header class="header"> <header class="header">
<nav> <nav>
@ -245,6 +247,6 @@ var templateCommonMap = map[string]string{
var templateCommonMapChecksums = map[string]string{ var templateCommonMapChecksums = map[string]string{
"entry_pagination": "4faa91e2eae150c5e4eab4d258e039dfdd413bab7602f0009360e6d52898e353", "entry_pagination": "4faa91e2eae150c5e4eab4d258e039dfdd413bab7602f0009360e6d52898e353",
"item_meta": "34deb081a054f2948ad808bdb2c8603d6ab00c58f2f50c4ead0b47ae092888eb", "item_meta": "34deb081a054f2948ad808bdb2c8603d6ab00c58f2f50c4ead0b47ae092888eb",
"layout": "4a5339267f67b5999a22ece7584df4c75785bc3bf95d44e1891da763aaea7991", "layout": "838fb8ec4df4120ff63168c15d900e3734f52e4b7473fb1d45695e6b27540d11",
"pagination": "3386e90c6e1230311459e9a484629bc5d5bf39514a75ef2e73bbbc61142f7abb", "pagination": "3386e90c6e1230311459e9a484629bc5d5bf39514a75ef2e73bbbc61142f7abb",
} }

View File

@ -38,7 +38,9 @@
<script type="text/javascript" src="{{ route "javascript" "name" "app" }}?{{ .app_js_checksum }}" defer></script> <script type="text/javascript" src="{{ route "javascript" "name" "app" }}?{{ .app_js_checksum }}" defer></script>
<script type="text/javascript" src="{{ route "javascript" "name" "sw" }}?{{ .sw_js_checksum }}" defer id="service-worker-script"></script> <script type="text/javascript" src="{{ route "javascript" "name" "sw" }}?{{ .sw_js_checksum }}" defer id="service-worker-script"></script>
</head> </head>
<body data-entries-status-url="{{ route "updateEntriesStatus" }}"> <body
data-entries-status-url="{{ route "updateEntriesStatus" }}"
{{ if .user }}{{ if not .user.KeyboardShortcuts }}data-disable-keyboard-shortcuts="true"{{ end }}{{ end }}>
{{ if .user }} {{ if .user }}
<header class="header"> <header class="header">
<nav> <nav>

View File

@ -64,6 +64,8 @@
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option> <option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
</select> </select>
<label><input type="checkbox" name="keyboard_shortcuts" value="1" {{ if .form.KeyboardShortcuts }}checked{{ end }}> {{ t "form.prefs.label.keyboard_shortcuts" }}</label>
<div class="buttons"> <div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button> <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div> </div>

View File

@ -1282,6 +1282,8 @@ var templateViewsMap = map[string]string{
<option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option> <option value="desc" {{ if eq "desc" $.form.EntryDirection }}selected="selected"{{ end }}>{{ t "form.prefs.select.recent_first" }}</option>
</select> </select>
<label><input type="checkbox" name="keyboard_shortcuts" value="1" {{ if .form.KeyboardShortcuts }}checked{{ end }}> {{ t "form.prefs.label.keyboard_shortcuts" }}</label>
<div class="buttons"> <div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button> <button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div> </div>
@ -1437,7 +1439,7 @@ var templateViewsMapChecksums = map[string]string{
"login": "2e72d2d4b9786641b696bedbed5e10b04bdfd68254ddbbdb0a53cca621d200c7", "login": "2e72d2d4b9786641b696bedbed5e10b04bdfd68254ddbbdb0a53cca621d200c7",
"search_entries": "d71849a4f2b0573c7c76ad0ea941812009e9f022de60895987a781d3e6f08a01", "search_entries": "d71849a4f2b0573c7c76ad0ea941812009e9f022de60895987a781d3e6f08a01",
"sessions": "1b3ec0970a4111b81f86d6ed187bb410f88972e2ede6723b9febcc4c7e5fc921", "sessions": "1b3ec0970a4111b81f86d6ed187bb410f88972e2ede6723b9febcc4c7e5fc921",
"settings": "bc04faf83dd977306825973375954600bd014619340188e1243fd9e2f5d5e1a9", "settings": "152143e58d057ea6ab3bfd8dd947bfd70685843ca40e40542484b23849746df4",
"unread_entries": "880018cbc59ec09b23dd800c4010fadad944d7023e0d36a3872c09b5d4952799", "unread_entries": "880018cbc59ec09b23dd800c4010fadad944d7023e0d36a3872c09b5d4952799",
"users": "4b56cc76fbcc424e7c870d0efca93bb44dbfcc2a08b685cf799c773fbb8dfb2f", "users": "4b56cc76fbcc424e7c870d0efca93bb44dbfcc2a08b685cf799c773fbb8dfb2f",
} }

View File

@ -13,13 +13,14 @@ import (
// SettingsForm represents the settings form. // SettingsForm represents the settings form.
type SettingsForm struct { type SettingsForm struct {
Username string Username string
Password string Password string
Confirmation string Confirmation string
Theme string Theme string
Language string Language string
Timezone string Timezone string
EntryDirection string EntryDirection string
KeyboardShortcuts bool
} }
// Merge updates the fields of the given user. // Merge updates the fields of the given user.
@ -29,6 +30,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
user.Language = s.Language user.Language = s.Language
user.Timezone = s.Timezone user.Timezone = s.Timezone
user.EntryDirection = s.EntryDirection user.EntryDirection = s.EntryDirection
user.KeyboardShortcuts = s.KeyboardShortcuts
if s.Password != "" { if s.Password != "" {
user.Password = s.Password user.Password = s.Password
@ -64,12 +66,13 @@ func (s *SettingsForm) Validate() error {
// NewSettingsForm returns a new SettingsForm. // NewSettingsForm returns a new SettingsForm.
func NewSettingsForm(r *http.Request) *SettingsForm { func NewSettingsForm(r *http.Request) *SettingsForm {
return &SettingsForm{ return &SettingsForm{
Username: r.FormValue("username"), Username: r.FormValue("username"),
Password: r.FormValue("password"), Password: r.FormValue("password"),
Confirmation: r.FormValue("confirmation"), Confirmation: r.FormValue("confirmation"),
Theme: r.FormValue("theme"), Theme: r.FormValue("theme"),
Language: r.FormValue("language"), Language: r.FormValue("language"),
Timezone: r.FormValue("timezone"), Timezone: r.FormValue("timezone"),
EntryDirection: r.FormValue("entry_direction"), EntryDirection: r.FormValue("entry_direction"),
KeyboardShortcuts: r.FormValue("keyboard_shortcuts") == "1",
} }
} }

View File

@ -27,11 +27,12 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
} }
settingsForm := form.SettingsForm{ settingsForm := form.SettingsForm{
Username: user.Username, Username: user.Username,
Theme: user.Theme, Theme: user.Theme,
Language: user.Language, Language: user.Language,
Timezone: user.Timezone, Timezone: user.Timezone,
EntryDirection: user.EntryDirection, EntryDirection: user.EntryDirection,
KeyboardShortcuts: user.KeyboardShortcuts,
} }
timezones, err := h.store.Timezones() timezones, err := h.store.Timezones()

View File

@ -98,13 +98,14 @@ break;}}}
isEntry(){return document.querySelector("section.entry")!==null;} isEntry(){return document.querySelector("section.entry")!==null;}
isListView(){return document.querySelector(".items")!==null;}} isListView(){return document.querySelector(".items")!==null;}}
class LinkStateHandler{static flip(element){let labelElement=document.createElement("span");labelElement.className="link-flipped-state";labelElement.appendChild(document.createTextNode(element.dataset.labelNewState));element.parentNode.appendChild(labelElement);element.parentNode.removeChild(element);}} class LinkStateHandler{static flip(element){let labelElement=document.createElement("span");labelElement.className="link-flipped-state";labelElement.appendChild(document.createTextNode(element.dataset.labelNewState));element.parentNode.appendChild(labelElement);element.parentNode.removeChild(element);}}
document.addEventListener("DOMContentLoaded",function(){FormHandler.handleSubmitButtons();let navHandler=new NavHandler();let keyboardHandler=new KeyboardHandler();keyboardHandler.on("g u",()=>navHandler.goToPage("unread"));keyboardHandler.on("g b",()=>navHandler.goToPage("starred"));keyboardHandler.on("g h",()=>navHandler.goToPage("history"));keyboardHandler.on("g f",()=>navHandler.goToFeedOrFeeds());keyboardHandler.on("g c",()=>navHandler.goToPage("categories"));keyboardHandler.on("g s",()=>navHandler.goToPage("settings"));keyboardHandler.on("ArrowLeft",()=>navHandler.goToPrevious());keyboardHandler.on("ArrowRight",()=>navHandler.goToNext());keyboardHandler.on("j",()=>navHandler.goToPrevious());keyboardHandler.on("p",()=>navHandler.goToPrevious());keyboardHandler.on("k",()=>navHandler.goToNext());keyboardHandler.on("n",()=>navHandler.goToNext());keyboardHandler.on("h",()=>navHandler.goToPage("previous"));keyboardHandler.on("l",()=>navHandler.goToPage("next"));keyboardHandler.on("o",()=>navHandler.openSelectedItem());keyboardHandler.on("v",()=>navHandler.openOriginalLink());keyboardHandler.on("m",()=>navHandler.toggleEntryStatus());keyboardHandler.on("A",()=>{let element=document.querySelector("a[data-on-click=markPageAsRead]");navHandler.markPageAsRead(element.dataset.showOnlyUnread||false);});keyboardHandler.on("s",()=>navHandler.saveEntry());keyboardHandler.on("d",()=>navHandler.fetchOriginalContent());keyboardHandler.on("f",()=>navHandler.toggleBookmark());keyboardHandler.on("?",()=>navHandler.showKeyboardShortcuts());keyboardHandler.on("#",()=>navHandler.unsubscribeFromFeed());keyboardHandler.on("/",(e)=>navHandler.setFocusToSearchInput(e));keyboardHandler.on("Escape",()=>ModalHandler.close());keyboardHandler.listen();let touchHandler=new TouchHandler(navHandler);touchHandler.listen();let mouseHandler=new MouseHandler();mouseHandler.onClick("a[data-save-entry]",(event)=>{EntryHandler.saveEntry(event.target);});mouseHandler.onClick("a[data-toggle-bookmark]",(event)=>{EntryHandler.toggleBookmark(event.target);});mouseHandler.onClick("a[data-toggle-status]",(event)=>{let currentItem=DomHelper.findParent(event.target,"entry");if(!currentItem){currentItem=DomHelper.findParent(event.target,"item");} document.addEventListener("DOMContentLoaded",function(){FormHandler.handleSubmitButtons();let navHandler=new NavHandler();if(!document.querySelector("body[data-disable-keyboard-shortcuts=true]")){let keyboardHandler=new KeyboardHandler();keyboardHandler.on("g u",()=>navHandler.goToPage("unread"));keyboardHandler.on("g b",()=>navHandler.goToPage("starred"));keyboardHandler.on("g h",()=>navHandler.goToPage("history"));keyboardHandler.on("g f",()=>navHandler.goToFeedOrFeeds());keyboardHandler.on("g c",()=>navHandler.goToPage("categories"));keyboardHandler.on("g s",()=>navHandler.goToPage("settings"));keyboardHandler.on("ArrowLeft",()=>navHandler.goToPrevious());keyboardHandler.on("ArrowRight",()=>navHandler.goToNext());keyboardHandler.on("j",()=>navHandler.goToPrevious());keyboardHandler.on("p",()=>navHandler.goToPrevious());keyboardHandler.on("k",()=>navHandler.goToNext());keyboardHandler.on("n",()=>navHandler.goToNext());keyboardHandler.on("h",()=>navHandler.goToPage("previous"));keyboardHandler.on("l",()=>navHandler.goToPage("next"));keyboardHandler.on("o",()=>navHandler.openSelectedItem());keyboardHandler.on("v",()=>navHandler.openOriginalLink());keyboardHandler.on("m",()=>navHandler.toggleEntryStatus());keyboardHandler.on("A",()=>{let element=document.querySelector("a[data-on-click=markPageAsRead]");navHandler.markPageAsRead(element.dataset.showOnlyUnread||false);});keyboardHandler.on("s",()=>navHandler.saveEntry());keyboardHandler.on("d",()=>navHandler.fetchOriginalContent());keyboardHandler.on("f",()=>navHandler.toggleBookmark());keyboardHandler.on("?",()=>navHandler.showKeyboardShortcuts());keyboardHandler.on("#",()=>navHandler.unsubscribeFromFeed());keyboardHandler.on("/",(e)=>navHandler.setFocusToSearchInput(e));keyboardHandler.on("Escape",()=>ModalHandler.close());keyboardHandler.listen();}
let touchHandler=new TouchHandler(navHandler);touchHandler.listen();let mouseHandler=new MouseHandler();mouseHandler.onClick("a[data-save-entry]",(event)=>{EntryHandler.saveEntry(event.target);});mouseHandler.onClick("a[data-toggle-bookmark]",(event)=>{EntryHandler.toggleBookmark(event.target);});mouseHandler.onClick("a[data-toggle-status]",(event)=>{let currentItem=DomHelper.findParent(event.target,"entry");if(!currentItem){currentItem=DomHelper.findParent(event.target,"item");}
if(currentItem){EntryHandler.toggleEntryStatus(currentItem);}});mouseHandler.onClick("a[data-fetch-content-entry]",(event)=>{EntryHandler.fetchOriginalContent(event.target);});mouseHandler.onClick("a[data-on-click=markPageAsRead]",(event)=>{navHandler.markPageAsRead(event.target.dataset.showOnlyUnread||false);});mouseHandler.onClick("a[data-confirm]",(event)=>{(new ConfirmHandler()).handle(event);});mouseHandler.onClick("a[data-action=search]",(event)=>{navHandler.setFocusToSearchInput(event);});mouseHandler.onClick("a[data-link-state=flip]",(event)=>{LinkStateHandler.flip(event.target);},true);if(document.documentElement.clientWidth<600){let menuHandler=new MenuHandler();mouseHandler.onClick(".logo",()=>menuHandler.toggleMainMenu());mouseHandler.onClick(".header nav li",(event)=>menuHandler.clickMenuListItem(event));} if(currentItem){EntryHandler.toggleEntryStatus(currentItem);}});mouseHandler.onClick("a[data-fetch-content-entry]",(event)=>{EntryHandler.fetchOriginalContent(event.target);});mouseHandler.onClick("a[data-on-click=markPageAsRead]",(event)=>{navHandler.markPageAsRead(event.target.dataset.showOnlyUnread||false);});mouseHandler.onClick("a[data-confirm]",(event)=>{(new ConfirmHandler()).handle(event);});mouseHandler.onClick("a[data-action=search]",(event)=>{navHandler.setFocusToSearchInput(event);});mouseHandler.onClick("a[data-link-state=flip]",(event)=>{LinkStateHandler.flip(event.target);},true);if(document.documentElement.clientWidth<600){let menuHandler=new MenuHandler();mouseHandler.onClick(".logo",()=>menuHandler.toggleMainMenu());mouseHandler.onClick(".header nav li",(event)=>menuHandler.clickMenuListItem(event));}
if("serviceWorker"in navigator){let scriptElement=document.getElementById("service-worker-script");if(scriptElement){navigator.serviceWorker.register(scriptElement.src);}}});})();`, if("serviceWorker"in navigator){let scriptElement=document.getElementById("service-worker-script");if(scriptElement){navigator.serviceWorker.register(scriptElement.src);}}});})();`,
"sw": `'use strict';self.addEventListener("fetch",(event)=>{if(event.request.url.includes("/feed/icon/")){event.respondWith(caches.open("feed_icons").then((cache)=>{return cache.match(event.request).then((response)=>{return response||fetch(event.request).then((response)=>{cache.put(event.request,response.clone());return response;});});}));}});`, "sw": `'use strict';self.addEventListener("fetch",(event)=>{if(event.request.url.includes("/feed/icon/")){event.respondWith(caches.open("feed_icons").then((cache)=>{return cache.match(event.request).then((response)=>{return response||fetch(event.request).then((response)=>{cache.put(event.request,response.clone());return response;});});}));}});`,
} }
var JavascriptsChecksums = map[string]string{ var JavascriptsChecksums = map[string]string{
"app": "a3b8e1f0b369fc0d543a67ee714bcdcbe5823bab0978118580e95e6c5cdecd2b", "app": "fc9e4a86bde34308c169039227376cf2dfe7311ce4bf06c9a417316714bab273",
"sw": "55fffa223919cc18572788fb9c62fccf92166c0eb5d3a1d6f91c31f24d020be9", "sw": "55fffa223919cc18572788fb9c62fccf92166c0eb5d3a1d6f91c31f24d020be9",
} }

View File

@ -2,36 +2,39 @@ document.addEventListener("DOMContentLoaded", function() {
FormHandler.handleSubmitButtons(); FormHandler.handleSubmitButtons();
let navHandler = new NavHandler(); let navHandler = new NavHandler();
let keyboardHandler = new KeyboardHandler();
keyboardHandler.on("g u", () => navHandler.goToPage("unread")); if (! document.querySelector("body[data-disable-keyboard-shortcuts=true]")) {
keyboardHandler.on("g b", () => navHandler.goToPage("starred")); let keyboardHandler = new KeyboardHandler();
keyboardHandler.on("g h", () => navHandler.goToPage("history")); keyboardHandler.on("g u", () => navHandler.goToPage("unread"));
keyboardHandler.on("g f", () => navHandler.goToFeedOrFeeds()); keyboardHandler.on("g b", () => navHandler.goToPage("starred"));
keyboardHandler.on("g c", () => navHandler.goToPage("categories")); keyboardHandler.on("g h", () => navHandler.goToPage("history"));
keyboardHandler.on("g s", () => navHandler.goToPage("settings")); keyboardHandler.on("g f", () => navHandler.goToFeedOrFeeds());
keyboardHandler.on("ArrowLeft", () => navHandler.goToPrevious()); keyboardHandler.on("g c", () => navHandler.goToPage("categories"));
keyboardHandler.on("ArrowRight", () => navHandler.goToNext()); keyboardHandler.on("g s", () => navHandler.goToPage("settings"));
keyboardHandler.on("j", () => navHandler.goToPrevious()); keyboardHandler.on("ArrowLeft", () => navHandler.goToPrevious());
keyboardHandler.on("p", () => navHandler.goToPrevious()); keyboardHandler.on("ArrowRight", () => navHandler.goToNext());
keyboardHandler.on("k", () => navHandler.goToNext()); keyboardHandler.on("j", () => navHandler.goToPrevious());
keyboardHandler.on("n", () => navHandler.goToNext()); keyboardHandler.on("p", () => navHandler.goToPrevious());
keyboardHandler.on("h", () => navHandler.goToPage("previous")); keyboardHandler.on("k", () => navHandler.goToNext());
keyboardHandler.on("l", () => navHandler.goToPage("next")); keyboardHandler.on("n", () => navHandler.goToNext());
keyboardHandler.on("o", () => navHandler.openSelectedItem()); keyboardHandler.on("h", () => navHandler.goToPage("previous"));
keyboardHandler.on("v", () => navHandler.openOriginalLink()); keyboardHandler.on("l", () => navHandler.goToPage("next"));
keyboardHandler.on("m", () => navHandler.toggleEntryStatus()); keyboardHandler.on("o", () => navHandler.openSelectedItem());
keyboardHandler.on("A", () => { keyboardHandler.on("v", () => navHandler.openOriginalLink());
let element = document.querySelector("a[data-on-click=markPageAsRead]"); keyboardHandler.on("m", () => navHandler.toggleEntryStatus());
navHandler.markPageAsRead(element.dataset.showOnlyUnread || false); keyboardHandler.on("A", () => {
}); let element = document.querySelector("a[data-on-click=markPageAsRead]");
keyboardHandler.on("s", () => navHandler.saveEntry()); navHandler.markPageAsRead(element.dataset.showOnlyUnread || false);
keyboardHandler.on("d", () => navHandler.fetchOriginalContent()); });
keyboardHandler.on("f", () => navHandler.toggleBookmark()); keyboardHandler.on("s", () => navHandler.saveEntry());
keyboardHandler.on("?", () => navHandler.showKeyboardShortcuts()); keyboardHandler.on("d", () => navHandler.fetchOriginalContent());
keyboardHandler.on("#", () => navHandler.unsubscribeFromFeed()); keyboardHandler.on("f", () => navHandler.toggleBookmark());
keyboardHandler.on("/", (e) => navHandler.setFocusToSearchInput(e)); keyboardHandler.on("?", () => navHandler.showKeyboardShortcuts());
keyboardHandler.on("Escape", () => ModalHandler.close()); keyboardHandler.on("#", () => navHandler.unsubscribeFromFeed());
keyboardHandler.listen(); keyboardHandler.on("/", (e) => navHandler.setFocusToSearchInput(e));
keyboardHandler.on("Escape", () => ModalHandler.close());
keyboardHandler.listen();
}
let touchHandler = new TouchHandler(navHandler); let touchHandler = new TouchHandler(navHandler);
touchHandler.listen(); touchHandler.listen();

View File

@ -64,5 +64,4 @@ class KeyboardHandler {
return event.key; return event.key;
} }
} }