Skip to content

Commit

Permalink
Add vote theme (options: lemmy, reddit) (#1440)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeharding committed Apr 29, 2024
1 parent 8323625 commit 807a9d7
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 31 deletions.
7 changes: 7 additions & 0 deletions src/core/theme/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export const baseVariables = css`
--read-color-medium: rgba(0, 0, 0, 0.4);
--share-img-drop-shadow: none;
--ion-color-reddit-upvote: #ff5c01;
}
.ios body {
Expand All @@ -107,6 +109,9 @@ export const baseVariables = css`
.ion-color-primary-fixed {
--ion-color-base: var(--ion-color-primary-fixed);
}
.ion-color-reddit-upvote {
--ion-color-base: var(--ion-color-reddit-upvote);
}
}
`;

Expand Down Expand Up @@ -224,6 +229,8 @@ export const darkVariables = css`
--read-color-medium: rgba(255, 255, 255, 0.4);
--share-img-drop-shadow: drop-shadow(0 0 8px black);
--ion-color-reddit-upvote: #f26700;
}
// iOS Dark Theme
Expand Down
31 changes: 5 additions & 26 deletions src/features/labels/Vote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ import { ImpactStyle } from "@capacitor/haptics";
import useHapticFeedback from "../../helpers/useHapticFeedback";
import useAppToast from "../../helpers/useAppToast";
import { formatNumber } from "../../helpers/number";
import { styled } from "@linaria/react";
import { getVoteErrorMessage } from "../../helpers/lemmyErrors";
import { PlainButton } from "../shared/PlainButton";
import Stat from "../post/detail/Stat";
import { css } from "@linaria/core";
import VoteStat from "./VoteStat";

