Skip to content

Commit

Permalink
Add custom gallery actions (copy, save image etc) (aeharding#625)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeharding authored Aug 9, 2023
1 parent d50ab6f commit d8980a5
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 5 deletions.
1 change: 1 addition & 0 deletions android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation project(':capacitor-share')
implementation project(':capacitor-status-bar')
implementation project(':capacitor-application-context')
implementation project(':capacitor-stash-media')

}

Expand Down
3 changes: 3 additions & 0 deletions android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ project(':capacitor-status-bar').projectDir = new File('../node_modules/.pnpm/@c

include ':capacitor-application-context'
project(':capacitor-application-context').projectDir = new File('../node_modules/.pnpm/capacitor-application-context@0.0.1_@capacitor+core@5.2.2/node_modules/capacitor-application-context/android')

include ':capacitor-stash-media'
project(':capacitor-stash-media').projectDir = new File('../node_modules/.pnpm/capacitor-stash-media@0.0.1_@capacitor+core@5.2.2/node_modules/capacitor-stash-media/android')
1 change: 1 addition & 0 deletions ios/App/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def capacitor_pods
pod 'CapacitorShare', :path => '../../node_modules/.pnpm/@capacitor+share@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/share'
pod 'CapacitorStatusBar', :path => '../../node_modules/.pnpm/@capacitor+status-bar@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/status-bar'
pod 'CapacitorApplicationContext', :path => '../../node_modules/.pnpm/capacitor-application-context@0.0.1_@capacitor+core@5.2.2/node_modules/capacitor-application-context'
pod 'CapacitorStashMedia', :path => '../../node_modules/.pnpm/capacitor-stash-media@0.0.1_@capacitor+core@5.2.2/node_modules/capacitor-stash-media'
end

target 'App' do
Expand Down
8 changes: 7 additions & 1 deletion ios/App/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ PODS:
- Capacitor
- CapacitorShare (5.0.6):
- Capacitor
- CapacitorStashMedia (0.0.1):
- Capacitor
- CapacitorStatusBar (5.0.6):
- Capacitor

Expand All @@ -26,6 +28,7 @@ DEPENDENCIES:
- "CapacitorHaptics (from `../../node_modules/.pnpm/@capacitor+haptics@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/haptics`)"
- "CapacitorKeyboard (from `../../node_modules/.pnpm/@capacitor+keyboard@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/keyboard`)"
- "CapacitorShare (from `../../node_modules/.pnpm/@capacitor+share@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/share`)"
- "CapacitorStashMedia (from `../../node_modules/.pnpm/capacitor-stash-media@0.0.1_@capacitor+core@5.2.2/node_modules/capacitor-stash-media`)"
- "CapacitorStatusBar (from `../../node_modules/.pnpm/@capacitor+status-bar@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/status-bar`)"

EXTERNAL SOURCES:
Expand All @@ -45,6 +48,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/.pnpm/@capacitor+keyboard@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/keyboard"
CapacitorShare:
:path: "../../node_modules/.pnpm/@capacitor+share@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/share"
CapacitorStashMedia:
:path: "../../node_modules/.pnpm/capacitor-stash-media@0.0.1_@capacitor+core@5.2.2/node_modules/capacitor-stash-media"
CapacitorStatusBar:
:path: "../../node_modules/.pnpm/@capacitor+status-bar@5.0.6_@capacitor+core@5.2.2/node_modules/@capacitor/status-bar"

Expand All @@ -57,8 +62,9 @@ SPEC CHECKSUMS:
CapacitorHaptics: 1fffc1217c7e64a472d7845be50fb0c2f7d4204c
CapacitorKeyboard: b978154b024a5f65e044908e37d15b7de58b9d12
CapacitorShare: cd41743331cb71d217c029de54b681cbd91e0fcc
CapacitorStashMedia: 891f0d023ef8328c8ab3ac57754ec959e4aff330
CapacitorStatusBar: 565c0a1ebd79bb40d797606a8992b4a105885309

PODFILE CHECKSUM: 707b8fe7a8e4e2291562870477bab59c5be7490c
PODFILE CHECKSUM: e5388331b477f578b2536347b4d6947301e2edd6

COCOAPODS: 1.12.1
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@vitejs/plugin-react": "^4.0.1",
"capacitor-application-context": "^0.0.1",
"capacitor-set-version": "^2.0.13",
"capacitor-stash-media": "^0.0.1",
"date-fns": "^2.30.0",
"dexie": "^3.2.4",
"dexie-react-hooks": "^1.1.6",
Expand Down
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

170 changes: 170 additions & 0 deletions src/features/gallery/GalleryMoreActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import {
IonIcon,
useIonActionSheet,
useIonRouter,
useIonToast,
} from "@ionic/react";
import {
bookmarkOutline,
copyOutline,
downloadOutline,
earthOutline,
ellipsisHorizontal,
peopleOutline,
personOutline,
shareOutline,
} from "ionicons/icons";
import { useBuildGeneralBrowseLink } from "../../helpers/routes";
import { getHandle } from "../../helpers/lemmy";
import { PostView } from "lemmy-js-client";
import { PageContext } from "../auth/PageContext";
import { useContext } from "react";
import { useAppDispatch, useAppSelector } from "../../store";
import { savePost } from "../post/postSlice";
import { saveError } from "../../helpers/toastMessages";
import { Browser } from "@capacitor/browser";
import { Share } from "@capacitor/share";
import { ActionButton } from "../post/actions/ActionButton";
import { StashMedia } from "capacitor-stash-media";

interface GalleryMoreActionsProps {
post: PostView;
imgSrc: string;
}

export default function GalleryMoreActions({
post,
imgSrc,
}: GalleryMoreActionsProps) {
const router = useIonRouter();
const [presentActionSheet] = useIonActionSheet();
const buildGeneralBrowseLink = useBuildGeneralBrowseLink();

const { presentLoginIfNeeded } = useContext(PageContext);
const [presentToast] = useIonToast();
const dispatch = useAppDispatch();

const postSavedById = useAppSelector((state) => state.post.postSavedById);
const mySaved = postSavedById[post.post.id] ?? post.saved;

function openActions() {
presentActionSheet({
cssClass: "left-align-buttons",
buttons: [
{
text: "Share",
icon: shareOutline,
handler: () => {
Share.share({ url: imgSrc });
},
},
{
text: "Save Image",
icon: downloadOutline,
handler: () => {
(async () => {
try {
await StashMedia.savePhoto({ url: imgSrc });
} catch (error) {
presentToast({
message: "Error saving photo to device",
duration: 3500,
position: "bottom",
color: "danger",
});

throw error;
}

presentToast({
message: "Photo saved",
duration: 3500,
position: "bottom",
color: "success",
});
})();
},
},
{
text: "Copy Image",
icon: copyOutline,
handler: () => {
(async () => {
try {
await StashMedia.copyPhotoToClipboard({ url: imgSrc });
} catch (error) {
presentToast({
message: "Error copying photo to clipboard",
duration: 3500,
position: "bottom",
color: "danger",
});

throw error;
}

presentToast({
message: "Photo copied to clipboard",
duration: 3500,
position: "bottom",
color: "success",
});
})();
},
},
{
text: "Open in Browser",
icon: earthOutline,
handler: () => {
Browser.open({ url: post.post.ap_id });
},
},
{
text: "Save",
icon: bookmarkOutline,
handler: () => {
(async () => {
if (presentLoginIfNeeded()) return;

try {
await dispatch(savePost(post.post.id, !mySaved));
} catch (error) {
presentToast(saveError);

throw error;
}
})();
},
},
{
text: getHandle(post.creator),
icon: personOutline,
handler: () => {
router.push(
buildGeneralBrowseLink(`/u/${getHandle(post.creator)}`)
);
},
},
{
text: getHandle(post.community),
icon: peopleOutline,
handler: () => {
router.push(
buildGeneralBrowseLink(`/c/${getHandle(post.community)}`)
);
},
},
{
text: "Cancel",
role: "cancel",
},
],
});
}

return (
<ActionButton onClick={openActions}>
<IonIcon icon={ellipsisHorizontal} />
</ActionButton>
);
}
16 changes: 13 additions & 3 deletions src/features/gallery/GalleryPostActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { useLocation } from "react-router";
import React, { useContext } from "react";
import { GalleryContext } from "./GalleryProvider";
import { OVoteDisplayMode } from "../../services/db";
import { isNative } from "../../helpers/device";
import GalleryMoreActions from "./GalleryMoreActions";

const Container = styled.div`
display: flex;
Expand All @@ -38,9 +40,13 @@ const Amount = styled.div`

interface GalleryPostActionsProps {
post: PostView;
imgSrc: string;
}

export default function GalleryPostActions({ post }: GalleryPostActionsProps) {
export default function GalleryPostActions({
post,
imgSrc,
}: GalleryPostActionsProps) {
const buildGeneralBrowseLink = useBuildGeneralBrowseLink();
const link = buildGeneralBrowseLink(
`/c/${getHandle(post.community)}/comments/${post.post.id}`
Expand All @@ -51,7 +57,7 @@ export default function GalleryPostActions({ post }: GalleryPostActionsProps) {

return (
<Container onClick={(e) => e.stopPropagation()}>
<Voting post={post} />
<Voting post={post} imgSrc={imgSrc} />
<div
onClick={() => {
close();
Expand All @@ -67,7 +73,11 @@ export default function GalleryPostActions({ post }: GalleryPostActionsProps) {
</Section>
</div>
<IonIcon icon={shareOutline} onClick={() => share(post.post)} />
<MoreActions post={post} onFeed />
{isNative() ? (
<GalleryMoreActions post={post} imgSrc={imgSrc} />
) : (
<MoreActions post={post} onFeed />
)}
</Container>
);
}
Expand Down
6 changes: 5 additions & 1 deletion src/features/gallery/GalleryProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export default function GalleryProvider({ children }: GalleryProviderProps) {
const [actionContainer, setActionContainer] = useState<HTMLElement | null>(
null
);
const imgRef = useRef<HTMLImageElement>();
const [post, setPost] = useState<PostView>();
const lightboxRef = useRef<PhotoSwipeLightbox | null>(null);
const location = useLocation();
Expand Down Expand Up @@ -90,6 +91,7 @@ export default function GalleryProvider({ children }: GalleryProviderProps) {
) => {
if (lightboxRef.current) return;

imgRef.current = img;
setPost(post);

const instance = new PhotoSwipeLightbox({
Expand Down Expand Up @@ -243,7 +245,9 @@ export default function GalleryProvider({ children }: GalleryProviderProps) {
post &&
createPortal(
<Container>
<GalleryPostActions post={post} />
{imgRef.current && (
<GalleryPostActions post={post} imgSrc={imgRef.current.src} />
)}
</Container>,
actionContainer
)}
Expand Down

0 comments on commit d8980a5

Please sign in to comment.