Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vote theme (options: lemmy, reddit) #1440

Merged
merged 1 commit into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading