From 7e605e5cda661e2dcd491bca7f4f90c79872eeb8 Mon Sep 17 00:00:00 2001 From: Athou Date: Fri, 19 Aug 2022 12:41:33 +0200 Subject: [PATCH] add sharing buttons --- commafeed-client/src/app/constants.ts | 65 ++++++++++++++++++- commafeed-client/src/app/slices/user.ts | 22 ++++++- commafeed-client/src/app/types.ts | 6 +- .../components/content/FeedEntryFooter.tsx | 24 ++++++- .../src/components/content/ShareButtons.tsx | 55 ++++++++++++++++ .../components/settings/DisplaySettings.tsx | 20 +++++- commafeed-client/src/locales/en/messages.po | 8 +++ commafeed-client/src/locales/fr/messages.po | 8 +++ .../commafeed/frontend/model/Settings.java | 39 ++++++----- .../commafeed/frontend/resource/UserREST.java | 48 +++++++------- 10 files changed, 245 insertions(+), 50 deletions(-) create mode 100644 commafeed-client/src/components/content/ShareButtons.tsx diff --git a/commafeed-client/src/app/constants.ts b/commafeed-client/src/app/constants.ts index 340d51d9..0f2f11c2 100644 --- a/commafeed-client/src/app/constants.ts +++ b/commafeed-client/src/app/constants.ts @@ -1,6 +1,9 @@ import { t } from "@lingui/macro" import { DEFAULT_THEME } from "@mantine/core" -import { Category } from "./types" +import { IconType } from "react-icons" +import { FaAt } from "react-icons/fa" +import { SiBuffer, SiFacebook, SiGmail, SiInstapaper, SiPocket, SiTumblr, SiTwitter } from "react-icons/si" +import { Category, SharingSettings } from "./types" const categories: { [key: string]: Category } = { all: { @@ -20,8 +23,68 @@ const categories: { [key: string]: Category } = { position: 1, }, } + +const sharing: { + [key in keyof SharingSettings]: { + label: string + icon: IconType + color: `#${string}` + url: (url: string, description: string) => string + } +} = { + email: { + label: "Email", + icon: FaAt, + color: "#000000", + url: (url, desc) => `mailto:?subject=${desc}&body=${url}`, + }, + gmail: { + label: "Gmail", + icon: SiGmail, + color: "#EA4335", + url: (url, desc) => `https://mail.google.com/mail/?view=cm&fs=1&tf=1&source=mailto&su=${desc}&body=${url}`, + }, + facebook: { + label: "Facebook", + icon: SiFacebook, + color: "#1B74E4", + url: url => `https://www.facebook.com/sharer/sharer.php?u=${url}`, + }, + twitter: { + label: "Twitter", + icon: SiTwitter, + color: "#1D9BF0", + url: (url, desc) => `http://twitter.com/share?text=${desc}&url=${url}`, + }, + tumblr: { + label: "Tumblr", + icon: SiTumblr, + color: "#375672", + url: (url, desc) => `http://www.tumblr.com/share/link?url=${url}&name=${desc}`, + }, + pocket: { + label: "Pocket", + icon: SiPocket, + color: "#EF4154", + url: (url, desc) => `https://getpocket.com/save?url=${url}&title=${desc}`, + }, + instapaper: { + label: "Instapaper", + icon: SiInstapaper, + color: "#010101", + url: (url, desc) => `https://www.instapaper.com/hello2?url=${url}&title=${desc}`, + }, + buffer: { + label: "Buffer", + icon: SiBuffer, + color: "#000000", + url: (url, desc) => `https://bufferapp.com/add?url=${url}&text=${desc}`, + }, +} + export const Constants = { categories, + sharing, layout: { mobileBreakpoint: DEFAULT_THEME.breakpoints.md, headerHeight: 60, diff --git a/commafeed-client/src/app/slices/user.ts b/commafeed-client/src/app/slices/user.ts index 7539d633..d8b04ee7 100644 --- a/commafeed-client/src/app/slices/user.ts +++ b/commafeed-client/src/app/slices/user.ts @@ -3,7 +3,7 @@ import { showNotification } from "@mantine/notifications" import { createAsyncThunk, createSlice, isAnyOf } from "@reduxjs/toolkit" import { client } from "app/client" import { RootState } from "app/store" -import { ReadingMode, ReadingOrder, Settings, UserModel } from "app/types" +import { ReadingMode, ReadingOrder, Settings, SharingSettings, UserModel } from "app/types" import { reloadEntries } from "./entries" interface UserState { @@ -43,6 +43,20 @@ export const changeScrollSpeed = createAsyncThunk( + "settings/sharingSetting", + (sharingSetting, thunkApi) => { + const { settings } = thunkApi.getState().user + if (!settings) return + client.user.saveSettings({ + ...settings, + sharingSettings: { + ...settings.sharingSettings, + [sharingSetting.site]: sharingSetting.value, + }, + }) + } +) export const userSlice = createSlice({ name: "user", @@ -71,7 +85,11 @@ export const userSlice = createSlice({ if (!state.settings) return state.settings.scrollSpeed = action.meta.arg ? 400 : 0 }) - builder.addMatcher(isAnyOf(changeLanguage.fulfilled, changeScrollSpeed.fulfilled), () => { + builder.addCase(changeSharingSetting.pending, (state, action) => { + if (!state.settings) return + state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value + }) + builder.addMatcher(isAnyOf(changeLanguage.fulfilled, changeScrollSpeed.fulfilled, changeSharingSetting.fulfilled), () => { showNotification({ message: t`Settings saved.`, color: "green", diff --git a/commafeed-client/src/app/types.ts b/commafeed-client/src/app/types.ts index 6683b4ce..9471051f 100644 --- a/commafeed-client/src/app/types.ts +++ b/commafeed-client/src/app/types.ts @@ -233,16 +233,18 @@ export interface Settings { theme?: string customCss?: string scrollSpeed: number + sharingSettings: SharingSettings +} + +export interface SharingSettings { email: boolean gmail: boolean facebook: boolean twitter: boolean - googleplus: boolean tumblr: boolean pocket: boolean instapaper: boolean buffer: boolean - readability: boolean } export interface StarRequest { diff --git a/commafeed-client/src/components/content/FeedEntryFooter.tsx b/commafeed-client/src/components/content/FeedEntryFooter.tsx index 79ae2c85..d0f29416 100644 --- a/commafeed-client/src/components/content/FeedEntryFooter.tsx +++ b/commafeed-client/src/components/content/FeedEntryFooter.tsx @@ -1,17 +1,23 @@ import { t } from "@lingui/macro" -import { Checkbox, Group } from "@mantine/core" +import { Checkbox, Group, Popover } from "@mantine/core" import { markEntry, starEntry } from "app/slices/entries" -import { useAppDispatch } from "app/store" +import { useAppDispatch, useAppSelector } from "app/store" import { Entry } from "app/types" import { ActionButton } from "components/ActionButtton" -import { TbExternalLink, TbStar, TbStarOff } from "react-icons/tb" +import { TbExternalLink, TbShare, TbStar, TbStarOff } from "react-icons/tb" +import { ShareButtons } from "./ShareButtons" interface FeedEntryFooterProps { entry: Entry } export function FeedEntryFooter(props: FeedEntryFooterProps) { + const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings) const dispatch = useAppDispatch() + + const showSharingButtons = + sharingSettings && (Object.values(sharingSettings) as Array).some(v => v) + const readStatusCheckboxClicked = () => dispatch(markEntry({ entry: props.entry, read: !props.entry.read })) return ( @@ -32,6 +38,18 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) { label={props.entry.starred ? t`Unstar` : t`Star`} onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))} /> + + {showSharingButtons && ( + + + } label={t`Share`} /> + + + + + + )} + } label={t`Open link`} /> diff --git a/commafeed-client/src/components/content/ShareButtons.tsx b/commafeed-client/src/components/content/ShareButtons.tsx new file mode 100644 index 00000000..b551e8ba --- /dev/null +++ b/commafeed-client/src/components/content/ShareButtons.tsx @@ -0,0 +1,55 @@ +import { ActionIcon, Box, createStyles, SimpleGrid } from "@mantine/core" +import { Constants } from "app/constants" +import { useAppSelector } from "app/store" +import { SharingSettings } from "app/types" +import { IconType } from "react-icons" + +type Color = `#${string}` + +const useStyles = createStyles((theme, props: { color: Color }) => ({ + socialIcon: { + color: props.color, + backgroundColor: theme.colorScheme === "dark" ? theme.colors.gray[2] : "white", + borderRadius: "50%", + }, +})) + +function ShareButton({ url, icon, color }: { url: string; icon: IconType; color: Color }) { + const { classes } = useStyles({ color }) + + const onClick = (e: React.MouseEvent) => { + e.preventDefault() + window.open(url, "", "menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=800,height=600") + } + + return ( + + + + {icon({ size: 18 })} + + + + ) +} + +export function ShareButtons(props: { url: string; description: string }) { + const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings) + const url = encodeURIComponent(props.url) + const desc = encodeURIComponent(props.description) + + return ( + + {(Object.keys(Constants.sharing) as Array) + .filter(site => sharingSettings && sharingSettings[site]) + .map(site => ( + + ))} + + ) +} diff --git a/commafeed-client/src/components/settings/DisplaySettings.tsx b/commafeed-client/src/components/settings/DisplaySettings.tsx index 701403b5..37cb7c56 100644 --- a/commafeed-client/src/components/settings/DisplaySettings.tsx +++ b/commafeed-client/src/components/settings/DisplaySettings.tsx @@ -1,12 +1,15 @@ import { t } from "@lingui/macro" -import { Select, Stack, Switch } from "@mantine/core" -import { changeLanguage, changeScrollSpeed } from "app/slices/user" +import { Divider, Select, SimpleGrid, Stack, Switch } from "@mantine/core" +import { Constants } from "app/constants" +import { changeLanguage, changeScrollSpeed, changeSharingSetting } from "app/slices/user" import { useAppDispatch, useAppSelector } from "app/store" +import { SharingSettings } from "app/types" import { locales } from "i18n" export function DisplaySettings() { const language = useAppSelector(state => state.user.settings?.language) const scrollSpeed = useAppSelector(state => state.user.settings?.scrollSpeed) + const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings) const dispatch = useAppDispatch() return ( @@ -26,6 +29,19 @@ export function DisplaySettings() { checked={scrollSpeed ? scrollSpeed > 0 : false} onChange={e => dispatch(changeScrollSpeed(e.currentTarget.checked))} /> + + + + + {(Object.keys(Constants.sharing) as Array).map(site => ( + dispatch(changeSharingSetting({ site, value: e.currentTarget.checked }))} + /> + ))} + ) } diff --git a/commafeed-client/src/locales/en/messages.po b/commafeed-client/src/locales/en/messages.po index 413bc9ec..efd6c1bf 100644 --- a/commafeed-client/src/locales/en/messages.po +++ b/commafeed-client/src/locales/en/messages.po @@ -564,6 +564,14 @@ msgstr "Settings" msgid "Settings saved." msgstr "Settings saved." +#: src/components/content/FeedEntryFooter.tsx +msgid "Share" +msgstr "Share" + +#: src/components/settings/DisplaySettings.tsx +msgid "Sharing sites" +msgstr "Sharing sites" + #: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx msgid "Shift" diff --git a/commafeed-client/src/locales/fr/messages.po b/commafeed-client/src/locales/fr/messages.po index 53a6adeb..70003f80 100644 --- a/commafeed-client/src/locales/fr/messages.po +++ b/commafeed-client/src/locales/fr/messages.po @@ -564,6 +564,14 @@ msgstr "Réglages" msgid "Settings saved." msgstr "Réglages enregistrés." +#: src/components/content/FeedEntryFooter.tsx +msgid "Share" +msgstr "Partager" + +#: src/components/settings/DisplaySettings.tsx +msgid "Sharing sites" +msgstr "Sites de partage" + #: src/components/KeyboardShortcutsHelp.tsx #: src/components/KeyboardShortcutsHelp.tsx msgid "Shift" diff --git a/commafeed-server/src/main/java/com/commafeed/frontend/model/Settings.java b/commafeed-server/src/main/java/com/commafeed/frontend/model/Settings.java index 85ec2722..b416455f 100644 --- a/commafeed-server/src/main/java/com/commafeed/frontend/model/Settings.java +++ b/commafeed-server/src/main/java/com/commafeed/frontend/model/Settings.java @@ -38,27 +38,34 @@ public class Settings implements Serializable { @ApiModelProperty(value = "user's preferred scroll speed when navigating between entries", required = true) private int scrollSpeed; - @ApiModelProperty(required = true) - private boolean email; + @ApiModelProperty(value = "sharing settings", required = true) + private SharingSettings sharingSettings = new SharingSettings(); - @ApiModelProperty(required = true) - private boolean gmail; + @ApiModel(description = "User sharing settings") + @Data + public static class SharingSettings implements Serializable { + @ApiModelProperty(required = true) + private boolean email; - @ApiModelProperty(required = true) - private boolean facebook; + @ApiModelProperty(required = true) + private boolean gmail; - @ApiModelProperty(required = true) - private boolean twitter; + @ApiModelProperty(required = true) + private boolean facebook; - @ApiModelProperty(required = true) - private boolean tumblr; + @ApiModelProperty(required = true) + private boolean twitter; - @ApiModelProperty(required = true) - private boolean pocket; + @ApiModelProperty(required = true) + private boolean tumblr; - @ApiModelProperty(required = true) - private boolean instapaper; + @ApiModelProperty(required = true) + private boolean pocket; - @ApiModelProperty(required = true) - private boolean buffer; + @ApiModelProperty(required = true) + private boolean instapaper; + + @ApiModelProperty(required = true) + private boolean buffer; + } } diff --git a/commafeed-server/src/main/java/com/commafeed/frontend/resource/UserREST.java b/commafeed-server/src/main/java/com/commafeed/frontend/resource/UserREST.java index ec09d7cf..e8a1ea20 100644 --- a/commafeed-server/src/main/java/com/commafeed/frontend/resource/UserREST.java +++ b/commafeed-server/src/main/java/com/commafeed/frontend/resource/UserREST.java @@ -91,14 +91,14 @@ public class UserREST { s.setViewMode(settings.getViewMode().name()); s.setShowRead(settings.isShowRead()); - s.setEmail(settings.isEmail()); - s.setGmail(settings.isGmail()); - s.setFacebook(settings.isFacebook()); - s.setTwitter(settings.isTwitter()); - s.setTumblr(settings.isTumblr()); - s.setPocket(settings.isPocket()); - s.setInstapaper(settings.isInstapaper()); - s.setBuffer(settings.isBuffer()); + s.getSharingSettings().setEmail(settings.isEmail()); + s.getSharingSettings().setGmail(settings.isGmail()); + s.getSharingSettings().setFacebook(settings.isFacebook()); + s.getSharingSettings().setTwitter(settings.isTwitter()); + s.getSharingSettings().setTumblr(settings.isTumblr()); + s.getSharingSettings().setPocket(settings.isPocket()); + s.getSharingSettings().setInstapaper(settings.isInstapaper()); + s.getSharingSettings().setBuffer(settings.isBuffer()); s.setScrollMarks(settings.isScrollMarks()); s.setTheme(settings.getTheme()); @@ -112,14 +112,14 @@ public class UserREST { s.setShowRead(true); s.setTheme("default"); - s.setEmail(true); - s.setGmail(true); - s.setFacebook(true); - s.setTwitter(true); - s.setTumblr(true); - s.setPocket(true); - s.setInstapaper(true); - s.setBuffer(true); + s.getSharingSettings().setEmail(true); + s.getSharingSettings().setGmail(true); + s.getSharingSettings().setFacebook(true); + s.getSharingSettings().setTwitter(true); + s.getSharingSettings().setTumblr(true); + s.getSharingSettings().setPocket(true); + s.getSharingSettings().setInstapaper(true); + s.getSharingSettings().setBuffer(true); s.setScrollMarks(true); s.setLanguage("en"); @@ -151,14 +151,14 @@ public class UserREST { s.setLanguage(settings.getLanguage()); s.setScrollSpeed(settings.getScrollSpeed()); - s.setEmail(settings.isEmail()); - s.setGmail(settings.isGmail()); - s.setFacebook(settings.isFacebook()); - s.setTwitter(settings.isTwitter()); - s.setTumblr(settings.isTumblr()); - s.setPocket(settings.isPocket()); - s.setInstapaper(settings.isInstapaper()); - s.setBuffer(settings.isBuffer()); + s.setEmail(settings.getSharingSettings().isEmail()); + s.setGmail(settings.getSharingSettings().isGmail()); + s.setFacebook(settings.getSharingSettings().isFacebook()); + s.setTwitter(settings.getSharingSettings().isTwitter()); + s.setTumblr(settings.getSharingSettings().isTumblr()); + s.setPocket(settings.getSharingSettings().isPocket()); + s.setInstapaper(settings.getSharingSettings().isInstapaper()); + s.setBuffer(settings.getSharingSettings().isBuffer()); userSettingsDAO.saveOrUpdate(s); return Response.ok().build();