Merge branch 'main' into switch-to-vite
commit
d1e59fe08c
|
@ -1222,8 +1222,12 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
|
|||
|
||||
### ntfy server v2.6.0 (UNRELEASED)
|
||||
|
||||
**Bug fixes + maintenance:**
|
||||
**Bug fixes:**
|
||||
|
||||
* Support encoding any header as RFC 2047 ([#737](https://github.com/binwiederhier/ntfy/issues/737), thanks to [@cfouche3005](https://github.com/cfouche3005) for reporting)
|
||||
|
||||
**Maintenance:**
|
||||
|
||||
* Improved GitHub Actions flow ([#745](https://github.com/binwiederhier/ntfy/pull/745), thanks to [@nimbleghost](https://github.com/nimbleghost))
|
||||
* Web: Add JS formatter "prettier" ([#746](https://github.com/binwiederhier/ntfy/pull/746), thanks to [@nimbleghost](https://github.com/nimbleghost))
|
||||
* Web: Add eslint with eslint-config-airbnb ([#748](https://github.com/binwiederhier/ntfy/pull/748), thanks to [@nimbleghost](https://github.com/nimbleghost))
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
"react/destructuring-assignment": "off",
|
||||
"react/jsx-no-useless-fragment": "off",
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"react/jsx-no-duplicate-props": [
|
||||
"error",
|
||||
{
|
||||
"ignoreCase": false // For <TextField>'s [iI]nputProps
|
||||
}
|
||||
],
|
||||
"react/function-component-definition": [
|
||||
"error",
|
||||
{
|
||||
|
|
|
@ -352,5 +352,24 @@
|
|||
"account_upgrade_dialog_interval_yearly_discount_save_up_to": "économisez jusqu'à {{discount}}%",
|
||||
"account_upgrade_dialog_tier_price_per_month": "mois",
|
||||
"account_upgrade_dialog_tier_price_billed_yearly": "{{price}} prélevé annuellement. Économisez {{save}}.",
|
||||
"account_upgrade_dialog_billing_contact_email": "Pour des questions concernant la facturation, merci de nous <Link>contacter</Link> directement."
|
||||
"account_upgrade_dialog_billing_contact_email": "Pour des questions concernant la facturation, merci de nous <Link>contacter</Link> directement.",
|
||||
"publish_dialog_call_label": "Appel téléphonique",
|
||||
"account_basics_phone_numbers_title": "Numéros de téléphone",
|
||||
"account_basics_phone_numbers_dialog_description": "Pour utiliser la fonctionnalité de notification par appels, vous devez ajouter et vérifier au moins un numéro de téléphone. La vérification peut se faire par SMS ou appel téléphonique.",
|
||||
"account_basics_phone_numbers_description": "Pour des notifications par appel téléphoniques",
|
||||
"account_basics_phone_numbers_no_phone_numbers_yet": "Pas encore de numéros de téléphone",
|
||||
"account_basics_phone_numbers_copied_to_clipboard": "Numéro de téléphone copié dans le presse-papier",
|
||||
"account_basics_phone_numbers_dialog_title": "Ajouter un numéro de téléphone",
|
||||
"account_basics_phone_numbers_dialog_number_label": "Numéro de téléphone",
|
||||
"account_basics_phone_numbers_dialog_number_placeholder": "Ex : +33701020304",
|
||||
"account_basics_phone_numbers_dialog_verify_button_sms": "Envoyer un SMS",
|
||||
"account_basics_phone_numbers_dialog_verify_button_call": "Appelez moi",
|
||||
"account_basics_phone_numbers_dialog_code_label": "Code de vérification",
|
||||
"account_basics_phone_numbers_dialog_code_placeholder": "Ex : 123456",
|
||||
"account_basics_phone_numbers_dialog_check_verification_button": "Code de confirmarion",
|
||||
"account_basics_phone_numbers_dialog_channel_sms": "SMS",
|
||||
"account_basics_phone_numbers_dialog_channel_call": "Appel",
|
||||
"account_usage_calls_none": "Aucun appels téléphoniques ne peut être fait avec ce compte",
|
||||
"publish_dialog_call_reset": "Supprimer les appels téléphoniques",
|
||||
"publish_dialog_chip_call_label": "Appel téléphonique"
|
||||
}
|
||||
|
|
|
@ -295,5 +295,62 @@
|
|||
"account_usage_messages_title": "Опубліковані повідомлення",
|
||||
"account_usage_emails_title": "Надіслані електронні листи",
|
||||
"account_usage_reservations_title": "Зарезервовані теми",
|
||||
"account_usage_reservations_none": "Для цього облікового запису немає зарезервованих тем"
|
||||
"account_usage_reservations_none": "Для цього облікового запису немає зарезервованих тем",
|
||||
"account_upgrade_dialog_tier_features_attachment_file_size": "{{filesize}} на файл",
|
||||
"account_upgrade_dialog_tier_features_attachment_total_size": "{{totalsize}} загальне сховище",
|
||||
"account_upgrade_dialog_tier_current_label": "Поточний",
|
||||
"account_upgrade_dialog_tier_selected_label": "Вибране",
|
||||
"account_upgrade_dialog_cancel_warning": "Це <strong> скасує вашу підписку</strong> і знизить версію вашого облікового запису {{date}}. У цю дату резервування тем, а також повідомлення, кешовані на сервері <strong>, буде видалено</strong>.",
|
||||
"account_upgrade_dialog_tier_features_reservations_other": "{{reservations}} зарезервовані теми",
|
||||
"account_upgrade_dialog_tier_features_no_reservations": "Немає зарезервованих тем",
|
||||
"account_upgrade_dialog_tier_features_messages_other": "{{messages}} повідомлень в день",
|
||||
"account_upgrade_dialog_tier_features_emails_one": "{{emails}} електронний лист в день",
|
||||
"account_upgrade_dialog_tier_features_emails_other": "{{emails}} електронних листів в день",
|
||||
"account_upgrade_dialog_tier_features_calls_one": "{{calls}} телефонний дзвінок в день",
|
||||
"account_upgrade_dialog_tier_features_calls_other": "{{дзвінки}} телефонних дзвінків в день",
|
||||
"account_upgrade_dialog_tier_features_no_calls": "Без телефонних дзвінків",
|
||||
"account_upgrade_dialog_tier_price_per_month": "місяць",
|
||||
"account_upgrade_dialog_tier_price_billed_monthly": "{{price}} на рік. Рахунок виставляється щомісяця.",
|
||||
"account_upgrade_dialog_tier_price_billed_yearly": "{{price}} виставляється щорічно. Збережіть {{save}}.",
|
||||
"account_upgrade_dialog_billing_contact_email": "Якщо у вас виникли запитання щодо оплати, <Link>зв’яжіться з нами</Link> безпосередньо.",
|
||||
"account_upgrade_dialog_billing_contact_website": "Якщо у вас виникли запитання щодо оплати, відвідайте наш <Link>веб-сайт</Link>.",
|
||||
"account_upgrade_dialog_button_cancel_subscription": "Скасувати підписку",
|
||||
"account_upgrade_dialog_button_update_subscription": "Оновити підписку",
|
||||
"account_tokens_title": "Токени доступу",
|
||||
"account_tokens_table_expires_header": "Термін дії закінчується",
|
||||
"account_tokens_description": "Використовуйте токени доступу при публікації та підписці через ntfy API, щоб не надсилати свої облікові дані. Ознайомтеся з <Link>документацією</Link>, щоб дізнатися більше.",
|
||||
"account_tokens_table_token_header": "Токен",
|
||||
"account_tokens_table_never_expires": "Ніколи не закінчується",
|
||||
"account_tokens_table_label_header": "Мітка",
|
||||
"account_tokens_table_current_session": "Поточний сеанс браузера",
|
||||
"account_tokens_table_last_access_header": "Останній доступ",
|
||||
"account_tokens_table_copied_to_clipboard": "Токен доступу скопійовано",
|
||||
"account_tokens_table_cannot_delete_or_edit": "Неможливо редагувати або видалити токен поточного сеансу",
|
||||
"account_tokens_table_create_token_button": "Створити токен доступу",
|
||||
"account_tokens_table_last_origin_tooltip": "З IP-адреси {{ip}} натисніть для пошуку",
|
||||
"account_tokens_dialog_title_create": "Створити токен доступу",
|
||||
"account_tokens_dialog_button_cancel": "Скасувати",
|
||||
"account_tokens_dialog_title_edit": "Редагувати токен доступу",
|
||||
"account_tokens_dialog_title_delete": "Видалити токен доступу",
|
||||
"account_tokens_dialog_label": "Мітка, наприклад, сповіщення Radarr",
|
||||
"account_tokens_dialog_button_create": "Створити токен",
|
||||
"account_tokens_dialog_button_update": "Оновити токен",
|
||||
"account_tokens_dialog_expires_label": "Термін дії токену доступу закінчується через",
|
||||
"account_tokens_dialog_expires_x_hours": "Термін дії токена закінчується через {{hours}} годин",
|
||||
"account_tokens_dialog_expires_x_days": "Термін дії токена закінчується через {{days}} днів",
|
||||
"account_tokens_delete_dialog_description": "Перш ніж видалити токен доступу, переконайтеся, що жодна програма або скрипт не використовує його. <strong>Ця дія не може бути скасована</strong>.",
|
||||
"prefs_users_description_no_sync": "Користувачі та паролі не синхронізуються з вашим акаунтом.",
|
||||
"prefs_users_table_cannot_delete_or_edit": "Неможливо видалити або відредагувати користувача, який увійшов у систему",
|
||||
"account_upgrade_dialog_tier_features_reservations_one": "{{reservations}} зарезервована тема",
|
||||
"account_upgrade_dialog_tier_features_messages_one": "{{messages}} повідомлення в день",
|
||||
"account_tokens_dialog_expires_unchanged": "Залишити термін придатності без змін",
|
||||
"account_tokens_dialog_expires_never": "Термін дії токена ніколи не закінчується",
|
||||
"account_tokens_delete_dialog_title": "Видалити токен доступу",
|
||||
"account_tokens_delete_dialog_submit_button": "Видалити токен назавжди",
|
||||
"account_upgrade_dialog_proration_info": "<strong>Пропорція</strong>: При переході з одного тарифного плану на інший різниця в ціні буде <strong>списана негайно</strong>. При переході на нижчий рівень залишок коштів буде використано для оплати майбутніх розрахункових періодів.",
|
||||
"account_upgrade_dialog_reservations_warning_one": "Обраний рівень дозволяє менше зарезервованих тем, ніж ваш поточний рівень. Перш ніж змінити свій рівень, <strong>будь ласка, видаліть принаймні одне резервування</strong>. Ви можете видалити резервування в <Link>Налаштуваннях</Link>.",
|
||||
"account_upgrade_dialog_reservations_warning_other": "Обраний рівень дозволяє менше зарезервованих тем, ніж ваш поточний рівень. Перш ніж змінити свій рівень, <strong>будь ласка, видаліть принаймні {{count}} резервувань</strong>. Ви можете видалити резервування в <Link>Налаштуваннях</Link>.",
|
||||
"account_upgrade_dialog_button_cancel": "Скасувати",
|
||||
"account_upgrade_dialog_button_redirect_signup": "Зареєструватися зараз",
|
||||
"account_upgrade_dialog_button_pay_now": "Оплатити зараз і підписатися"
|
||||
}
|
||||
|
|
|
@ -994,6 +994,7 @@ const TokenDialog = (props) => {
|
|||
|
||||
const TokenDeleteDialog = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
|
@ -1003,6 +1004,8 @@ const TokenDeleteDialog = (props) => {
|
|||
console.log(`[Account] Error deleting token`, e);
|
||||
if (e instanceof UnauthorizedError) {
|
||||
session.resetAndRedirect(routes.login);
|
||||
} else {
|
||||
setError(e.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1015,7 +1018,7 @@ const TokenDeleteDialog = (props) => {
|
|||
<Trans i18nKey="account_tokens_delete_dialog_description" />
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogFooter status>
|
||||
<DialogFooter status={error}>
|
||||
<Button onClick={props.onClose}>{t("common_cancel")}</Button>
|
||||
<Button onClick={handleSubmit} color="error">
|
||||
{t("account_tokens_delete_dialog_submit_button")}
|
||||
|
|
|
@ -27,14 +27,13 @@ export const AccountContext = createContext(null);
|
|||
|
||||
const App = () => {
|
||||
const [account, setAccount] = useState(null);
|
||||
|
||||
const contextValue = useMemo(() => ({ account, setAccount }), [account, setAccount]);
|
||||
const accountMemo = useMemo(() => ({ account, setAccount }), [account, setAccount]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Loader />}>
|
||||
<BrowserRouter>
|
||||
<ThemeProvider theme={theme}>
|
||||
<AccountContext.Provider value={contextValue}>
|
||||
<AccountContext.Provider value={accountMemo}>
|
||||
<CssBaseline />
|
||||
<ErrorBoundary>
|
||||
<Routes>
|
||||
|
|
|
@ -74,6 +74,8 @@ const EmojiPicker = (props) => {
|
|||
inputProps={{
|
||||
role: "searchbox",
|
||||
"aria-label": t("emoji_picker_search_placeholder"),
|
||||
}}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end" sx={{ display: search ? "" : "none" }}>
|
||||
<IconButton size="small" onClick={handleSearchClear} edge="end" aria-label={t("emoji_picker_search_clear")}>
|
||||
|
|
|
@ -45,9 +45,10 @@ class ErrorBoundaryImpl extends React.Component {
|
|||
// Fetch additional info and a better stack trace
|
||||
StackTrace.fromError(error).then((stack) => {
|
||||
console.error("[ErrorBoundary] Stacktrace fetched", stack);
|
||||
const niceStack = `${error.toString()}\n${stack
|
||||
.map((el) => ` at ${el.functionName} (${el.fileName}:${el.columnNumber}:${el.lineNumber})`)
|
||||
.join("\n")}`;
|
||||
const stackString = stack
|
||||
.map((el) => ` at ${el.functionName} (${el.fileName}:${el.columnNumber}:${el.lineNumber})`)
|
||||
.join("\n");
|
||||
const niceStack = `${error.toString()}\n${stackString}`;
|
||||
this.setState({ niceStack });
|
||||
});
|
||||
}
|
||||
|
|
|
@ -383,23 +383,23 @@ const PublishDialog = (props) => {
|
|||
"aria-label": t("publish_dialog_priority_label"),
|
||||
}}
|
||||
>
|
||||
{[5, 4, 3, 2, 1].map((priorityMenuItem) => (
|
||||
{[5, 4, 3, 2, 1].map((p) => (
|
||||
<MenuItem
|
||||
key={`priorityMenuItem${priorityMenuItem}`}
|
||||
value={priorityMenuItem}
|
||||
key={`priorityMenuItem${p}`}
|
||||
value={p}
|
||||
aria-label={t("notifications_priority_x", {
|
||||
priority: priorityMenuItem,
|
||||
priority: p,
|
||||
})}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<img
|
||||
src={priorities[priorityMenuItem].file}
|
||||
src={priorities[p].file}
|
||||
style={{ marginRight: "8px" }}
|
||||
alt={t("notifications_priority_x", {
|
||||
priority: priorityMenuItem,
|
||||
priority: p,
|
||||
})}
|
||||
/>
|
||||
<div>{priorities[priorityMenuItem].label}</div>
|
||||
<div>{priorities[p].label}</div>
|
||||
</div>
|
||||
</MenuItem>
|
||||
))}
|
||||
|
@ -477,10 +477,8 @@ const PublishDialog = (props) => {
|
|||
"aria-label": t("publish_dialog_call_label"),
|
||||
}}
|
||||
>
|
||||
{account?.phone_numbers?.map((phoneNumber, i) => (
|
||||
// TODO(eslint): Possibly just use the phone number as a key?
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<MenuItem key={`phoneNumberMenuItem${i}`} value={phoneNumber} aria-label={phoneNumber}>
|
||||
{account?.phone_numbers?.map((phoneNumber) => (
|
||||
<MenuItem key={phoneNumber} value={phoneNumber} aria-label={phoneNumber}>
|
||||
{t("publish_dialog_call_item", { number: phoneNumber })}
|
||||
</MenuItem>
|
||||
))}
|
||||
|
@ -834,7 +832,10 @@ const ExpandingTextField = (props) => {
|
|||
variant="standard"
|
||||
sx={{ width: `${textWidth}px`, borderBottom: "none" }}
|
||||
InputProps={{
|
||||
style: { fontSize: theme.typography[props.variant].fontSize, paddingBottom: 0, paddingTop: 0 },
|
||||
style: { fontSize: theme.typography[props.variant].fontSize },
|
||||
}}
|
||||
inputProps={{
|
||||
style: { paddingBottom: 0, paddingTop: 0 },
|
||||
"aria-label": props.placeholder,
|
||||
}}
|
||||
disabled={props.disabled}
|
||||
|
|
|
@ -247,6 +247,8 @@ const DisplayNameDialog = (props) => {
|
|||
inputProps={{
|
||||
maxLength: 64,
|
||||
"aria-label": t("display_name_dialog_placeholder"),
|
||||
}}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={() => setDisplayName("")} edge="end">
|
||||
|
|
Loading…
Reference in New Issue