Skip to content

Commit

Permalink
Merge pull request #691 from NatalieWilson19/allow-hosts-to-change-he…
Browse files Browse the repository at this point in the history
…aders-and-subheaders-#360

Allow hosts to change headers and subheaders #360
  • Loading branch information
lil5 authored Feb 23, 2024
2 parents 689ce70 + 8b669ad commit 5a84de9
Show file tree
Hide file tree
Showing 15 changed files with 468 additions and 51 deletions.
7 changes: 6 additions & 1 deletion app/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,10 @@
"day_other": "{{count}} days",
"untilITurnItBackOn": "Until I turn it back on",
"selectPauseDuration": "Select pause duration",
"setTimerForACoupleOfWeeks": "Select how many weeks you want to pause your participation"
"setTimerForACoupleOfWeeks": "Select how many weeks you want to pause your participation",
"updateHeader": "Edit this page's header",
"updateHeaderDesc": "This change will be visible to all loop members.",
"restHeaders": "Rest headers",
"resetHeadersDesc": "Reset your loop's headers to the default headers.",
"areYouSureYouWantToResetHeaders": "Are you sure you want to reset the loop's headers to defualt?"
}
32 changes: 32 additions & 0 deletions app/src/Store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
bulkyItemGetAllByChain,
userUpdate,
chainUpdate,
ChainHeaders,
} from "./api";
import dayjs from "./dayjs";
import { OverlayContainsState, OverlayState } from "./utils/overlay_open";
Expand Down Expand Up @@ -43,6 +44,8 @@ export const StoreContext = createContext({
setTheme: (c: string) => {},
chain: null as Chain | null,
chainUsers: [] as Array<User>,
chainHeaders: undefined as ChainHeaders | undefined,
getChainHeader: (key: string, override: string) => override,
listOfChains: [] as Array<Chain>,
route: [] as UID[],
bags: [] as Bag[],
Expand Down Expand Up @@ -74,6 +77,9 @@ export function StoreProvider({
}) {
const [authUser, setAuthUser] = useState<User | null>(null);
const [chain, _setChain] = useState<Chain | null>(null);
const [chainHeaders, setChainHeaders] = useState<ChainHeaders | undefined>(
undefined,
);
const [chainUsers, setChainUsers] = useState<Array<User>>([]);
const [listOfChains, setListOfChains] = useState<Array<Chain>>([]);
const [route, setRoute] = useState<UID[]>([]);
Expand Down Expand Up @@ -133,6 +139,7 @@ export function StoreProvider({
await storage.set("chain_uid", "");
setAuthUser(null);
_setChain(null);
setChainHeaders(undefined);
setListOfChains([]);
setRoute([]);
setBags([]);
Expand Down Expand Up @@ -215,6 +222,7 @@ export function StoreProvider({
) {
let _chain: typeof chain = null;
let _chainUsers: typeof chainUsers = [];
let _chainHeaders: typeof chainHeaders = undefined;
let _route: typeof route = [];
let _bags: typeof bags = [];
let _isChainAdmin: typeof isChainAdmin = false;
Expand All @@ -224,6 +232,7 @@ export function StoreProvider({
const res = await Promise.all([
chainGet(_chainUID, {
addRules: true,
addHeaders: true,
addTheme: true,
addIsAppDisabled: true,
}),
Expand All @@ -233,6 +242,7 @@ export function StoreProvider({
bulkyItemGetAllByChain(_chainUID, _authUser.uid),
]);
_chain = res[0].data;
_chainHeaders = ChainReadHeaders(_chain);
_chainUsers = res[1].data;
_route = res[2].data;
_bags = res[3].data;
Expand All @@ -252,6 +262,7 @@ export function StoreProvider({

await storage.set("chain_uid", _chainUID ? _chainUID : null);
_setChain(_chain);
setChainHeaders(_chainHeaders);
setChainUsers(_chainUsers);
setRoute(_route);
setBags(_bags);
Expand Down Expand Up @@ -304,6 +315,7 @@ export function StoreProvider({

let _chain = await chainGet(chain.uid, {
addRules: true,
addHeaders: true,
addTheme: true,
addIsAppDisabled: true,
});
Expand Down Expand Up @@ -337,6 +349,7 @@ export function StoreProvider({
const isCurrentChain = uc.chain_uid === chain?.uid;
return chainGet(uc.chain_uid, {
addRules: isCurrentChain,
addHeaders: isCurrentChain,
addTheme: isCurrentChain,
addIsAppDisabled: true,
});
Expand All @@ -353,6 +366,7 @@ export function StoreProvider({
setAuthUser(_authUser.data);
setListOfChains(_listOfChains);
_setChain(_chain);
setChainHeaders(ChainReadHeaders(_chain));
}
} catch (err: any) {
if (err === errLoopMustBeSelected) {
Expand Down Expand Up @@ -392,6 +406,14 @@ export function StoreProvider({
return IsAuthenticated.OfflineLoggedIn;
}

function getChainHeader(key: string, override: string): string {
if (chainHeaders && chainHeaders[key]) {
return chainHeaders[key];
}

return override;
}

return (
<StoreContext.Provider
value={{
Expand All @@ -402,8 +424,10 @@ export function StoreProvider({
bags,
bulkyItems,
chain,
chainHeaders,
isThemeDefault,
chainUsers,
getChainHeader,
listOfChains,
setChain,
isAuthenticated,
Expand Down Expand Up @@ -435,3 +459,11 @@ export function IsChainAdmin(
: undefined;
return userChain?.is_chain_admin || false;
}

function ChainReadHeaders(
chain: Chain | null | undefined,
): ChainHeaders | undefined {
if (chain?.headers_override) {
return JSON.parse(chain.headers_override);
}
}
5 changes: 5 additions & 0 deletions app/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export interface UserChain {
created_at: string;
}

export type ChainHeaders = Record<string, string>;

export interface Chain {
uid: UID;
name: string;
Expand All @@ -78,6 +80,7 @@ export interface Chain {
published: boolean;
open_to_new_members: boolean;
rules_override?: string;
headers_override?: string;
theme?: string;
is_app_disabled?: boolean;
}
Expand Down Expand Up @@ -156,6 +159,7 @@ export function chainGet(
chainUID: UID,
o: {
addRules?: boolean;
addHeaders?: boolean;
addTheme?: boolean;
addIsAppDisabled?: boolean;
} = {},
Expand All @@ -164,6 +168,7 @@ export function chainGet(
params: {
chain_uid: chainUID,
add_rules: o.addRules || false,
add_headers: o.addHeaders || false,
add_theme: o.addTheme || false,
add_is_app_disabled: o.addIsAppDisabled || false,
},
Expand Down
161 changes: 161 additions & 0 deletions app/src/components/EditHeaders.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonIcon,
IonInput,
IonItem,
IonLabel,
IonList,
IonModal,
IonTitle,
IonToolbar,
useIonAlert,
} from "@ionic/react";

import type { IonModalCustomEvent } from "@ionic/core";
import { RefObject, useContext, useState } from "react";
import { StoreContext } from "../Store";
import { OverlayEventDetail } from "@ionic/react/dist/types/components/react-component-lib/interfaces";
import { useTranslation } from "react-i18next";
import { chainUpdate } from "../api";
import { refreshOutline } from "ionicons/icons";

export default function EditHeaders(props: {
initialHeader: string | null;
headerKey: string;
modal: RefObject<HTMLIonModalElement>;
didDismiss?: (e: IonModalCustomEvent<OverlayEventDetail<any>>) => void;
}) {
const { t } = useTranslation();
const [error, setError] = useState("");
const { chain, chainHeaders } = useContext(StoreContext);

const [presentAlert] = useIonAlert();

const [text, setText] = useState(props.initialHeader);

function modalInit() {
setText(props.initialHeader);
setError("");
}

function cancel() {
props.modal.current?.dismiss();
}

function handleSave() {
if (!chain?.uid) return;
const _headerKey = props.headerKey;
const newH = {
...chainHeaders,
[_headerKey]: text,
};

chainUpdate({
uid: chain.uid,
headers_override: JSON.stringify(newH),
});

props.modal.current?.dismiss("", "confirm");
}

function reset() {
if (!chain?.uid) return;
const handler = () => {
chainUpdate({
uid: chain.uid,
headers_override: "",
});
props.modal.current?.dismiss();
};

presentAlert({
header: t("resetHeaders"),
subHeader: t("areYouSureYouWantToResetHeaders"),
buttons: [
{
text: t("cancel"),
},
{
role: "destructive",
text: t("reset"),
handler,
},
],
});
}

return (
<IonModal
ref={props.modal}
onIonModalWillPresent={modalInit}
onIonModalDidDismiss={props.didDismiss}
initialBreakpoint={0.5}
breakpoints={[0, 0.5, 0.75, 1]}
>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonButton onClick={cancel}>{t("cancel")}</IonButton>
</IonButtons>
<IonTitle>{t("update")}</IonTitle>
<IonButtons slot="end">
<IonButton
onClick={handleSave}
color={!error ? "primary" : "danger"}
disabled={text?.length == 0}
>
{t("save")}
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonList>
{chain?.headers_override ? (
<IonItem
lines="full"
color="dark"
key="reset"
className="tw-bottom-0"
>
<IonIcon icon={refreshOutline} className="tw-ml-0.5 tw-mr-5" />
<IonLabel className="ion-text-wrap">
<h3>{t("restHeaders")}</h3>
<p>{t("resetHeadersDesc")}</p>
</IonLabel>
<IonButton slot="end" onClick={reset} color="danger">
{t("reset")}
</IonButton>
</IonItem>
) : null}
<IonItem
lines="none"
color={error === "number" ? "danger" : undefined}
>
{t("updateHeaderDesc")}
</IonItem>
<IonItem
lines="none"
color={error === "number" ? "danger" : undefined}
>
<IonInput
type="text"
label={t("Title")}
labelPlacement="start"
max={50}
spellCheck
autoCapitalize="words"
maxlength={50}
counter
value={text || ""}
onIonInput={(e) => setText(e.detail.value + "")}
/>
</IonItem>
</IonList>
</IonContent>
</IonModal>
);
}
39 changes: 39 additions & 0 deletions app/src/components/HeaderTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { IonIcon, IonTitle } from "@ionic/react";
import { construct } from "ionicons/icons";
import { useLongPress } from "use-long-press";

interface Props {
isChainAdmin: boolean;
className: string;
headerText: string;
onEdit: () => void;
}

export default function HeaderTitle({
className,
isChainAdmin,
headerText,
onEdit,
}: Props) {
const longPressHeader = useLongPress(onEdit);

if (isChainAdmin) {
return (
<IonTitle size="large" className={className} {...longPressHeader()}>
{headerText}

<IonIcon
icon={construct}
className="tw-text-[18px] tw-ml-1.5 !tw-text-blue tw-cursor-pointer"
onClick={onEdit}
/>
</IonTitle>
);
}

return (
<IonTitle size="large" className={className}>
{headerText}
</IonTitle>
);
}
Loading

0 comments on commit 5a84de9

Please sign in to comment.