Skip to content

Commit

Permalink
Add appearance page, add font size adjust (aeharding#54)
Browse files Browse the repository at this point in the history
* Add appearance page and customizable font size
* Add layout tweaks for large font size
  • Loading branch information
aeharding authored Jun 29, 2023
1 parent 591eef7 commit 7dfa3d6
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 19 deletions.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { AppContextProvider } from "./features/auth/AppContext";
import Router from "./Router";
import BeforeInstallPromptProvider from "./BeforeInstallPromptProvider";
import { UpdateContextProvider } from "./pages/settings/update/UpdateContext";
import GlobalStyles from "./GlobalStyles";

setupIonicReact({
rippleEffect: false,
Expand All @@ -39,6 +40,7 @@ export default function App() {
return (
<AppContextProvider>
<Provider store={store}>
<GlobalStyles />
<BeforeInstallPromptProvider>
<UpdateContextProvider>
<Router>
Expand Down
30 changes: 30 additions & 0 deletions src/GlobalStyles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Global, css } from "@emotion/react";
import { useAppSelector } from "./store";

export default function GlobalStyles() {
const { fontSizeMultiplier, useSystemFontSize } = useAppSelector(
(state) => state.appearance.font
);

const baseFontStyles = useSystemFontSize
? css`
font: -apple-system-body;
`
: css`
font-size: ${fontSizeMultiplier}rem;
`;

return (
<Global
styles={css`
html {
${baseFontStyles}
ion-content ion-item {
font-size: 1rem;
}
}
`}
/>
);
}
4 changes: 4 additions & 0 deletions src/TabbedRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import UpdateAppPage from "./pages/settings/UpdateAppPage";
import useShouldInstall from "./features/pwa/useShouldInstall";
import { UpdateContext } from "./pages/settings/update/UpdateContext";
import { LEMMY_SERVERS } from "./helpers/lemmy";
import AppearancePage from "./pages/settings/AppearancePage";

const Interceptor = styled.div`
position: absolute;
Expand Down Expand Up @@ -314,6 +315,9 @@ export default function TabbedRoutes() {
<Route exact path="/settings/update">
<UpdateAppPage />
</Route>
<Route exact path="/settings/appearance">
<AppearancePage />
</Route>
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton
Expand Down
16 changes: 8 additions & 8 deletions src/features/post/detail/PostDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const CenteredSpinner = styled(IonSpinner)`
`;

const Container = styled.div`
margin: 0 0 1rem;
margin: 0 0 16px;
width: 100%;
`;

Expand All @@ -71,7 +71,7 @@ const LightboxImg = styled(Img)`
`;

const StyledMarkdown = styled(Markdown)`
margin: 1rem 0;
margin: 16px 0;
img {
display: block;
Expand All @@ -83,11 +83,11 @@ const StyledMarkdown = styled(Markdown)`
`;

const StyledEmbed = styled(Embed)`
margin: 1rem 0;
margin: 16px 0;
`;

const PostDeets = styled.div`
margin: 0 1rem;
margin: 0 16px;
font-size: 0.9em;
h1,
Expand All @@ -102,12 +102,12 @@ const PostDeets = styled.div`

const Title = styled.div`
font-size: 1.3em;
padding: 1rem 0 0;
margin-bottom: 1rem;
padding: 16px 0 0;
margin-bottom: 16px;
`;

const By = styled.div`
margin-bottom: 0.3rem;
margin-bottom: 5px;
color: var(--ion-color-medium);
white-space: nowrap;
Expand All @@ -117,7 +117,7 @@ const By = styled.div`

export const AnnouncementIcon = styled(IonIcon)`
font-size: 1.1rem;
margin-right: 0.3rem;
margin-right: 5px;
vertical-align: middle;
color: var(--ion-color-success);
`;
Expand Down
2 changes: 2 additions & 0 deletions src/features/post/inFeed/Post.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const LeftDetails = styled.div`
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 0;
`;

const RightDetails = styled.div`
Expand Down
85 changes: 85 additions & 0 deletions src/features/settings/appearance/TextSize.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import styled from "@emotion/styled";
import { css } from "@emotion/react";
import { IonLabel, IonList, IonRange, IonToggle } from "@ionic/react";
import { InsetIonItem } from "../../../pages/profile/ProfileFeedItemsPage";
import { useAppDispatch, useAppSelector } from "../../../store";
import { setFontSizeMultiplier, setUseSystemFontSize } from "./appearanceSlice";

const ListHeader = styled.div`
font-size: 0.8em;
margin: 32px 0 -8px 32px;
text-transform: uppercase;
color: var(--ion-color-medium);
`;

const Range = styled(IonRange)`
--bar-background: var(--ion-color-medium);
::part(tick) {
background: var(--ion-color-medium);
}
`;

const A = styled.div<{ small?: boolean }>`
font-size: 1.3em;
padding: 0 0.5rem;
font-weight: 500;
${({ small }) =>
small &&
css`
font-size: 0.8em;
`}
`;

const HelperText = styled.div`
margin: 0 32px;
font-size: 0.9em;
color: var(--ion-color-medium);
`;

export default function TextSize() {
const dispatch = useAppDispatch();
const { fontSizeMultiplier, useSystemFontSize } = useAppSelector(
(state) => state.appearance.font
);

return (
<>
<ListHeader>
<IonLabel>Text size</IonLabel>
</ListHeader>
<IonList inset>
<InsetIonItem>
<IonLabel>Use System Text Size</IonLabel>
<IonToggle
checked={useSystemFontSize}
onIonChange={(e) =>
dispatch(setUseSystemFontSize(e.detail.checked))
}
/>
</InsetIonItem>
<InsetIonItem>
<Range
disabled={useSystemFontSize}
ticks
snaps
min={0.8}
max={1.6}
step={0.1}
value={fontSizeMultiplier}
onIonInput={(e) => {
dispatch(setFontSizeMultiplier(e.detail.value as number));
}}
>
<A slot="start" small>
A
</A>
<A slot="end">A</A>
</Range>
</InsetIonItem>
</IonList>
<HelperText>Default is two ticks from the left.</HelperText>
</>
);
}
55 changes: 55 additions & 0 deletions src/features/settings/appearance/appearanceSlice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { get, set } from "../storage";
import { merge } from "lodash";

const STORAGE_KEYS = {
FONT: {
FONT_SIZE_MULTIPLIER: "appearance--font-size-multiplier",
USE_SYSTEM: "appearance--font-use-system",
},
} as const;

interface AppearanceState {
font: {
fontSizeMultiplier: number;
useSystemFontSize: boolean;
};
}

const initialState: AppearanceState = {
font: {
fontSizeMultiplier: 1,
useSystemFontSize: false,
},
};

const stateFromStorage: AppearanceState = merge(initialState, {
font: {
fontSizeMultiplier: get(STORAGE_KEYS.FONT.FONT_SIZE_MULTIPLIER),
useSystemFontSize: get(STORAGE_KEYS.FONT.USE_SYSTEM),
},
});

export const appearanceSlice = createSlice({
name: "appearance",
initialState: stateFromStorage,
reducers: {
setFontSizeMultiplier(state, action: PayloadAction<number>) {
state.font.fontSizeMultiplier = action.payload;

set(STORAGE_KEYS.FONT.FONT_SIZE_MULTIPLIER, action.payload);
},
setUseSystemFontSize(state, action: PayloadAction<boolean>) {
state.font.useSystemFontSize = action.payload;

set(STORAGE_KEYS.FONT.USE_SYSTEM, action.payload);
},

resetAppearance: () => initialState,
},
});

export const { setFontSizeMultiplier, setUseSystemFontSize } =
appearanceSlice.actions;

export default appearanceSlice.reducer;
10 changes: 10 additions & 0 deletions src/features/settings/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function get(key: string): any {
const data = localStorage.getItem(key);
if (!data) return;
return JSON.parse(data);
}

export function set(key: string, value: unknown) {
localStorage.setItem(key, JSON.stringify(value));
}
2 changes: 1 addition & 1 deletion src/features/user/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const InsetIonItem = styled(IonItem)`
`;

export const SettingLabel = styled(IonLabel)`
margin-left: 1rem;
margin-left: 16px;
`;

interface ProfileProps {
Expand Down
6 changes: 0 additions & 6 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,3 @@ ion-modal.small {
ion-modal.small ion-header ion-toolbar:first-of-type {
padding-top: 0px;
}

/* TODO: Native font scaling */
/* (Need a configurable option first) */
/* body ion-content ion-item {
font: -apple-system-body;
} */
2 changes: 1 addition & 1 deletion src/pages/profile/ProfileFeedItemsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const InsetIonItem = styled(IonItem)`
`;

export const SettingLabel = styled(IonLabel)`
margin-left: 1rem;
margin-left: 16px;
`;

interface ProfileFeedItemsPageProps {
Expand Down
29 changes: 29 additions & 0 deletions src/pages/settings/AppearancePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
IonBackButton,
IonButtons,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
} from "@ionic/react";
import AppContent from "../../features/shared/AppContent";
import TextSize from "../../features/settings/appearance/TextSize";

export default function AppearancePage() {
return (
<IonPage className="grey-bg">
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton defaultHref="/settings" text="Settings" />
</IonButtons>

<IonTitle>Appearance</IonTitle>
</IonToolbar>
</IonHeader>
<AppContent scrollY>
<TextSize />
</AppContent>
</IonPage>
);
}
Loading

0 comments on commit 7dfa3d6

Please sign in to comment.