const iconClass = css`
// Vote icons are tall and narrow, but svg container is square.
Expand All @@ -29,26 +28,6 @@ const iconClass = css`
margin: 0 -2px;
`;

const VoteStat = styled(Stat)<{
vote?: 1 | -1 | 0;
voteRepresented?: 1 | -1;
}>`
&& {
color: ${({ vote, voteRepresented }) => {
if (voteRepresented === undefined || vote === voteRepresented) {
switch (vote) {
case 1:
return "var(--ion-color-primary-fixed)";
case -1:
return "var(--ion-color-danger)";
}
}
return "inherit";
}};
}
`;

interface VoteProps {
item: PostView | CommentView;
className?: string;
Expand Down Expand Up @@ -120,7 +99,7 @@ export default function Vote({
icon={arrowUpSharp}
className={className}
iconClassName={iconClass}
vote={myVote}
currentVote={myVote}
voteRepresented={1}
onClick={async (e) => {
await onVote(e, myVote === 1 ? 0 : 1);
Expand All @@ -133,7 +112,7 @@ export default function Vote({
icon={arrowDownSharp}
className={className}
iconClassName={iconClass}
vote={myVote}
currentVote={myVote}
voteRepresented={-1}
onClick={async (e) => {
await onVote(e, myVote === -1 ? 0 : -1);
Expand All @@ -151,7 +130,7 @@ export default function Vote({
icon={myVote === -1 ? arrowDownSharp : arrowUpSharp}
className={className}
iconClassName={iconClass}
vote={myVote}
currentVote={myVote}
onClick={async (e) => {
await onVote(e, myVote ? 0 : 1);
}}
Expand All @@ -166,7 +145,7 @@ export default function Vote({
icon={myVote === -1 ? arrowDownSharp : arrowUpSharp}
className={className}
iconClassName={iconClass}
vote={myVote}
currentVote={myVote}
onClick={async (e) => {
await onVote(e, myVote ? 0 : 1);
}}
Expand Down
37 changes: 37 additions & 0 deletions src/features/labels/VoteStat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ComponentProps, useMemo } from "react";
import { useAppSelector } from "../../store";
import Stat from "../post/detail/Stat";
import {
VOTE_COLORS,
bgColorToVariable,
} from "../settings/appearance/themes/votesTheme/VotesTheme";

interface VoteStatProps extends ComponentProps<typeof Stat> {
currentVote: 1 | -1 | 0;
voteRepresented?: 1 | -1 | 0;
}

export default function VoteStat({
currentVote,
voteRepresented,
...props
}: VoteStatProps) {
const votesTheme = useAppSelector(
(state) => state.settings.appearance.votesTheme,
);

const color = useMemo(() => {
if (voteRepresented === undefined || currentVote === voteRepresented) {
switch (currentVote) {
case 1:
return bgColorToVariable(VOTE_COLORS.UPVOTE[votesTheme]);
case -1:
return bgColorToVariable(VOTE_COLORS.DOWNVOTE[votesTheme]);
}
}

return "inherit";
}, [currentVote, voteRepresented, votesTheme]);

return <Stat {...props} style={{ color }} />;
}
13 changes: 10 additions & 3 deletions src/features/post/shared/VoteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import useHapticFeedback from "../../../helpers/useHapticFeedback";
import useAppToast from "../../../helpers/useAppToast";
import { styled } from "@linaria/react";
import { getVoteErrorMessage } from "../../../helpers/lemmyErrors";
import {
VOTE_COLORS,
bgColorToVariable,
} from "../../settings/appearance/themes/votesTheme/VotesTheme";

const InactiveItem = styled(ActionButton)`
${bounceAnimationOnTransition}
Expand All @@ -36,6 +40,9 @@ export function VoteButton({ type, postId }: VoteButtonProps) {
const vibrate = useHapticFeedback();
const { presentLoginIfNeeded } = useContext(PageContext);
const downvoteAllowed = useAppSelector(isDownvoteEnabledSelector);
const votesTheme = useAppSelector(
(state) => state.settings.appearance.votesTheme,
);

const postVotesById = useAppSelector((state) => state.post.postVotesById);
const myVote = postVotesById[postId];
Expand Down Expand Up @@ -64,10 +71,10 @@ export function VoteButton({ type, postId }: VoteButtonProps) {

const activeColor = (() => {
switch (type) {
case "down":
return "var(--ion-color-danger)";
case "up":
return "var(--ion-color-primary)";
return bgColorToVariable(VOTE_COLORS.UPVOTE[votesTheme]);
case "down":
return bgColorToVariable(VOTE_COLORS.DOWNVOTE[votesTheme]);
}
})();

Expand Down
2 changes: 2 additions & 0 deletions src/features/settings/appearance/themes/Theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import AppTheme from "./appTheme/AppTheme";
import CommentsTheme from "./commentsTheme/CommentsTheme";
import Dark from "./dark/Dark";
import System from "./system/System";
import VotesTheme from "./votesTheme/VotesTheme";

export default function Theme() {
return (
<>
<AppTheme />
<CommentsTheme />
<VotesTheme />
<System />
<Dark />
</>
Expand Down
102 changes: 102 additions & 0 deletions src/features/settings/appearance/themes/votesTheme/VotesTheme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
IonIcon,
IonItem,
IonLabel,
IonList,
IonRadio,
IonRadioGroup,
} from "@ionic/react";
import { ListHeader } from "../../../shared/formatting";
import { OVotesThemeType } from "../../../../../services/db";
import { styled } from "@linaria/react";
import { css } from "@linaria/core";
import { useAppDispatch, useAppSelector } from "../../../../../store";
import { startCase } from "lodash";
import { arrowDownOutline, arrowUpOutline } from "ionicons/icons";
import { setVotesTheme } from "../../../settingsSlice";

export const VOTE_COLORS = {
UPVOTE: {
[OVotesThemeType.Lemmy]: "primary-fixed",
[OVotesThemeType.Reddit]: "reddit-upvote",
},
DOWNVOTE: {
[OVotesThemeType.Lemmy]: "danger",
[OVotesThemeType.Reddit]: "primary-fixed",
},
};

export function bgColorToVariable(bgColor: string): string {
return `var(--ion-color-${bgColor})`;
}

const VotesContainer = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
`;

const Votes = styled.div`
display: flex;
gap: 6px;
margin: 0 6px;
`;

const Vote = styled.div<{ bgColor: string }>`
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
border-radius: 4px;
background: ${({ bgColor }) => bgColorToVariable(bgColor)};
color: white;
`;

export default function VotesTheme() {
const dispatch = useAppDispatch();
const votesTheme = useAppSelector(
(state) => state.settings.appearance.votesTheme,
);

return (
<>
<ListHeader>
<IonLabel>Votes Theme</IonLabel>
</ListHeader>
<IonRadioGroup
value={votesTheme}
onIonChange={(e) => {
dispatch(setVotesTheme(e.detail.value));
}}
>
<IonList inset>
{Object.entries(OVotesThemeType).map(([label, value]) => (
<IonItem key={value}>
<IonRadio
value={value}
className={css`
&::part(label) {
flex: 1;
}
`}
>
<VotesContainer>
<IonLabel>{startCase(label)}</IonLabel>
<Votes>
<Vote bgColor={VOTE_COLORS.UPVOTE[value]}>
<IonIcon icon={arrowUpOutline} />
</Vote>
<Vote bgColor={VOTE_COLORS.DOWNVOTE[value]}>
<IonIcon icon={arrowDownOutline} />
</Vote>
</Votes>
</VotesContainer>
</IonRadio>
</IonItem>
))}
</IonList>
</IonRadioGroup>
</>
);
}
10 changes: 10 additions & 0 deletions src/features/settings/settingsSlice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
AutoplayMediaType,
OAutoplayMediaType,
CommentsThemeType,
VotesThemeType,
} from "../../services/db";
import { get, set } from "./storage";
import { Mode } from "@ionic/core";
Expand Down Expand Up @@ -95,6 +96,7 @@ interface SettingsState {
deviceMode: Mode;
theme: AppThemeType;
commentsTheme: CommentsThemeType;
votesTheme: VotesThemeType;
};
general: {
comments: {
Expand Down Expand Up @@ -189,6 +191,7 @@ export const initialState: SettingsState = {
deviceMode: "ios",
theme: "default",
commentsTheme: "rainbow",
votesTheme: "lemmy",
},
general: {
comments: {
Expand Down Expand Up @@ -492,6 +495,10 @@ export const appearanceSlice = createSlice({
state.appearance.commentsTheme = action.payload;
db.setSetting("comments_theme", action.payload);
},
setVotesTheme(state, action: PayloadAction<VotesThemeType>) {
state.appearance.votesTheme = action.payload;
db.setSetting("votes_theme", action.payload);
},
setEnableHapticFeedback(state, action: PayloadAction<boolean>) {
state.general.enableHapticFeedback = action.payload;

Expand Down Expand Up @@ -609,6 +616,7 @@ export const fetchSettingsFromDatabase = createAsyncThunk<SettingsState>(
const result = db.transaction("r", db.settings, async () => {
const state = thunkApi.getState() as RootState;
const comments_theme = await db.getSetting("comments_theme");
const votes_theme = await db.getSetting("votes_theme");
const collapse_comment_threads = await db.getSetting(
"collapse_comment_threads",
);
Expand Down Expand Up @@ -693,6 +701,7 @@ export const fetchSettingsFromDatabase = createAsyncThunk<SettingsState>(
...state.settings.appearance,
commentsTheme:
comments_theme ?? initialState.appearance.commentsTheme,
votesTheme: votes_theme ?? initialState.appearance.votesTheme,
general: {
userInstanceUrlDisplay:
user_instance_url_display ??
Expand Down Expand Up @@ -836,6 +845,7 @@ export const fetchSettingsFromDatabase = createAsyncThunk<SettingsState>(

export const {
setCommentsTheme,
setVotesTheme,
setDatabaseError,
setFontSizeMultiplier,
setUseSystemFontSize,
Expand Down
10 changes: 8 additions & 2 deletions src/features/shared/sliding/BaseSlidingVote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { AppContext } from "../../auth/AppContext";
import { getCanModerate } from "../../moderation/useCanModerate";
import { isStubComment } from "../../comment/CommentHeader";
import { getVoteErrorMessage } from "../../../helpers/lemmyErrors";
import { VOTE_COLORS } from "../../settings/appearance/themes/votesTheme/VotesTheme";

const StyledItemContainer = styled.div`
--ion-item-border-color: transparent;
Expand Down Expand Up @@ -111,6 +112,10 @@ function BaseSlidingVoteInternal({
const presentToast = useAppToast();
const dispatch = useAppDispatch();

const votesTheme = useAppSelector(
(state) => state.settings.appearance.votesTheme,
);

const postVotesById = useAppSelector((state) => state.post.postVotesById);
const commentVotesById = useAppSelector(
(state) => state.comment.commentVotesById,
Expand Down Expand Up @@ -315,15 +320,15 @@ function BaseSlidingVoteInternal({
trigger: () => {
onVote(currentVote === 1 ? 0 : 1);
},
bgColor: "primary-fixed",
bgColor: VOTE_COLORS.UPVOTE[votesTheme],
slash: currentVote === 1,
},
downvote: {
icon: arrowDownSharp,
trigger: () => {
onVote(currentVote === -1 ? 0 : -1);
},
bgColor: "danger",
bgColor: VOTE_COLORS.DOWNVOTE[votesTheme],
slash: currentVote === -1,
},
reply: {
Expand Down Expand Up @@ -352,6 +357,7 @@ function BaseSlidingVoteInternal({
markUnreadAction,
onVote,
shareTrigger,
votesTheme,
]);

const startActions: ActionList = useMemo(
Expand Down
Loading

0 comments on commit 807a9d7

Please sign in to comment.