diff --git a/web/src/app/AccountApi.js b/web/src/app/AccountApi.js
index 2b057d9a..cc13452d 100644
--- a/web/src/app/AccountApi.js
+++ b/web/src/app/AccountApi.js
@@ -12,7 +12,7 @@ import {
topicUrl,
topicUrlAuth,
topicUrlJsonPoll,
- topicUrlJsonPollWithSince
+ topicUrlJsonPollWithSince, accountAccessUrl, accountAccessSingleUrl
} from "./utils";
import userManager from "./UserManager";
import session from "./Session";
@@ -208,6 +208,38 @@ class AccountApi {
}
}
+ async upsertAccess(topic, everyone) {
+ const url = accountAccessUrl(config.baseUrl);
+ console.log(`[AccountApi] Upserting user access to topic ${topic}, everyone=${everyone}`);
+ const response = await fetch(url, {
+ method: "POST",
+ headers: withBearerAuth({}, session.token()),
+ body: JSON.stringify({
+ topic: topic,
+ everyone: everyone
+ })
+ });
+ if (response.status === 401 || response.status === 403) {
+ throw new UnauthorizedError();
+ } else if (response.status !== 200) {
+ throw new Error(`Unexpected server response ${response.status}`);
+ }
+ }
+
+ async deleteAccess(topic) {
+ const url = accountAccessSingleUrl(config.baseUrl, topic);
+ console.log(`[AccountApi] Removing topic reservation ${url}`);
+ const response = await fetch(url, {
+ method: "DELETE",
+ headers: withBearerAuth({}, session.token())
+ });
+ if (response.status === 401 || response.status === 403) {
+ throw new UnauthorizedError();
+ } else if (response.status !== 200) {
+ throw new Error(`Unexpected server response ${response.status}`);
+ }
+ }
+
sync() {
// TODO
}
diff --git a/web/src/app/utils.js b/web/src/app/utils.js
index 6170cc25..fdddab05 100644
--- a/web/src/app/utils.js
+++ b/web/src/app/utils.js
@@ -24,6 +24,8 @@ export const accountTokenUrl = (baseUrl) => `${baseUrl}/v1/account/token`;
export const accountSettingsUrl = (baseUrl) => `${baseUrl}/v1/account/settings`;
export const accountSubscriptionUrl = (baseUrl) => `${baseUrl}/v1/account/subscription`;
export const accountSubscriptionSingleUrl = (baseUrl, id) => `${baseUrl}/v1/account/subscription/${id}`;
+export const accountAccessUrl = (baseUrl) => `${baseUrl}/v1/account/access`;
+export const accountAccessSingleUrl = (baseUrl, topic) => `${baseUrl}/v1/account/access/${topic}`;
export const shortUrl = (url) => url.replaceAll(/https?:\/\//g, "");
export const expandUrl = (url) => [`https://${url}`, `http://${url}`];
export const expandSecureUrl = (url) => `https://${url}`;
diff --git a/web/src/components/App.js b/web/src/components/App.js
index 6c714bfb..1374d8dd 100644
--- a/web/src/components/App.js
+++ b/web/src/components/App.js
@@ -33,9 +33,6 @@ import Account from "./Account";
import ResetPassword from "./ResetPassword";
import accountApi, {UnauthorizedError} from "../app/AccountApi";
-// TODO races when two tabs are open
-// TODO investigate service workers
-
const App = () => {
return (
}>
diff --git a/web/src/components/Preferences.js b/web/src/components/Preferences.js
index b46e4617..32c48d2f 100644
--- a/web/src/components/Preferences.js
+++ b/web/src/components/Preferences.js
@@ -33,7 +33,7 @@ import DialogTitle from "@mui/material/DialogTitle";
import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions";
import userManager from "../app/UserManager";
-import {playSound, shuffle, sounds, validUrl} from "../app/utils";
+import {playSound, shuffle, sounds, validTopic, validUrl} from "../app/utils";
import {useTranslation} from "react-i18next";
import session from "../app/Session";
import routes from "./routes";
@@ -491,14 +491,15 @@ const Reservations = () => {
setDialogOpen(false);
};
- const handleDialogSubmit = async (entry) => {
+ const handleDialogSubmit = async (reservation) => {
setDialogOpen(false);
try {
- await accountApi.addAccessEntry();
- console.debug(`[Preferences] Added entry ${entry.topic}`);
+ await accountApi.upsertAccess(reservation.topic, reservation.everyone);
+ console.debug(`[Preferences] Added topic reservation`, reservation);
} catch (e) {
- console.log(`[Preferences] Error adding access entry.`, e);
+ console.log(`[Preferences] Error topic reservation.`, e);
}
+ // FIXME handle 401/403
};
if (!session.exists() || !account) {
@@ -519,10 +520,10 @@ const Reservations = () => {
@@ -535,11 +536,11 @@ const ReservationsTable = (props) => {
const { t } = useTranslation();
const [dialogKey, setDialogKey] = useState(0);
const [dialogOpen, setDialogOpen] = useState(false);
- const [dialogEntry, setDialogEntry] = useState(null);
+ const [dialogReservation, setDialogReservation] = useState(null);
- const handleEditClick = (entry) => {
+ const handleEditClick = (reservation) => {
setDialogKey(prev => prev+1);
- setDialogEntry(entry);
+ setDialogReservation(reservation);
setDialogOpen(true);
};
@@ -547,13 +548,25 @@ const ReservationsTable = (props) => {
setDialogOpen(false);
};
- const handleDialogSubmit = async (user) => {
+ const handleDialogSubmit = async (reservation) => {
setDialogOpen(false);
- // FIXME
+ try {
+ await accountApi.upsertAccess(reservation.topic, reservation.everyone);
+ console.debug(`[Preferences] Added topic reservation`, reservation);
+ } catch (e) {
+ console.log(`[Preferences] Error topic reservation.`, e);
+ }
+ // FIXME handle 401/403
};
- const handleDeleteClick = async (user) => {
- // FIXME
+ const handleDeleteClick = async (reservation) => {
+ try {
+ await accountApi.deleteAccess(reservation.topic);
+ console.debug(`[Preferences] Deleted topic reservation`, reservation);
+ } catch (e) {
+ console.log(`[Preferences] Error topic reservation.`, e);
+ }
+ // FIXME handle 401/403
};
return (
@@ -575,25 +588,25 @@ const ReservationsTable = (props) => {
{reservation.everyone === "read-write" &&
<>
-
+
{t("prefs_reservations_table_everyone_read_write")}
>
}
{reservation.everyone === "read-only" &&
<>
-
+
{t("prefs_reservations_table_everyone_read_only")}
>
}
{reservation.everyone === "write-only" &&
<>
-
+
{t("prefs_reservations_table_everyone_write_only")}
>
}
{reservation.everyone === "deny-all" &&
<>
-
+
{t("prefs_reservations_table_everyone_deny_all")}
>
}
@@ -610,10 +623,10 @@ const ReservationsTable = (props) => {
))}
@@ -624,24 +637,31 @@ const ReservationsTable = (props) => {
const ReservationsDialog = (props) => {
const { t } = useTranslation();
const [topic, setTopic] = useState("");
- const [access, setAccess] = useState("private");
+ const [everyone, setEveryone] = useState("deny-all");
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
- const editMode = props.entry !== null;
+ const editMode = props.reservation !== null;
const addButtonEnabled = (() => {
- // FIXME
+ if (editMode) {
+ return true;
+ } else if (!validTopic(topic)) {
+ return false;
+ }
+ return props.reservations
+ .filter(r => r.topic === topic)
+ .length === 0;
})();
const handleSubmit = async () => {
props.onSubmit({
- topic: topic,
- // FIXME
+ topic: (editMode) ? props.reservation.topic : topic,
+ everyone: everyone
})
};
useEffect(() => {
if (editMode) {
- setTopic(props.topic);
- //setAccess(props.access);
+ setTopic(props.reservation.topic);
+ setEveryone(props.reservation.everyone);
}
- }, [editMode, props]);
+ }, [editMode, props.reservation]);
return (