useContext work in JS
This commit is contained in:
parent
a4529617cc
commit
b27c608508
17 changed files with 87 additions and 176 deletions
|
@ -79,7 +79,7 @@ var flagsServe = append(
|
||||||
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-signup", Aliases: []string{"enable_signup"}, EnvVars: []string{"NTFY_ENABLE_SIGNUP"}, Value: false, Usage: "xxx"}),
|
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-signup", Aliases: []string{"enable_signup"}, EnvVars: []string{"NTFY_ENABLE_SIGNUP"}, Value: false, Usage: "xxx"}),
|
||||||
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-login", Aliases: []string{"enable_login"}, EnvVars: []string{"NTFY_ENABLE_LOGIN"}, Value: false, Usage: "xxx"}),
|
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-login", Aliases: []string{"enable_login"}, EnvVars: []string{"NTFY_ENABLE_LOGIN"}, Value: false, Usage: "xxx"}),
|
||||||
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-payments", Aliases: []string{"enable_payments"}, EnvVars: []string{"NTFY_ENABLE_PAYMENTS"}, Value: false, Usage: "xxx"}),
|
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-payments", Aliases: []string{"enable_payments"}, EnvVars: []string{"NTFY_ENABLE_PAYMENTS"}, Value: false, Usage: "xxx"}),
|
||||||
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-reserve-topics", Aliases: []string{"enable_reserve_topics"}, EnvVars: []string{"NTFY_ENABLE_RESERVE_TOPICS"}, Value: false, Usage: "xxx"}),
|
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-reservations", Aliases: []string{"enable_reservations"}, EnvVars: []string{"NTFY_ENABLE_RESERVATIONS"}, Value: false, Usage: "xxx"}),
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdServe = &cli.Command{
|
var cmdServe = &cli.Command{
|
||||||
|
@ -151,7 +151,7 @@ func execServe(c *cli.Context) error {
|
||||||
enableSignup := c.Bool("enable-signup")
|
enableSignup := c.Bool("enable-signup")
|
||||||
enableLogin := c.Bool("enable-login")
|
enableLogin := c.Bool("enable-login")
|
||||||
enablePayments := c.Bool("enable-payments")
|
enablePayments := c.Bool("enable-payments")
|
||||||
enableReserveTopics := c.Bool("enable-reserve-topics")
|
enableReservations := c.Bool("enable-reservations")
|
||||||
|
|
||||||
// Check values
|
// Check values
|
||||||
if firebaseKeyFile != "" && !util.FileExists(firebaseKeyFile) {
|
if firebaseKeyFile != "" && !util.FileExists(firebaseKeyFile) {
|
||||||
|
@ -188,7 +188,7 @@ func execServe(c *cli.Context) error {
|
||||||
return errors.New("if upstream-base-url is set, base-url must also be set")
|
return errors.New("if upstream-base-url is set, base-url must also be set")
|
||||||
} else if upstreamBaseURL != "" && baseURL != "" && baseURL == upstreamBaseURL {
|
} else if upstreamBaseURL != "" && baseURL != "" && baseURL == upstreamBaseURL {
|
||||||
return errors.New("base-url and upstream-base-url cannot be identical, you'll likely want to set upstream-base-url to https://ntfy.sh, see https://ntfy.sh/docs/config/#ios-instant-notifications")
|
return errors.New("base-url and upstream-base-url cannot be identical, you'll likely want to set upstream-base-url to https://ntfy.sh, see https://ntfy.sh/docs/config/#ios-instant-notifications")
|
||||||
} else if authFile == "" && (enableSignup || enableLogin || enableReserveTopics || enablePayments) {
|
} else if authFile == "" && (enableSignup || enableLogin || enableReservations || enablePayments) {
|
||||||
return errors.New("cannot set enable-signup, enable-login, enable-reserve-topics, or enable-payments if auth-file is not set")
|
return errors.New("cannot set enable-signup, enable-login, enable-reserve-topics, or enable-payments if auth-file is not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +284,7 @@ func execServe(c *cli.Context) error {
|
||||||
conf.EnableSignup = enableSignup
|
conf.EnableSignup = enableSignup
|
||||||
conf.EnableLogin = enableLogin
|
conf.EnableLogin = enableLogin
|
||||||
conf.EnablePayments = enablePayments
|
conf.EnablePayments = enablePayments
|
||||||
conf.EnableReserveTopics = enableReserveTopics
|
conf.EnableReservations = enableReservations
|
||||||
conf.Version = c.App.Version
|
conf.Version = c.App.Version
|
||||||
|
|
||||||
// Set up hot-reloading of config
|
// Set up hot-reloading of config
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -35,8 +35,6 @@ github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8b
|
||||||
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
|
github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
|
||||||
github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||||
github.com/emersion/go-smtp v0.16.0 h1:eB9CY9527WdEZSs5sWisTmilDX7gG+Q/2IdRcmubpa8=
|
|
||||||
github.com/emersion/go-smtp v0.16.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
@ -172,8 +170,6 @@ google.golang.org/appengine/v2 v2.0.2/go.mod h1:PkgRUWz4o1XOvbqtWTkBtCitEJ5Tp4Ho
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70=
|
|
||||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
|
||||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY=
|
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY=
|
||||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
|
|
@ -110,7 +110,7 @@ type Config struct {
|
||||||
EnableEmailConfirm bool
|
EnableEmailConfirm bool
|
||||||
EnablePasswordReset bool
|
EnablePasswordReset bool
|
||||||
EnablePayments bool
|
EnablePayments bool
|
||||||
EnableReserveTopics bool // Allow users with role "user" to own/reserve topics
|
EnableReservations bool // Allow users with role "user" to own/reserve topics
|
||||||
Version string // injected by App
|
Version string // injected by App
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ import (
|
||||||
UI:
|
UI:
|
||||||
- flicker of upgrade banner
|
- flicker of upgrade banner
|
||||||
- JS constants
|
- JS constants
|
||||||
- useContext for account
|
|
||||||
Sync:
|
Sync:
|
||||||
- "account topic" sync mechanism
|
- "account topic" sync mechanism
|
||||||
- "mute" setting
|
- "mute" setting
|
||||||
|
@ -58,9 +57,7 @@ import (
|
||||||
Refactor:
|
Refactor:
|
||||||
- rename /access -> /reservation
|
- rename /access -> /reservation
|
||||||
Later:
|
Later:
|
||||||
- Password reset
|
|
||||||
- Pricing
|
- Pricing
|
||||||
- change email
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Server is the main server, providing the UI and API for ntfy
|
// Server is the main server, providing the UI and API for ntfy
|
||||||
|
@ -457,10 +454,10 @@ func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visi
|
||||||
EnableSignup: s.config.EnableSignup,
|
EnableSignup: s.config.EnableSignup,
|
||||||
EnablePasswordReset: s.config.EnablePasswordReset,
|
EnablePasswordReset: s.config.EnablePasswordReset,
|
||||||
EnablePayments: s.config.EnablePayments,
|
EnablePayments: s.config.EnablePayments,
|
||||||
EnableReserveTopics: s.config.EnableReserveTopics,
|
EnableReservations: s.config.EnableReservations,
|
||||||
DisallowedTopics: disallowedTopics,
|
DisallowedTopics: disallowedTopics,
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(response)
|
b, err := json.MarshalIndent(response, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,6 +292,6 @@ type apiConfigResponse struct {
|
||||||
EnableSignup bool `json:"enable_signup"`
|
EnableSignup bool `json:"enable_signup"`
|
||||||
EnablePasswordReset bool `json:"enable_password_reset"`
|
EnablePasswordReset bool `json:"enable_password_reset"`
|
||||||
EnablePayments bool `json:"enable_payments"`
|
EnablePayments bool `json:"enable_payments"`
|
||||||
EnableReserveTopics bool `json:"enable_reserve_topics"`
|
EnableReservations bool `json:"enable_reservations"`
|
||||||
DisallowedTopics []string `json:"disallowed_topics"`
|
DisallowedTopics []string `json:"disallowed_topics"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,6 @@ var config = {
|
||||||
enable_signup: true,
|
enable_signup: true,
|
||||||
enable_password_reset: false,
|
enable_password_reset: false,
|
||||||
enable_payments: true,
|
enable_payments: true,
|
||||||
enable_reserve_topics: true,
|
enable_reservations: true,
|
||||||
disallowed_topics: ["docs", "static", "file", "app", "account", "settings", "pricing", "signup", "login", "reset-password"]
|
disallowed_topics: ["docs", "static", "file", "app", "account", "settings", "pricing", "signup", "login", "reset-password"]
|
||||||
};
|
};
|
||||||
|
|
|
@ -243,6 +243,7 @@
|
||||||
"prefs_appearance_language_title": "Language",
|
"prefs_appearance_language_title": "Language",
|
||||||
"prefs_reservations_title": "Reserved topics",
|
"prefs_reservations_title": "Reserved topics",
|
||||||
"prefs_reservations_description": "You can reserve topic names for personal use here. Reserving a topic gives you ownership over the topic, and allows you to define access permissions for other users over the topic.",
|
"prefs_reservations_description": "You can reserve topic names for personal use here. Reserving a topic gives you ownership over the topic, and allows you to define access permissions for other users over the topic.",
|
||||||
|
"prefs_reservations_limit_reached": "You reached your reserved topics limit.",
|
||||||
"prefs_reservations_add_button": "Add reserved topic",
|
"prefs_reservations_add_button": "Add reserved topic",
|
||||||
"prefs_reservations_edit_button": "Edit topic access",
|
"prefs_reservations_edit_button": "Edit topic access",
|
||||||
"prefs_reservations_delete_button": "Reset topic access",
|
"prefs_reservations_delete_button": "Reset topic access",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useState} from 'react';
|
import {useContext, useState} from 'react';
|
||||||
import {LinearProgress, Link, Stack, useMediaQuery} from "@mui/material";
|
import {LinearProgress, Stack, useMediaQuery} from "@mui/material";
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
|
@ -18,7 +18,6 @@ import TextField from "@mui/material/TextField";
|
||||||
import DialogActions from "@mui/material/DialogActions";
|
import DialogActions from "@mui/material/DialogActions";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import {useOutletContext} from "react-router-dom";
|
|
||||||
import {formatBytes} from "../app/utils";
|
import {formatBytes} from "../app/utils";
|
||||||
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||||
|
@ -28,6 +27,7 @@ import i18n from "i18next";
|
||||||
import humanizeDuration from "humanize-duration";
|
import humanizeDuration from "humanize-duration";
|
||||||
import UpgradeDialog from "./UpgradeDialog";
|
import UpgradeDialog from "./UpgradeDialog";
|
||||||
import CelebrationIcon from "@mui/icons-material/Celebration";
|
import CelebrationIcon from "@mui/icons-material/Celebration";
|
||||||
|
import {AccountContext} from "./App";
|
||||||
|
|
||||||
const Account = () => {
|
const Account = () => {
|
||||||
if (!session.exists()) {
|
if (!session.exists()) {
|
||||||
|
@ -62,7 +62,7 @@ const Basics = () => {
|
||||||
|
|
||||||
const Username = () => {
|
const Username = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { account } = useOutletContext();
|
const { account } = useContext(AccountContext);
|
||||||
const labelId = "prefUsername";
|
const labelId = "prefUsername";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -169,23 +169,12 @@ const ChangePasswordDialog = (props) => {
|
||||||
|
|
||||||
const Stats = () => {
|
const Stats = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { account } = useOutletContext();
|
const { account } = useContext(AccountContext);
|
||||||
const [upgradeDialogOpen, setUpgradeDialogOpen] = useState(false);
|
const [upgradeDialogOpen, setUpgradeDialogOpen] = useState(false);
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalize = (value, max) => Math.min(value / max * 100, 100);
|
const normalize = (value, max) => Math.min(value / max * 100, 100);
|
||||||
const barColor = (remaining, limit) => {
|
|
||||||
if (account.role === "admin") {
|
|
||||||
return "primary";
|
|
||||||
} else if (limit > 0 && remaining === 0) {
|
|
||||||
return "error";
|
|
||||||
}
|
|
||||||
return "primary";
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{p: 3}} aria-label={t("account_usage_title")}>
|
<Card sx={{p: 3}} aria-label={t("account_usage_title")}>
|
||||||
<Typography variant="h5" sx={{marginBottom: 2}}>
|
<Typography variant="h5" sx={{marginBottom: 2}}>
|
||||||
|
@ -238,7 +227,6 @@ const Stats = () => {
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={account.limits.reservations > 0 ? normalize(account.stats.reservations, account.limits.reservations) : 100}
|
value={account.limits.reservations > 0 ? normalize(account.stats.reservations, account.limits.reservations) : 100}
|
||||||
color={barColor(account.stats.reservations_remaining, account.limits.reservations)}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -260,7 +248,6 @@ const Stats = () => {
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={account.role === "user" ? normalize(account.stats.messages, account.limits.messages) : 100}
|
value={account.role === "user" ? normalize(account.stats.messages, account.limits.messages) : 100}
|
||||||
color={account.role === "user" && account.stats.messages_remaining === 0 ? 'error' : 'primary'}
|
|
||||||
/>
|
/>
|
||||||
</Pref>
|
</Pref>
|
||||||
<Pref title={
|
<Pref title={
|
||||||
|
@ -271,12 +258,11 @@ const Stats = () => {
|
||||||
}>
|
}>
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="body2" sx={{float: "left"}}>{account.stats.emails}</Typography>
|
<Typography variant="body2" sx={{float: "left"}}>{account.stats.emails}</Typography>
|
||||||
<Typography variant="body2" sx={{float: "right"}}>{account.limits.emails > 0 ? t("account_usage_of_limit", { limit: account.limits.emails }) : t("account_usage_unlimited")}</Typography>
|
<Typography variant="body2" sx={{float: "right"}}>{account.role === "user" ? t("account_usage_of_limit", { limit: account.limits.emails }) : t("account_usage_unlimited")}</Typography>
|
||||||
</div>
|
</div>
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={account.limits.emails > 0 ? normalize(account.stats.emails, account.limits.emails) : 100}
|
value={account.role === "user" ? normalize(account.stats.emails, account.limits.emails) : 100}
|
||||||
color={account?.role !== "admin" && account.stats.emails_remaining === 0 ? 'error' : 'primary'}
|
|
||||||
/>
|
/>
|
||||||
</Pref>
|
</Pref>
|
||||||
<Pref
|
<Pref
|
||||||
|
@ -292,16 +278,15 @@ const Stats = () => {
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="body2" sx={{float: "left"}}>{formatBytes(account.stats.attachment_total_size)}</Typography>
|
<Typography variant="body2" sx={{float: "left"}}>{formatBytes(account.stats.attachment_total_size)}</Typography>
|
||||||
<Typography variant="body2" sx={{float: "right"}}>{account.limits.attachment_total_size > 0 ? t("account_usage_of_limit", { limit: formatBytes(account.limits.attachment_total_size) }) : t("account_usage_unlimited")}</Typography>
|
<Typography variant="body2" sx={{float: "right"}}>{account.role === "user" ? t("account_usage_of_limit", { limit: formatBytes(account.limits.attachment_total_size) }) : t("account_usage_unlimited")}</Typography>
|
||||||
</div>
|
</div>
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={account.limits.attachment_total_size > 0 ? normalize(account.stats.attachment_total_size, account.limits.attachment_total_size) : 100}
|
value={account.role === "user" ? normalize(account.stats.attachment_total_size, account.limits.attachment_total_size) : 100}
|
||||||
color={account.role !== "admin" && account.stats.attachment_total_size_remaining === 0 ? 'error' : 'primary'}
|
|
||||||
/>
|
/>
|
||||||
</Pref>
|
</Pref>
|
||||||
</PrefGroup>
|
</PrefGroup>
|
||||||
{account.limits.basis === "ip" &&
|
{account.role === "user" && account.limits.basis === "ip" &&
|
||||||
<Typography variant="body1">
|
<Typography variant="body1">
|
||||||
{t("account_usage_basis_ip_description")}
|
{t("account_usage_basis_ip_description")}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {Suspense, useEffect, useState} from 'react';
|
import {createContext, Suspense, useContext, useEffect, useState} from 'react';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import {ThemeProvider} from '@mui/material/styles';
|
import {ThemeProvider} from '@mui/material/styles';
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import Notifications from "./Notifications";
|
import {AllSubscriptions, SingleSubscription} from "./Notifications";
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import Navigation from "./Navigation";
|
import Navigation from "./Navigation";
|
||||||
import ActionBar from "./ActionBar";
|
import ActionBar from "./ActionBar";
|
||||||
|
@ -13,11 +13,11 @@ import Preferences from "./Preferences";
|
||||||
import {useLiveQuery} from "dexie-react-hooks";
|
import {useLiveQuery} from "dexie-react-hooks";
|
||||||
import subscriptionManager from "../app/SubscriptionManager";
|
import subscriptionManager from "../app/SubscriptionManager";
|
||||||
import userManager from "../app/UserManager";
|
import userManager from "../app/UserManager";
|
||||||
import {BrowserRouter, Outlet, Route, Routes, useOutletContext, useParams} from "react-router-dom";
|
import {BrowserRouter, Outlet, Route, Routes, useParams} from "react-router-dom";
|
||||||
import {expandUrl} from "../app/utils";
|
import {expandUrl} from "../app/utils";
|
||||||
import ErrorBoundary from "./ErrorBoundary";
|
import ErrorBoundary from "./ErrorBoundary";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import {useAccountListener, useAutoSubscribe, useBackgroundProcesses, useConnectionListeners} from "./hooks";
|
import {useAccountListener, useBackgroundProcesses, useConnectionListeners} from "./hooks";
|
||||||
import PublishDialog from "./PublishDialog";
|
import PublishDialog from "./PublishDialog";
|
||||||
import Messaging from "./Messaging";
|
import Messaging from "./Messaging";
|
||||||
import "./i18n"; // Translations!
|
import "./i18n"; // Translations!
|
||||||
|
@ -27,53 +27,45 @@ import Login from "./Login";
|
||||||
import Pricing from "./Pricing";
|
import Pricing from "./Pricing";
|
||||||
import Signup from "./Signup";
|
import Signup from "./Signup";
|
||||||
import Account from "./Account";
|
import Account from "./Account";
|
||||||
import ResetPassword from "./ResetPassword";
|
|
||||||
|
export const AccountContext = createContext(null);
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
const [account, setAccount] = useState(null);
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline/>
|
<AccountContext.Provider value={{ account, setAccount }}>
|
||||||
<ErrorBoundary>
|
<CssBaseline/>
|
||||||
<Routes>
|
<ErrorBoundary>
|
||||||
<Route path={routes.home} element={<Home/>}/>
|
<Routes>
|
||||||
<Route path={routes.pricing} element={<Pricing/>}/>
|
<Route path={routes.home} element={<Home/>}/>
|
||||||
<Route path={routes.login} element={<Login/>}/>
|
<Route path={routes.pricing} element={<Pricing/>}/>
|
||||||
<Route path={routes.signup} element={<Signup/>}/>
|
<Route path={routes.login} element={<Login/>}/>
|
||||||
<Route path={routes.resetPassword} element={<ResetPassword/>}/>
|
<Route path={routes.signup} element={<Signup/>}/>
|
||||||
<Route element={<Layout/>}>
|
<Route element={<Layout/>}>
|
||||||
<Route path={routes.app} element={<AllSubscriptions/>}/>
|
<Route path={routes.app} element={<AllSubscriptions/>}/>
|
||||||
<Route path={routes.account} element={<Account/>}/>
|
<Route path={routes.account} element={<Account/>}/>
|
||||||
<Route path={routes.settings} element={<Preferences/>}/>
|
<Route path={routes.settings} element={<Preferences/>}/>
|
||||||
<Route path={routes.subscription} element={<SingleSubscription/>}/>
|
<Route path={routes.subscription} element={<SingleSubscription/>}/>
|
||||||
<Route path={routes.subscriptionExternal} element={<SingleSubscription/>}/>
|
<Route path={routes.subscriptionExternal} element={<SingleSubscription/>}/>
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
</AccountContext.Provider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AllSubscriptions = () => {
|
|
||||||
const { subscriptions } = useOutletContext();
|
|
||||||
return <Notifications mode="all" subscriptions={subscriptions}/>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SingleSubscription = () => {
|
|
||||||
const { subscriptions, selected } = useOutletContext();
|
|
||||||
useAutoSubscribe(subscriptions, selected);
|
|
||||||
return <Notifications mode="one" subscription={selected}/>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Layout = () => {
|
const Layout = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const { account, setAccount } = useContext(AccountContext);
|
||||||
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
||||||
const [notificationsGranted, setNotificationsGranted] = useState(notifier.granted());
|
const [notificationsGranted, setNotificationsGranted] = useState(notifier.granted());
|
||||||
const [sendDialogOpenMode, setSendDialogOpenMode] = useState("");
|
const [sendDialogOpenMode, setSendDialogOpenMode] = useState("");
|
||||||
const [account, setAccount] = useState(null);
|
|
||||||
const users = useLiveQuery(() => userManager.all());
|
const users = useLiveQuery(() => userManager.all());
|
||||||
const subscriptions = useLiveQuery(() => subscriptionManager.all());
|
const subscriptions = useLiveQuery(() => subscriptionManager.all());
|
||||||
const newNotificationsCount = subscriptions?.reduce((prev, cur) => prev + cur.new, 0) || 0;
|
const newNotificationsCount = subscriptions?.reduce((prev, cur) => prev + cur.new, 0) || 0;
|
||||||
|
@ -94,7 +86,6 @@ const Layout = () => {
|
||||||
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
|
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
|
||||||
/>
|
/>
|
||||||
<Navigation
|
<Navigation
|
||||||
account={account}
|
|
||||||
subscriptions={subscriptions}
|
subscriptions={subscriptions}
|
||||||
selectedSubscription={selected}
|
selectedSubscription={selected}
|
||||||
notificationsGranted={notificationsGranted}
|
notificationsGranted={notificationsGranted}
|
||||||
|
@ -105,7 +96,7 @@ const Layout = () => {
|
||||||
/>
|
/>
|
||||||
<Main>
|
<Main>
|
||||||
<Toolbar/>
|
<Toolbar/>
|
||||||
<Outlet context={{ account, subscriptions, selected }}/>
|
<Outlet context={{ subscriptions, selected }}/>
|
||||||
</Main>
|
</Main>
|
||||||
<Messaging
|
<Messaging
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Drawer from "@mui/material/Drawer";
|
import Drawer from "@mui/material/Drawer";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {useState} from "react";
|
import {useContext, useState} from "react";
|
||||||
import ListItemButton from "@mui/material/ListItemButton";
|
import ListItemButton from "@mui/material/ListItemButton";
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||||
import ChatBubbleOutlineIcon from "@mui/icons-material/ChatBubbleOutline";
|
import ChatBubbleOutlineIcon from "@mui/icons-material/ChatBubbleOutline";
|
||||||
|
@ -30,6 +30,7 @@ import session from "../app/Session";
|
||||||
import accountApi from "../app/AccountApi";
|
import accountApi from "../app/AccountApi";
|
||||||
import CelebrationIcon from '@mui/icons-material/Celebration';
|
import CelebrationIcon from '@mui/icons-material/Celebration';
|
||||||
import UpgradeDialog from "./UpgradeDialog";
|
import UpgradeDialog from "./UpgradeDialog";
|
||||||
|
import {AccountContext} from "./App";
|
||||||
|
|
||||||
const navWidth = 280;
|
const navWidth = 280;
|
||||||
|
|
||||||
|
@ -76,6 +77,7 @@ const NavList = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const { account } = useContext(AccountContext);
|
||||||
const [subscribeDialogKey, setSubscribeDialogKey] = useState(0);
|
const [subscribeDialogKey, setSubscribeDialogKey] = useState(0);
|
||||||
const [subscribeDialogOpen, setSubscribeDialogOpen] = useState(false);
|
const [subscribeDialogOpen, setSubscribeDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
@ -100,8 +102,8 @@ const NavList = (props) => {
|
||||||
navigate(routes.account);
|
navigate(routes.account);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isAdmin = props.account?.role === "admin";
|
const isAdmin = account?.role === "admin";
|
||||||
const isPaid = props.account?.tier?.paid;
|
const isPaid = account?.tier?.paid;
|
||||||
const showUpgradeBanner = config.enable_payments && !isAdmin && !isPaid;// && (!props.account || !props.account.tier || !props.account.tier.paid || props.account);
|
const showUpgradeBanner = config.enable_payments && !isAdmin && !isPaid;// && (!props.account || !props.account.tier || !props.account.tier.paid || props.account);
|
||||||
const showSubscriptionsList = props.subscriptions?.length > 0;
|
const showSubscriptionsList = props.subscriptions?.length > 0;
|
||||||
const showNotificationBrowserNotSupportedBox = !notifier.browserSupported();
|
const showNotificationBrowserNotSupportedBox = !notifier.browserSupported();
|
||||||
|
|
|
@ -19,7 +19,8 @@ import {
|
||||||
formatBytes,
|
formatBytes,
|
||||||
formatMessage,
|
formatMessage,
|
||||||
formatShortDateTime,
|
formatShortDateTime,
|
||||||
formatTitle, maybeAppendActionErrors,
|
formatTitle,
|
||||||
|
maybeAppendActionErrors,
|
||||||
openUrl,
|
openUrl,
|
||||||
shortUrl,
|
shortUrl,
|
||||||
topicShortUrl,
|
topicShortUrl,
|
||||||
|
@ -41,15 +42,27 @@ import priority5 from "../img/priority-5.svg";
|
||||||
import logoOutline from "../img/ntfy-outline.svg";
|
import logoOutline from "../img/ntfy-outline.svg";
|
||||||
import AttachmentIcon from "./AttachmentIcon";
|
import AttachmentIcon from "./AttachmentIcon";
|
||||||
import {Trans, useTranslation} from "react-i18next";
|
import {Trans, useTranslation} from "react-i18next";
|
||||||
|
import {useOutletContext} from "react-router-dom";
|
||||||
|
import {useAutoSubscribe} from "./hooks";
|
||||||
|
|
||||||
const Notifications = (props) => {
|
export const AllSubscriptions = () => {
|
||||||
if (props.mode === "all") {
|
const { subscriptions } = useOutletContext();
|
||||||
return (props.subscriptions) ? <AllSubscriptions subscriptions={props.subscriptions}/> : <Loading/>;
|
if (!subscriptions) {
|
||||||
|
return <Loading/>;
|
||||||
}
|
}
|
||||||
return (props.subscription) ? <SingleSubscription subscription={props.subscription}/> : <Loading/>;
|
return <AllSubscriptionsList subscriptions={subscriptions}/>;
|
||||||
}
|
};
|
||||||
|
|
||||||
const AllSubscriptions = (props) => {
|
export const SingleSubscription = () => {
|
||||||
|
const { subscriptions, selected } = useOutletContext();
|
||||||
|
useAutoSubscribe(subscriptions, selected);
|
||||||
|
if (!selected) {
|
||||||
|
return <Loading/>;
|
||||||
|
}
|
||||||
|
return <SingleSubscriptionList subscription={selected}/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AllSubscriptionsList = (props) => {
|
||||||
const subscriptions = props.subscriptions;
|
const subscriptions = props.subscriptions;
|
||||||
const notifications = useLiveQuery(() => subscriptionManager.getAllNotifications(), []);
|
const notifications = useLiveQuery(() => subscriptionManager.getAllNotifications(), []);
|
||||||
if (notifications === null || notifications === undefined) {
|
if (notifications === null || notifications === undefined) {
|
||||||
|
@ -62,7 +75,7 @@ const AllSubscriptions = (props) => {
|
||||||
return <NotificationList key="all" notifications={notifications} messageBar={false}/>;
|
return <NotificationList key="all" notifications={notifications} messageBar={false}/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SingleSubscription = (props) => {
|
const SingleSubscriptionList = (props) => {
|
||||||
const subscription = props.subscription;
|
const subscription = props.subscription;
|
||||||
const notifications = useLiveQuery(() => subscriptionManager.getNotifications(subscription.id), [subscription]);
|
const notifications = useLiveQuery(() => subscriptionManager.getNotifications(subscription.id), [subscription]);
|
||||||
if (notifications === null || notifications === undefined) {
|
if (notifications === null || notifications === undefined) {
|
||||||
|
@ -533,5 +546,3 @@ const Loading = () => {
|
||||||
</VerticallyCenteredContainer>
|
</VerticallyCenteredContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Notifications;
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useEffect, useState} from 'react';
|
import {useContext, useEffect, useState} from 'react';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
CardActions,
|
CardActions,
|
||||||
|
@ -40,13 +40,11 @@ import session from "../app/Session";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
import accountApi, {UnauthorizedError} from "../app/AccountApi";
|
||||||
import {Pref, PrefGroup} from "./Pref";
|
import {Pref, PrefGroup} from "./Pref";
|
||||||
import {useOutletContext} from "react-router-dom";
|
|
||||||
import LockIcon from "@mui/icons-material/Lock";
|
import LockIcon from "@mui/icons-material/Lock";
|
||||||
import {Public, PublicOff} from "@mui/icons-material";
|
import {Public, PublicOff} from "@mui/icons-material";
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
|
||||||
import DialogContentText from "@mui/material/DialogContentText";
|
import DialogContentText from "@mui/material/DialogContentText";
|
||||||
import ReserveTopicSelect from "./ReserveTopicSelect";
|
import ReserveTopicSelect from "./ReserveTopicSelect";
|
||||||
|
import {AccountContext} from "./App";
|
||||||
|
|
||||||
const Preferences = () => {
|
const Preferences = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -481,11 +479,11 @@ const Language = () => {
|
||||||
|
|
||||||
const Reservations = () => {
|
const Reservations = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { account } = useOutletContext();
|
const { account } = useContext(AccountContext);
|
||||||
const [dialogKey, setDialogKey] = useState(0);
|
const [dialogKey, setDialogKey] = useState(0);
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
|
||||||
if (!config.enable_reserve_topics || !session.exists() || !account || account.role === "admin") {
|
if (!config.enable_reservations || !session.exists() || !account || account.role === "admin") {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
const reservations = account.reservations || [];
|
const reservations = account.reservations || [];
|
||||||
|
@ -522,14 +520,7 @@ const Reservations = () => {
|
||||||
{t("prefs_reservations_description")}
|
{t("prefs_reservations_description")}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
{reservations.length > 0 && <ReservationsTable reservations={reservations}/>}
|
{reservations.length > 0 && <ReservationsTable reservations={reservations}/>}
|
||||||
{limitReached &&
|
{limitReached && <Alert severity="info">{t("prefs_reservations_limit_reached")}</Alert>}
|
||||||
<Alert severity="info">
|
|
||||||
You reached your reserved topics limit.
|
|
||||||
{config.enable_payments &&
|
|
||||||
<>{" "}<b>Upgrade</b></>
|
|
||||||
}
|
|
||||||
</Alert>
|
|
||||||
}
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardActions>
|
<CardActions>
|
||||||
<Button onClick={handleAddClick} disabled={limitReached}>{t("prefs_reservations_add_button")}</Button>
|
<Button onClick={handleAddClick} disabled={limitReached}>{t("prefs_reservations_add_button")}</Button>
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import * as React from 'react';
|
|
||||||
import TextField from "@mui/material/TextField";
|
|
||||||
import Button from "@mui/material/Button";
|
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import routes from "./routes";
|
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
import {NavLink} from "react-router-dom";
|
|
||||||
import AvatarBox from "./AvatarBox";
|
|
||||||
|
|
||||||
const ResetPassword = () => {
|
|
||||||
const handleSubmit = async (event) => {
|
|
||||||
//
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AvatarBox>
|
|
||||||
<Typography sx={{ typography: 'h6' }}>
|
|
||||||
Reset password
|
|
||||||
</Typography>
|
|
||||||
<Box component="form" onSubmit={handleSubmit} noValidate sx={{mt: 1, maxWidth: 400}}>
|
|
||||||
<TextField
|
|
||||||
margin="dense"
|
|
||||||
required
|
|
||||||
fullWidth
|
|
||||||
id="email"
|
|
||||||
label="Email"
|
|
||||||
name="email"
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
fullWidth
|
|
||||||
variant="contained"
|
|
||||||
sx={{mt: 2, mb: 2}}
|
|
||||||
>
|
|
||||||
Reset password
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
<Typography sx={{mb: 4}}>
|
|
||||||
<NavLink to={routes.login} variant="body1">
|
|
||||||
< Return to sign in
|
|
||||||
</NavLink>
|
|
||||||
</Typography>
|
|
||||||
</AvatarBox>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ResetPassword;
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useState} from 'react';
|
import {useContext, useState} from 'react';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
@ -19,7 +19,7 @@ import session from "../app/Session";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import accountApi, {TopicReservedError, UnauthorizedError} from "../app/AccountApi";
|
import accountApi, {TopicReservedError, UnauthorizedError} from "../app/AccountApi";
|
||||||
import ReserveTopicSelect from "./ReserveTopicSelect";
|
import ReserveTopicSelect from "./ReserveTopicSelect";
|
||||||
import {useOutletContext} from "react-router-dom";
|
import {AccountContext} from "./App";
|
||||||
|
|
||||||
const publicBaseUrl = "https://ntfy.sh";
|
const publicBaseUrl = "https://ntfy.sh";
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ const SubscribeDialog = (props) => {
|
||||||
|
|
||||||
const SubscribePage = (props) => {
|
const SubscribePage = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
//const { account } = useOutletContext();
|
const { account } = useContext(AccountContext);
|
||||||
const [reserveTopicVisible, setReserveTopicVisible] = useState(false);
|
const [reserveTopicVisible, setReserveTopicVisible] = useState(false);
|
||||||
const [anotherServerVisible, setAnotherServerVisible] = useState(false);
|
const [anotherServerVisible, setAnotherServerVisible] = useState(false);
|
||||||
const [errorText, setErrorText] = useState("");
|
const [errorText, setErrorText] = useState("");
|
||||||
|
@ -87,7 +87,7 @@ const SubscribePage = (props) => {
|
||||||
const existingBaseUrls = Array
|
const existingBaseUrls = Array
|
||||||
.from(new Set([publicBaseUrl, ...props.subscriptions.map(s => s.baseUrl)]))
|
.from(new Set([publicBaseUrl, ...props.subscriptions.map(s => s.baseUrl)]))
|
||||||
.filter(s => s !== config.base_url);
|
.filter(s => s !== config.base_url);
|
||||||
//const reserveTopicEnabled = session.exists() && (account?.stats.reservations_remaining || 0) > 0;
|
const reserveTopicEnabled = session.exists() && account?.role === "user" && (account?.stats.reservations_remaining || 0) > 0;
|
||||||
|
|
||||||
const handleSubscribe = async () => {
|
const handleSubscribe = async () => {
|
||||||
const user = await userManager.get(baseUrl); // May be undefined
|
const user = await userManager.get(baseUrl); // May be undefined
|
||||||
|
@ -177,14 +177,14 @@ const SubscribePage = (props) => {
|
||||||
{t("subscribe_dialog_subscribe_button_generate_topic_name")}
|
{t("subscribe_dialog_subscribe_button_generate_topic_name")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{config.enable_reserve_topics && session.exists() && !anotherServerVisible &&
|
{config.enable_reservations && session.exists() && !anotherServerVisible &&
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
variant="standard"
|
variant="standard"
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
fullWidth
|
fullWidth
|
||||||
// disabled={account.stats.reservations_remaining}
|
disabled={!reserveTopicEnabled}
|
||||||
checked={reserveTopicVisible}
|
checked={reserveTopicVisible}
|
||||||
onChange={(ev) => setReserveTopicVisible(ev.target.checked)}
|
onChange={(ev) => setReserveTopicVisible(ev.target.checked)}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
|
|
|
@ -78,7 +78,7 @@ const SubscriptionSettingsDialog = (props) => {
|
||||||
"aria-label": t("subscription_settings_dialog_display_name_placeholder")
|
"aria-label": t("subscription_settings_dialog_display_name_placeholder")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{config.enable_reserve_topics && session.exists() &&
|
{config.enable_reservations && session.exists() &&
|
||||||
<>
|
<>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
fullWidth
|
fullWidth
|
||||||
|
|
|
@ -1,25 +1,10 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useState} from 'react';
|
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import TextField from '@mui/material/TextField';
|
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import DialogContentText from '@mui/material/DialogContentText';
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import {Autocomplete, Checkbox, FormControlLabel, FormGroup, useMediaQuery} from "@mui/material";
|
import {useMediaQuery} from "@mui/material";
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import api from "../app/Api";
|
|
||||||
import {randomAlphanumericString, topicUrl, validTopic, validUrl} from "../app/utils";
|
|
||||||
import userManager from "../app/UserManager";
|
|
||||||
import subscriptionManager from "../app/SubscriptionManager";
|
|
||||||
import poller from "../app/Poller";
|
|
||||||
import DialogFooter from "./DialogFooter";
|
import DialogFooter from "./DialogFooter";
|
||||||
import {useTranslation} from "react-i18next";
|
|
||||||
import session from "../app/Session";
|
|
||||||
import routes from "./routes";
|
|
||||||
import accountApi, {TopicReservedError, UnauthorizedError} from "../app/AccountApi";
|
|
||||||
import ReserveTopicSelect from "./ReserveTopicSelect";
|
|
||||||
import {useOutletContext} from "react-router-dom";
|
|
||||||
|
|
||||||
const UpgradeDialog = (props) => {
|
const UpgradeDialog = (props) => {
|
||||||
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
|
|
|
@ -8,7 +8,7 @@ const routes = {
|
||||||
pricing: "/pricing",
|
pricing: "/pricing",
|
||||||
login: "/login",
|
login: "/login",
|
||||||
signup: "/signup",
|
signup: "/signup",
|
||||||
resetPassword: "/reset-password",
|
resetPassword: "/reset-password", // Not used (yet)
|
||||||
app: config.app_root,
|
app: config.app_root,
|
||||||
account: "/account",
|
account: "/account",
|
||||||
settings: "/settings",
|
settings: "/settings",
|
||||||
|
|
Loading…
Reference in a new issue