Upgrade banner
parent
a91da7cf2c
commit
3280c2c440
|
@ -107,8 +107,8 @@ type Config struct {
|
||||||
EnableSignup bool
|
EnableSignup bool
|
||||||
EnableLogin bool
|
EnableLogin bool
|
||||||
EnableEmailConfirm bool
|
EnableEmailConfirm bool
|
||||||
EnableResetPassword bool
|
EnablePasswordReset bool
|
||||||
EnableAccountUpgrades bool
|
EnablePayments bool
|
||||||
Version string // injected by App
|
Version string // injected by App
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -452,7 +452,7 @@ func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visi
|
||||||
AppRoot: appRoot,
|
AppRoot: appRoot,
|
||||||
EnableLogin: s.config.EnableLogin,
|
EnableLogin: s.config.EnableLogin,
|
||||||
EnableSignup: s.config.EnableSignup,
|
EnableSignup: s.config.EnableSignup,
|
||||||
EnableResetPassword: s.config.EnableResetPassword,
|
EnablePasswordReset: s.config.EnablePasswordReset,
|
||||||
DisallowedTopics: disallowedTopics,
|
DisallowedTopics: disallowedTopics,
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(response)
|
b, err := json.Marshal(response)
|
||||||
|
|
|
@ -81,17 +81,17 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, _ *http.Request, v *vis
|
||||||
if v.user.Plan != nil {
|
if v.user.Plan != nil {
|
||||||
response.Plan = &apiAccountPlan{
|
response.Plan = &apiAccountPlan{
|
||||||
Code: v.user.Plan.Code,
|
Code: v.user.Plan.Code,
|
||||||
Upgradable: v.user.Plan.Upgradable,
|
Upgradeable: v.user.Plan.Upgradeable,
|
||||||
}
|
}
|
||||||
} else if v.user.Role == user.RoleAdmin {
|
} else if v.user.Role == user.RoleAdmin {
|
||||||
response.Plan = &apiAccountPlan{
|
response.Plan = &apiAccountPlan{
|
||||||
Code: string(user.PlanUnlimited),
|
Code: string(user.PlanUnlimited),
|
||||||
Upgradable: false,
|
Upgradeable: false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
response.Plan = &apiAccountPlan{
|
response.Plan = &apiAccountPlan{
|
||||||
Code: string(user.PlanDefault),
|
Code: string(user.PlanDefault),
|
||||||
Upgradable: true,
|
Upgradeable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reservations, err := s.userManager.Reservations(v.user.Name)
|
reservations, err := s.userManager.Reservations(v.user.Name)
|
||||||
|
@ -112,7 +112,7 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, _ *http.Request, v *vis
|
||||||
response.Role = string(user.RoleAnonymous)
|
response.Role = string(user.RoleAnonymous)
|
||||||
response.Plan = &apiAccountPlan{
|
response.Plan = &apiAccountPlan{
|
||||||
Code: string(user.PlanNone),
|
Code: string(user.PlanNone),
|
||||||
Upgradable: true,
|
Upgradeable: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
|
@ -236,7 +236,7 @@ type apiAccountTokenResponse struct {
|
||||||
|
|
||||||
type apiAccountPlan struct {
|
type apiAccountPlan struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
Upgradable bool `json:"upgradable"`
|
Upgradeable bool `json:"upgradeable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiAccountLimits struct {
|
type apiAccountLimits struct {
|
||||||
|
@ -286,6 +286,7 @@ type apiConfigResponse struct {
|
||||||
AppRoot string `json:"app_root"`
|
AppRoot string `json:"app_root"`
|
||||||
EnableLogin bool `json:"enable_login"`
|
EnableLogin bool `json:"enable_login"`
|
||||||
EnableSignup bool `json:"enable_signup"`
|
EnableSignup bool `json:"enable_signup"`
|
||||||
EnableResetPassword bool `json:"enable_reset_password"`
|
EnablePasswordReset bool `json:"enable_password_reset"`
|
||||||
|
EnablePayments bool `json:"enable_payments"`
|
||||||
DisallowedTopics []string `json:"disallowed_topics"`
|
DisallowedTopics []string `json:"disallowed_topics"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -503,7 +503,7 @@ func (a *Manager) readUser(rows *sql.Rows) (*User, error) {
|
||||||
if planCode.Valid {
|
if planCode.Valid {
|
||||||
user.Plan = &Plan{
|
user.Plan = &Plan{
|
||||||
Code: planCode.String,
|
Code: planCode.String,
|
||||||
Upgradable: true, // FIXME
|
Upgradeable: false,
|
||||||
MessagesLimit: messagesLimit.Int64,
|
MessagesLimit: messagesLimit.Int64,
|
||||||
EmailsLimit: emailsLimit.Int64,
|
EmailsLimit: emailsLimit.Int64,
|
||||||
TopicsLimit: topicsLimit.Int64,
|
TopicsLimit: topicsLimit.Int64,
|
||||||
|
|
|
@ -56,7 +56,7 @@ const (
|
||||||
// Plan represents a user's account type, including its account limits
|
// Plan represents a user's account type, including its account limits
|
||||||
type Plan struct {
|
type Plan struct {
|
||||||
Code string `json:"name"`
|
Code string `json:"name"`
|
||||||
Upgradable bool `json:"upgradable"`
|
Upgradeable bool `json:"upgradeable"`
|
||||||
MessagesLimit int64 `json:"messages_limit"`
|
MessagesLimit int64 `json:"messages_limit"`
|
||||||
EmailsLimit int64 `json:"emails_limit"`
|
EmailsLimit int64 `json:"emails_limit"`
|
||||||
TopicsLimit int64 `json:"topics_limit"`
|
TopicsLimit int64 `json:"topics_limit"`
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
// During web development, you may change values here for rapid testing.
|
// During web development, you may change values here for rapid testing.
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
baseUrl: "http://localhost:2586", // window.location.origin FIXME update before merging
|
base_url: "http://localhost:2586", // window.location.origin FIXME update before merging
|
||||||
appRoot: "/app",
|
app_root: "/app",
|
||||||
enableLogin: true,
|
enable_login: true,
|
||||||
enableSignup: true,
|
enable_signup: true,
|
||||||
enableResetPassword: false,
|
enable_password_reset: false,
|
||||||
disallowedTopics: ["docs", "static", "file", "app", "account", "settings", "pricing", "signup", "login", "reset-password"]
|
enable_payments: true,
|
||||||
|
disallowed_topics: ["docs", "static", "file", "app", "account", "settings", "pricing", "signup", "login", "reset-password"]
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"action_bar_show_menu": "Show menu",
|
"action_bar_show_menu": "Show menu",
|
||||||
"action_bar_logo_alt": "ntfy logo",
|
"action_bar_logo_alt": "ntfy logo",
|
||||||
"action_bar_settings": "Settings",
|
"action_bar_settings": "Settings",
|
||||||
|
"action_bar_account": "Account",
|
||||||
"action_bar_subscription_settings": "Subscription settings",
|
"action_bar_subscription_settings": "Subscription settings",
|
||||||
"action_bar_send_test_notification": "Send test notification",
|
"action_bar_send_test_notification": "Send test notification",
|
||||||
"action_bar_clear_notifications": "Clear all notifications",
|
"action_bar_clear_notifications": "Clear all notifications",
|
||||||
|
|
|
@ -34,7 +34,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(user) {
|
async login(user) {
|
||||||
const url = accountTokenUrl(config.baseUrl);
|
const url = accountTokenUrl(config.base_url);
|
||||||
console.log(`[AccountApi] Checking auth for ${url}`);
|
console.log(`[AccountApi] Checking auth for ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -53,7 +53,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async logout() {
|
async logout() {
|
||||||
const url = accountTokenUrl(config.baseUrl);
|
const url = accountTokenUrl(config.base_url);
|
||||||
console.log(`[AccountApi] Logging out from ${url} using token ${session.token()}`);
|
console.log(`[AccountApi] Logging out from ${url} using token ${session.token()}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
@ -67,7 +67,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(username, password) {
|
async create(username, password) {
|
||||||
const url = accountUrl(config.baseUrl);
|
const url = accountUrl(config.base_url);
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
username: username,
|
username: username,
|
||||||
password: password
|
password: password
|
||||||
|
@ -87,7 +87,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async get() {
|
async get() {
|
||||||
const url = accountUrl(config.baseUrl);
|
const url = accountUrl(config.base_url);
|
||||||
console.log(`[AccountApi] Fetching user account ${url}`);
|
console.log(`[AccountApi] Fetching user account ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: withBearerAuth({}, session.token())
|
headers: withBearerAuth({}, session.token())
|
||||||
|
@ -106,7 +106,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete() {
|
async delete() {
|
||||||
const url = accountUrl(config.baseUrl);
|
const url = accountUrl(config.base_url);
|
||||||
console.log(`[AccountApi] Deleting user account ${url}`);
|
console.log(`[AccountApi] Deleting user account ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
@ -120,7 +120,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async changePassword(newPassword) {
|
async changePassword(newPassword) {
|
||||||
const url = accountPasswordUrl(config.baseUrl);
|
const url = accountPasswordUrl(config.base_url);
|
||||||
console.log(`[AccountApi] Changing account password ${url}`);
|
console.log(`[AccountApi] Changing account password ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -137,7 +137,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async extendToken() {
|
async extendToken() {
|
||||||
const url = accountTokenUrl(config.baseUrl);
|
const url = accountTokenUrl(config.base_url);
|
||||||
console.log(`[AccountApi] Extending user access token ${url}`);
|
console.log(`[AccountApi] Extending user access token ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
@ -151,7 +151,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateSettings(payload) {
|
async updateSettings(payload) {
|
||||||
const url = accountSettingsUrl(config.baseUrl);
|
const url = accountSettingsUrl(config.base_url);
|
||||||
const body = JSON.stringify(payload);
|
const body = JSON.stringify(payload);
|
||||||
console.log(`[AccountApi] Updating user account ${url}: ${body}`);
|
console.log(`[AccountApi] Updating user account ${url}: ${body}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
@ -167,7 +167,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async addSubscription(payload) {
|
async addSubscription(payload) {
|
||||||
const url = accountSubscriptionUrl(config.baseUrl);
|
const url = accountSubscriptionUrl(config.base_url);
|
||||||
const body = JSON.stringify(payload);
|
const body = JSON.stringify(payload);
|
||||||
console.log(`[AccountApi] Adding user subscription ${url}: ${body}`);
|
console.log(`[AccountApi] Adding user subscription ${url}: ${body}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
@ -186,7 +186,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateSubscription(remoteId, payload) {
|
async updateSubscription(remoteId, payload) {
|
||||||
const url = accountSubscriptionSingleUrl(config.baseUrl, remoteId);
|
const url = accountSubscriptionSingleUrl(config.base_url, remoteId);
|
||||||
const body = JSON.stringify(payload);
|
const body = JSON.stringify(payload);
|
||||||
console.log(`[AccountApi] Updating user subscription ${url}: ${body}`);
|
console.log(`[AccountApi] Updating user subscription ${url}: ${body}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
@ -205,7 +205,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSubscription(remoteId) {
|
async deleteSubscription(remoteId) {
|
||||||
const url = accountSubscriptionSingleUrl(config.baseUrl, remoteId);
|
const url = accountSubscriptionSingleUrl(config.base_url, remoteId);
|
||||||
console.log(`[AccountApi] Removing user subscription ${url}`);
|
console.log(`[AccountApi] Removing user subscription ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
@ -219,7 +219,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async upsertAccess(topic, everyone) {
|
async upsertAccess(topic, everyone) {
|
||||||
const url = accountAccessUrl(config.baseUrl);
|
const url = accountAccessUrl(config.base_url);
|
||||||
console.log(`[AccountApi] Upserting user access to topic ${topic}, everyone=${everyone}`);
|
console.log(`[AccountApi] Upserting user access to topic ${topic}, everyone=${everyone}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -239,7 +239,7 @@ class AccountApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteAccess(topic) {
|
async deleteAccess(topic) {
|
||||||
const url = accountAccessSingleUrl(config.baseUrl, topic);
|
const url = accountAccessSingleUrl(config.base_url, topic);
|
||||||
console.log(`[AccountApi] Removing topic reservation ${url}`);
|
console.log(`[AccountApi] Removing topic reservation ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
|
|
@ -43,7 +43,7 @@ class SubscriptionManager {
|
||||||
for (let i = 0; i < remoteSubscriptions.length; i++) {
|
for (let i = 0; i < remoteSubscriptions.length; i++) {
|
||||||
const remote = remoteSubscriptions[i];
|
const remote = remoteSubscriptions[i];
|
||||||
const local = await this.add(remote.base_url, remote.topic);
|
const local = await this.add(remote.base_url, remote.topic);
|
||||||
const reservation = remoteReservations?.find(r => remote.base_url === config.baseUrl && remote.topic === r.topic) || null;
|
const reservation = remoteReservations?.find(r => remote.base_url === config.base_url && remote.topic === r.topic) || null;
|
||||||
await this.setRemoteId(local.id, remote.id);
|
await this.setRemoteId(local.id, remote.id);
|
||||||
await this.setDisplayName(local.id, remote.display_name);
|
await this.setDisplayName(local.id, remote.display_name);
|
||||||
await this.setReservation(local.id, reservation); // May be null!
|
await this.setReservation(local.id, reservation); // May be null!
|
||||||
|
|
|
@ -11,21 +11,21 @@ class UserManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(baseUrl) {
|
async get(baseUrl) {
|
||||||
if (session.exists() && baseUrl === config.baseUrl) {
|
if (session.exists() && baseUrl === config.base_url) {
|
||||||
return this.localUser();
|
return this.localUser();
|
||||||
}
|
}
|
||||||
return db.users.get(baseUrl);
|
return db.users.get(baseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(user) {
|
async save(user) {
|
||||||
if (session.exists() && user.baseUrl === config.baseUrl) {
|
if (session.exists() && user.baseUrl === config.base_url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await db.users.put(user);
|
await db.users.put(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(baseUrl) {
|
async delete(baseUrl) {
|
||||||
if (session.exists() && baseUrl === config.baseUrl) {
|
if (session.exists() && baseUrl === config.base_url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await db.users.delete(baseUrl);
|
await db.users.delete(baseUrl);
|
||||||
|
@ -36,7 +36,7 @@ class UserManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
baseUrl: config.baseUrl,
|
baseUrl: config.base_url,
|
||||||
username: session.username(),
|
username: session.username(),
|
||||||
token: session.token() // Not "password"!
|
token: session.token() // Not "password"!
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,13 +42,13 @@ export const validTopic = (topic) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const disallowedTopic = (topic) => {
|
export const disallowedTopic = (topic) => {
|
||||||
return config.disallowedTopics.includes(topic);
|
return config.disallowed_topics.includes(topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const topicDisplayName = (subscription) => {
|
export const topicDisplayName = (subscription) => {
|
||||||
if (subscription.displayName) {
|
if (subscription.displayName) {
|
||||||
return subscription.displayName;
|
return subscription.displayName;
|
||||||
} else if (subscription.baseUrl === config.baseUrl) {
|
} else if (subscription.baseUrl === config.base_url) {
|
||||||
return subscription.topic;
|
return subscription.topic;
|
||||||
}
|
}
|
||||||
return topicShortUrl(subscription.baseUrl, subscription.topic);
|
return topicShortUrl(subscription.baseUrl, subscription.topic);
|
||||||
|
|
|
@ -5,17 +5,12 @@ import IconButton from "@mui/material/IconButton";
|
||||||
import MenuIcon from "@mui/icons-material/Menu";
|
import MenuIcon from "@mui/icons-material/Menu";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {useEffect, useRef, useState} from "react";
|
import {useState} from "react";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import {formatShortDateTime, shuffle, topicDisplayName} from "../app/utils";
|
import {formatShortDateTime, shuffle, topicDisplayName} from "../app/utils";
|
||||||
import db from "../app/db";
|
import db from "../app/db";
|
||||||
import {useLocation, useNavigate} from "react-router-dom";
|
import {useLocation, useNavigate} from "react-router-dom";
|
||||||
import ClickAwayListener from '@mui/material/ClickAwayListener';
|
|
||||||
import Grow from '@mui/material/Grow';
|
|
||||||
import Paper from '@mui/material/Paper';
|
|
||||||
import Popper from '@mui/material/Popper';
|
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import MenuList from '@mui/material/MenuList';
|
|
||||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||||
import NotificationsOffIcon from '@mui/icons-material/NotificationsOff';
|
import NotificationsOffIcon from '@mui/icons-material/NotificationsOff';
|
||||||
|
@ -24,7 +19,7 @@ import routes from "./routes";
|
||||||
import subscriptionManager from "../app/SubscriptionManager";
|
import subscriptionManager from "../app/SubscriptionManager";
|
||||||
import logo from "../img/ntfy.svg";
|
import logo from "../img/ntfy.svg";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {Menu, Portal, Snackbar} from "@mui/material";
|
import {Portal, Snackbar} from "@mui/material";
|
||||||
import SubscriptionSettingsDialog from "./SubscriptionSettingsDialog";
|
import SubscriptionSettingsDialog from "./SubscriptionSettingsDialog";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
|
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
|
||||||
|
@ -41,8 +36,10 @@ const ActionBar = (props) => {
|
||||||
let title = "ntfy";
|
let title = "ntfy";
|
||||||
if (props.selected) {
|
if (props.selected) {
|
||||||
title = topicDisplayName(props.selected);
|
title = topicDisplayName(props.selected);
|
||||||
} else if (location.pathname === "/settings") {
|
} else if (location.pathname === routes.settings) {
|
||||||
title = t("action_bar_settings");
|
title = t("action_bar_settings");
|
||||||
|
} else if (location.pathname === routes.account) {
|
||||||
|
title = t("action_bar_account");
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<AppBar position="fixed" sx={{
|
<AppBar position="fixed" sx={{
|
||||||
|
@ -250,12 +247,12 @@ const ProfileIcon = () => {
|
||||||
<AccountCircleIcon/>
|
<AccountCircleIcon/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
{!session.exists() && config.enableLogin &&
|
{!session.exists() && config.enable_login &&
|
||||||
<Button color="inherit" variant="text" onClick={() => navigate(routes.login)} sx={{m: 1}} aria-label={t("action_bar_sign_in")}>
|
<Button color="inherit" variant="text" onClick={() => navigate(routes.login)} sx={{m: 1}} aria-label={t("action_bar_sign_in")}>
|
||||||
{t("action_bar_sign_in")}
|
{t("action_bar_sign_in")}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
{!session.exists() && config.enableSignup &&
|
{!session.exists() && config.enable_signup &&
|
||||||
<Button color="inherit" variant="outlined" onClick={() => navigate(routes.signup)} aria-label={t("action_bar_sign_up")}>
|
<Button color="inherit" variant="outlined" onClick={() => navigate(routes.signup)} aria-label={t("action_bar_sign_up")}>
|
||||||
{t("action_bar_sign_up")}
|
{t("action_bar_sign_up")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -79,7 +79,7 @@ const Layout = () => {
|
||||||
const newNotificationsCount = subscriptions?.reduce((prev, cur) => prev + cur.new, 0) || 0;
|
const newNotificationsCount = subscriptions?.reduce((prev, cur) => prev + cur.new, 0) || 0;
|
||||||
const [selected] = (subscriptions || []).filter(s => {
|
const [selected] = (subscriptions || []).filter(s => {
|
||||||
return (params.baseUrl && expandUrl(params.baseUrl).includes(s.baseUrl) && params.topic === s.topic)
|
return (params.baseUrl && expandUrl(params.baseUrl).includes(s.baseUrl) && params.topic === s.topic)
|
||||||
|| (config.baseUrl === s.baseUrl && params.topic === s.topic)
|
|| (config.base_url === s.baseUrl && params.topic === s.topic)
|
||||||
});
|
});
|
||||||
|
|
||||||
useConnectionListeners(subscriptions, users);
|
useConnectionListeners(subscriptions, users);
|
||||||
|
@ -95,6 +95,7 @@ const Layout = () => {
|
||||||
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
|
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
|
||||||
/>
|
/>
|
||||||
<Navigation
|
<Navigation
|
||||||
|
account={account}
|
||||||
subscriptions={subscriptions}
|
subscriptions={subscriptions}
|
||||||
selectedSubscription={selected}
|
selectedSubscription={selected}
|
||||||
notificationsGranted={notificationsGranted}
|
notificationsGranted={notificationsGranted}
|
||||||
|
|
|
@ -41,7 +41,7 @@ const Login = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!config.enableLogin) {
|
if (!config.enable_login) {
|
||||||
return (
|
return (
|
||||||
<AvatarBox>
|
<AvatarBox>
|
||||||
<Typography sx={{ typography: 'h6' }}>{t("Login is disabled")}</Typography>
|
<Typography sx={{ typography: 'h6' }}>{t("Login is disabled")}</Typography>
|
||||||
|
@ -112,8 +112,8 @@ const Login = () => {
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
<Box sx={{width: "100%"}}>
|
<Box sx={{width: "100%"}}>
|
||||||
{config.enableResetPassword && <div style={{float: "left"}}><NavLink to={routes.resetPassword} variant="body1">{t("Reset password")}</NavLink></div>}
|
{config.enable_password_reset && <div style={{float: "left"}}><NavLink to={routes.resetPassword} variant="body1">{t("Reset password")}</NavLink></div>}
|
||||||
{config.enableSignup && <div style={{float: "right"}}><NavLink to={routes.signup} variant="body1">{t("login_link_signup")}</NavLink></div>}
|
{config.enable_signup && <div style={{float: "right"}}><NavLink to={routes.signup} variant="body1">{t("login_link_signup")}</NavLink></div>}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</AvatarBox>
|
</AvatarBox>
|
||||||
|
|
|
@ -38,7 +38,7 @@ const Messaging = (props) => {
|
||||||
<PublishDialog
|
<PublishDialog
|
||||||
key={`publishDialog${dialogKey}`} // Resets dialog when canceled/closed
|
key={`publishDialog${dialogKey}`} // Resets dialog when canceled/closed
|
||||||
openMode={dialogOpenMode}
|
openMode={dialogOpenMode}
|
||||||
baseUrl={subscription?.baseUrl ?? config.baseUrl}
|
baseUrl={subscription?.baseUrl ?? config.base_url}
|
||||||
topic={subscription?.topic ?? ""}
|
topic={subscription?.topic ?? ""}
|
||||||
message={message}
|
message={message}
|
||||||
onClose={handleDialogClose}
|
onClose={handleDialogClose}
|
||||||
|
|
|
@ -12,24 +12,15 @@ import List from "@mui/material/List";
|
||||||
import SettingsIcon from "@mui/icons-material/Settings";
|
import SettingsIcon from "@mui/icons-material/Settings";
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import SubscribeDialog from "./SubscribeDialog";
|
import SubscribeDialog from "./SubscribeDialog";
|
||||||
import {
|
import {Alert, AlertTitle, Badge, CircularProgress, Link, ListSubheader, Tooltip} from "@mui/material";
|
||||||
Alert,
|
|
||||||
AlertTitle,
|
|
||||||
Badge,
|
|
||||||
CircularProgress,
|
|
||||||
Link,
|
|
||||||
ListItem,
|
|
||||||
ListItemSecondaryAction,
|
|
||||||
ListSubheader, Tooltip
|
|
||||||
} from "@mui/material";
|
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import {openUrl, topicDisplayName, topicUrl} from "../app/utils";
|
import {openUrl, topicDisplayName, topicUrl} from "../app/utils";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import {ConnectionState} from "../app/Connection";
|
import {ConnectionState} from "../app/Connection";
|
||||||
import {useLocation, useNavigate, useOutletContext} from "react-router-dom";
|
import {useLocation, useNavigate} from "react-router-dom";
|
||||||
import subscriptionManager from "../app/SubscriptionManager";
|
import subscriptionManager from "../app/SubscriptionManager";
|
||||||
import {ChatBubble, Lock, MoreVert, NotificationsOffOutlined, Public, PublicOff, Send} from "@mui/icons-material";
|
import {ChatBubble, Lock, NotificationsOffOutlined, Public, PublicOff, Send} from "@mui/icons-material";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import notifier from "../app/Notifier";
|
import notifier from "../app/Notifier";
|
||||||
import config from "../app/config";
|
import config from "../app/config";
|
||||||
|
@ -37,8 +28,7 @@ import ArticleIcon from '@mui/icons-material/Article';
|
||||||
import {Trans, useTranslation} from "react-i18next";
|
import {Trans, useTranslation} from "react-i18next";
|
||||||
import session from "../app/Session";
|
import session from "../app/Session";
|
||||||
import accountApi from "../app/AccountApi";
|
import accountApi from "../app/AccountApi";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import CelebrationIcon from '@mui/icons-material/Celebration';
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
|
||||||
|
|
||||||
const navWidth = 280;
|
const navWidth = 280;
|
||||||
|
|
||||||
|
@ -109,6 +99,7 @@ const NavList = (props) => {
|
||||||
navigate(routes.account);
|
navigate(routes.account);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showUpgradeBanner = config.enable_payments && (!props.account || props.account.plan.upgradeable);
|
||||||
const showSubscriptionsList = props.subscriptions?.length > 0;
|
const showSubscriptionsList = props.subscriptions?.length > 0;
|
||||||
const showNotificationBrowserNotSupportedBox = !notifier.browserSupported();
|
const showNotificationBrowserNotSupportedBox = !notifier.browserSupported();
|
||||||
const showNotificationContextNotSupportedBox = notifier.browserSupported() && !notifier.contextSupported(); // Only show if notifications are generally supported in the browser
|
const showNotificationContextNotSupportedBox = notifier.browserSupported() && !notifier.contextSupported(); // Only show if notifications are generally supported in the browser
|
||||||
|
@ -123,14 +114,14 @@ const NavList = (props) => {
|
||||||
{showNotificationContextNotSupportedBox && <NotificationContextNotSupportedAlert/>}
|
{showNotificationContextNotSupportedBox && <NotificationContextNotSupportedAlert/>}
|
||||||
{showNotificationGrantBox && <NotificationGrantAlert onRequestPermissionClick={handleRequestNotificationPermission}/>}
|
{showNotificationGrantBox && <NotificationGrantAlert onRequestPermissionClick={handleRequestNotificationPermission}/>}
|
||||||
{!showSubscriptionsList &&
|
{!showSubscriptionsList &&
|
||||||
<ListItemButton onClick={() => navigate(routes.app)} selected={location.pathname === config.appRoot}>
|
<ListItemButton onClick={() => navigate(routes.app)} selected={location.pathname === config.app_root}>
|
||||||
<ListItemIcon><ChatBubble/></ListItemIcon>
|
<ListItemIcon><ChatBubble/></ListItemIcon>
|
||||||
<ListItemText primary={t("nav_button_all_notifications")}/>
|
<ListItemText primary={t("nav_button_all_notifications")}/>
|
||||||
</ListItemButton>}
|
</ListItemButton>}
|
||||||
{showSubscriptionsList &&
|
{showSubscriptionsList &&
|
||||||
<>
|
<>
|
||||||
<ListSubheader>{t("nav_topics_title")}</ListSubheader>
|
<ListSubheader>{t("nav_topics_title")}</ListSubheader>
|
||||||
<ListItemButton onClick={() => navigate(routes.app)} selected={location.pathname === config.appRoot}>
|
<ListItemButton onClick={() => navigate(routes.app)} selected={location.pathname === config.app_root}>
|
||||||
<ListItemIcon><ChatBubble/></ListItemIcon>
|
<ListItemIcon><ChatBubble/></ListItemIcon>
|
||||||
<ListItemText primary={t("nav_button_all_notifications")}/>
|
<ListItemText primary={t("nav_button_all_notifications")}/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
@ -162,6 +153,34 @@ const NavList = (props) => {
|
||||||
<ListItemIcon><AddIcon/></ListItemIcon>
|
<ListItemIcon><AddIcon/></ListItemIcon>
|
||||||
<ListItemText primary={t("nav_button_subscribe")}/>
|
<ListItemText primary={t("nav_button_subscribe")}/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
{showUpgradeBanner &&
|
||||||
|
<Box sx={{
|
||||||
|
position: "fixed",
|
||||||
|
width: `${Navigation.width - 1}px`,
|
||||||
|
bottom: 0,
|
||||||
|
mt: 'auto',
|
||||||
|
background: "linear-gradient(150deg, rgba(196, 228, 221, 0.46) 0%, rgb(255, 255, 255) 100%)",
|
||||||
|
}}>
|
||||||
|
<Divider/>
|
||||||
|
<ListItemButton onClick={() => setSubscribeDialogOpen(true)}>
|
||||||
|
<ListItemIcon><CelebrationIcon sx={{ color: "#55b86e" }} fontSize="large"/></ListItemIcon>
|
||||||
|
<ListItemText
|
||||||
|
sx={{ ml: 1 }}
|
||||||
|
primary={"Upgrade to ntfy Pro"}
|
||||||
|
secondary={"Reserve topics, more messages & emails, bigger attachments"}
|
||||||
|
primaryTypographyProps={{
|
||||||
|
style: {
|
||||||
|
fontWeight: 500,
|
||||||
|
background: "-webkit-linear-gradient(45deg, #09009f, #00ff95 80%)",
|
||||||
|
WebkitBackgroundClip: "text",
|
||||||
|
WebkitTextFillColor: "transparent"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ListItemButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
}
|
||||||
</List>
|
</List>
|
||||||
<SubscribeDialog
|
<SubscribeDialog
|
||||||
key={`subscribeDialog${subscribeDialogKey}`} // Resets dialog when canceled/closed
|
key={`subscribeDialog${subscribeDialogKey}`} // Resets dialog when canceled/closed
|
||||||
|
|
|
@ -304,7 +304,7 @@ const UserTable = (props) => {
|
||||||
aria-label={t("prefs_users_table_user_header")}>{user.username}</TableCell>
|
aria-label={t("prefs_users_table_user_header")}>{user.username}</TableCell>
|
||||||
<TableCell aria-label={t("prefs_users_table_base_url_header")}>{user.baseUrl}</TableCell>
|
<TableCell aria-label={t("prefs_users_table_base_url_header")}>{user.baseUrl}</TableCell>
|
||||||
<TableCell align="right">
|
<TableCell align="right">
|
||||||
{(!session.exists() || user.baseUrl !== config.baseUrl) &&
|
{(!session.exists() || user.baseUrl !== config.base_url) &&
|
||||||
<>
|
<>
|
||||||
<IconButton onClick={() => handleEditClick(user)} aria-label={t("prefs_users_edit_button")}>
|
<IconButton onClick={() => handleEditClick(user)} aria-label={t("prefs_users_edit_button")}>
|
||||||
<EditIcon/>
|
<EditIcon/>
|
||||||
|
@ -314,7 +314,7 @@ const UserTable = (props) => {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{session.exists() && user.baseUrl === config.baseUrl &&
|
{session.exists() && user.baseUrl === config.base_url &&
|
||||||
<Tooltip title={t("prefs_users_table_cannot_delete_or_edit")}>
|
<Tooltip title={t("prefs_users_table_cannot_delete_or_edit")}>
|
||||||
<span>
|
<span>
|
||||||
<IconButton disabled><EditIcon/></IconButton>
|
<IconButton disabled><EditIcon/></IconButton>
|
||||||
|
@ -525,6 +525,9 @@ const Reservations = () => {
|
||||||
{limitReached &&
|
{limitReached &&
|
||||||
<Alert severity="info">
|
<Alert severity="info">
|
||||||
You reached your reserved topics limit.
|
You reached your reserved topics limit.
|
||||||
|
{config.enable_payments &&
|
||||||
|
<>{" "}<b>Upgrade</b></>
|
||||||
|
}
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
@ -43,7 +43,7 @@ const Signup = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!config.enableSignup) {
|
if (!config.enable_signup) {
|
||||||
return (
|
return (
|
||||||
<AvatarBox>
|
<AvatarBox>
|
||||||
<Typography sx={{ typography: 'h6' }}>{t("signup_disabled")}</Typography>
|
<Typography sx={{ typography: 'h6' }}>{t("signup_disabled")}</Typography>
|
||||||
|
@ -114,7 +114,7 @@ const Signup = () => {
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
{config.enableLogin &&
|
{config.enable_login &&
|
||||||
<Typography sx={{mb: 4}}>
|
<Typography sx={{mb: 4}}>
|
||||||
<NavLink to={routes.login} variant="body1">
|
<NavLink to={routes.login} variant="body1">
|
||||||
{t("signup_already_have_account")}
|
{t("signup_already_have_account")}
|
||||||
|
|
|
@ -18,13 +18,8 @@ import {useTranslation} from "react-i18next";
|
||||||
import session from "../app/Session";
|
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 PublicIcon from '@mui/icons-material/Public';
|
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
|
||||||
import PublicOffIcon from '@mui/icons-material/PublicOff';
|
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
|
||||||
import PopupMenu from "./PopupMenu";
|
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
|
||||||
import ReserveTopicSelect from "./ReserveTopicSelect";
|
import ReserveTopicSelect from "./ReserveTopicSelect";
|
||||||
|
import {useOutletContext} from "react-router-dom";
|
||||||
|
|
||||||
const publicBaseUrl = "https://ntfy.sh";
|
const publicBaseUrl = "https://ntfy.sh";
|
||||||
|
|
||||||
|
@ -36,7 +31,7 @@ const SubscribeDialog = (props) => {
|
||||||
|
|
||||||
const handleSuccess = async () => {
|
const handleSuccess = async () => {
|
||||||
console.log(`[SubscribeDialog] Subscribing to topic ${topic}`);
|
console.log(`[SubscribeDialog] Subscribing to topic ${topic}`);
|
||||||
const actualBaseUrl = (baseUrl) ? baseUrl : config.baseUrl;
|
const actualBaseUrl = (baseUrl) ? baseUrl : config.base_url;
|
||||||
const subscription = await subscriptionManager.add(actualBaseUrl, topic);
|
const subscription = await subscriptionManager.add(actualBaseUrl, topic);
|
||||||
if (session.exists()) {
|
if (session.exists()) {
|
||||||
try {
|
try {
|
||||||
|
@ -81,17 +76,18 @@ const SubscribeDialog = (props) => {
|
||||||
|
|
||||||
const SubscribePage = (props) => {
|
const SubscribePage = (props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { account } = useOutletContext();
|
||||||
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("");
|
||||||
const [accessAnchorEl, setAccessAnchorEl] = useState(null);
|
|
||||||
const [everyone, setEveryone] = useState("deny-all");
|
const [everyone, setEveryone] = useState("deny-all");
|
||||||
const baseUrl = (anotherServerVisible) ? props.baseUrl : config.baseUrl;
|
const baseUrl = (anotherServerVisible) ? props.baseUrl : config.base_url;
|
||||||
const topic = props.topic;
|
const topic = props.topic;
|
||||||
const existingTopicUrls = props.subscriptions.map(s => topicUrl(s.baseUrl, s.topic));
|
const existingTopicUrls = props.subscriptions.map(s => topicUrl(s.baseUrl, s.topic));
|
||||||
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.baseUrl);
|
.filter(s => s !== config.base_url);
|
||||||
|
const reserveTopicEnabled = session.exists() && (account?.stats.topics_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
|
||||||
|
@ -111,7 +107,7 @@ const SubscribePage = (props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve topic (if requested)
|
// Reserve topic (if requested)
|
||||||
if (session.exists() && baseUrl === config.baseUrl && reserveTopicVisible) {
|
if (session.exists() && baseUrl === config.base_url && reserveTopicVisible) {
|
||||||
console.log(`[SubscribeDialog] Reserving topic ${topic} with everyone access ${everyone}`);
|
console.log(`[SubscribeDialog] Reserving topic ${topic} with everyone access ${everyone}`);
|
||||||
try {
|
try {
|
||||||
await accountApi.upsertAccess(topic, everyone);
|
await accountApi.upsertAccess(topic, everyone);
|
||||||
|
@ -141,7 +137,7 @@ const SubscribePage = (props) => {
|
||||||
const isExistingTopicUrl = existingTopicUrls.includes(topicUrl(baseUrl, topic));
|
const isExistingTopicUrl = existingTopicUrls.includes(topicUrl(baseUrl, topic));
|
||||||
return validTopic(topic) && validUrl(baseUrl) && !isExistingTopicUrl;
|
return validTopic(topic) && validUrl(baseUrl) && !isExistingTopicUrl;
|
||||||
} else {
|
} else {
|
||||||
const isExistingTopicUrl = existingTopicUrls.includes(topicUrl(config.baseUrl, topic));
|
const isExistingTopicUrl = existingTopicUrls.includes(topicUrl(config.base_url, topic));
|
||||||
return validTopic(topic) && !isExistingTopicUrl;
|
return validTopic(topic) && !isExistingTopicUrl;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -180,30 +176,6 @@ const SubscribePage = (props) => {
|
||||||
<Button onClick={() => {props.setTopic(randomAlphanumericString(16))}} style={{flexShrink: "0", marginTop: "0.5em"}}>
|
<Button onClick={() => {props.setTopic(randomAlphanumericString(16))}} style={{flexShrink: "0", marginTop: "0.5em"}}>
|
||||||
{t("subscribe_dialog_subscribe_button_generate_topic_name")}
|
{t("subscribe_dialog_subscribe_button_generate_topic_name")}
|
||||||
</Button>
|
</Button>
|
||||||
<PopupMenu
|
|
||||||
anchorEl={accessAnchorEl}
|
|
||||||
open={!!accessAnchorEl}
|
|
||||||
onClose={() => setAccessAnchorEl(null)}
|
|
||||||
>
|
|
||||||
<MenuItem onClick={() => setEveryone("private")} selected={everyone === "private"}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<LockIcon fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
Only I can publish and subscribe
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={() => setEveryone("public-read")} selected={everyone === "public-read"}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<PublicOffIcon fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
I can publish, everyone can subscribe
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={() => setEveryone("public")} selected={everyone === "public"}>
|
|
||||||
<ListItemIcon>
|
|
||||||
<PublicIcon fontSize="small" />
|
|
||||||
</ListItemIcon>
|
|
||||||
Everyone can publish and subscribe
|
|
||||||
</MenuItem>
|
|
||||||
</PopupMenu>
|
|
||||||
</div>
|
</div>
|
||||||
{session.exists() && !anotherServerVisible &&
|
{session.exists() && !anotherServerVisible &&
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
|
@ -212,6 +184,7 @@ const SubscribePage = (props) => {
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
fullWidth
|
fullWidth
|
||||||
|
disabled={account.stats.topics_remaining}
|
||||||
checked={reserveTopicVisible}
|
checked={reserveTopicVisible}
|
||||||
onChange={(ev) => setReserveTopicVisible(ev.target.checked)}
|
onChange={(ev) => setReserveTopicVisible(ev.target.checked)}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
|
@ -249,7 +222,7 @@ const SubscribePage = (props) => {
|
||||||
renderInput={(params) =>
|
renderInput={(params) =>
|
||||||
<TextField
|
<TextField
|
||||||
{...params}
|
{...params}
|
||||||
placeholder={config.baseUrl}
|
placeholder={config.base_url}
|
||||||
variant="standard"
|
variant="standard"
|
||||||
aria-label={t("subscribe_dialog_subscribe_base_url_label")}
|
aria-label={t("subscribe_dialog_subscribe_base_url_label")}
|
||||||
/>
|
/>
|
||||||
|
@ -271,7 +244,7 @@ const LoginPage = (props) => {
|
||||||
const [username, setUsername] = useState("");
|
const [username, setUsername] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [errorText, setErrorText] = useState("");
|
const [errorText, setErrorText] = useState("");
|
||||||
const baseUrl = (props.baseUrl) ? props.baseUrl : config.baseUrl;
|
const baseUrl = (props.baseUrl) ? props.baseUrl : config.base_url;
|
||||||
const topic = props.topic;
|
const topic = props.topic;
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
const user = {baseUrl, username, password};
|
const user = {baseUrl, username, password};
|
||||||
|
|
|
@ -60,7 +60,7 @@ export const useAutoSubscribe = (subscriptions, selected) => {
|
||||||
setHasRun(true);
|
setHasRun(true);
|
||||||
const eligible = params.topic && !selected && !disallowedTopic(params.topic);
|
const eligible = params.topic && !selected && !disallowedTopic(params.topic);
|
||||||
if (eligible) {
|
if (eligible) {
|
||||||
const baseUrl = (params.baseUrl) ? expandSecureUrl(params.baseUrl) : config.baseUrl;
|
const baseUrl = (params.baseUrl) ? expandSecureUrl(params.baseUrl) : config.base_url;
|
||||||
console.log(`[App] Auto-subscribing to ${topicUrl(baseUrl, params.topic)}`);
|
console.log(`[App] Auto-subscribing to ${topicUrl(baseUrl, params.topic)}`);
|
||||||
(async () => {
|
(async () => {
|
||||||
const subscription = await subscriptionManager.add(baseUrl, params.topic);
|
const subscription = await subscriptionManager.add(baseUrl, params.topic);
|
||||||
|
|
|
@ -9,13 +9,13 @@ const routes = {
|
||||||
login: "/login",
|
login: "/login",
|
||||||
signup: "/signup",
|
signup: "/signup",
|
||||||
resetPassword: "/reset-password",
|
resetPassword: "/reset-password",
|
||||||
app: config.appRoot,
|
app: config.app_root,
|
||||||
account: "/account",
|
account: "/account",
|
||||||
settings: "/settings",
|
settings: "/settings",
|
||||||
subscription: "/:topic",
|
subscription: "/:topic",
|
||||||
subscriptionExternal: "/:baseUrl/:topic",
|
subscriptionExternal: "/:baseUrl/:topic",
|
||||||
forSubscription: (subscription) => {
|
forSubscription: (subscription) => {
|
||||||
if (subscription.baseUrl !== config.baseUrl) {
|
if (subscription.baseUrl !== config.base_url) {
|
||||||
return `/${shortUrl(subscription.baseUrl)}/${subscription.topic}`;
|
return `/${shortUrl(subscription.baseUrl)}/${subscription.topic}`;
|
||||||
}
|
}
|
||||||
return `/${subscription.topic}`;
|
return `/${subscription.topic}`;
|
||||||
|
|
Loading…
Reference in New Issue