Skip to content

Commit 471df14

Browse files
chore(example-app): Fix Typescript build issues and better support for Media Result
1 parent f6eaab7 commit 471df14

13 files changed

Lines changed: 290 additions & 220 deletions

example-app/src/components/camera/ChooseFromGalleryConfigurable.tsx

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import React from "react";
1212
import {
1313
Camera,
14-
GalleryPhoto,
14+
MediaResult,
1515
MediaType,
1616
} from "@capacitor/camera";
1717

@@ -24,7 +24,7 @@ interface ChooseFromGalleryConfig {
2424

2525
interface ChooseFromGalleryConfigurableProps {
2626
buttonLabel?: string;
27-
onMediaResult: (media: GalleryPhoto[]) => void;
27+
onMediaResult: (results: MediaResult[]) => void;
2828
}
2929

3030
interface ChooseFromGalleryConfigurableState {
@@ -64,15 +64,7 @@ class ChooseFromGalleryConfigurable extends React.Component<
6464
});
6565
console.log("chooseFromGallery result", result);
6666

67-
// Convert MediaResult[] to GalleryPhoto[]
68-
const galleryPhotos: GalleryPhoto[] = result.photos.map(photo => ({
69-
path: photo.path,
70-
webPath: photo.webPath,
71-
format: photo.format,
72-
exif: undefined, // MediaResult doesn't include exif
73-
}));
74-
75-
this.props.onMediaResult(galleryPhotos);
67+
this.props.onMediaResult(result.results);
7668
} catch (e) {
7769
const error = e as any;
7870
const errorMessage = error.code ? `[${error.code}] ${error.message}` : error.message;
@@ -91,15 +83,7 @@ class ChooseFromGalleryConfigurable extends React.Component<
9183
});
9284
console.log("chooseFromGallery result", result);
9385

94-
// Convert MediaResult[] to GalleryPhoto[]
95-
const galleryPhotos: GalleryPhoto[] = result.photos.map(photo => ({
96-
path: photo.path,
97-
webPath: photo.webPath,
98-
format: photo.format,
99-
exif: undefined, // MediaResult doesn't include exif
100-
}));
101-
102-
this.props.onMediaResult(galleryPhotos);
86+
this.props.onMediaResult(result.results);
10387
} catch (e) {
10488
const error = e as any;
10589
const errorMessage = error.code ? `[${error.code}] ${error.message}` : error.message;

example-app/src/components/camera/EditPhotoConfigurable.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class EditPhotoConfigurable extends React.Component<
101101

102102
try {
103103
const result = await Camera.editPhoto({
104-
base64: this.state.base64Image,
104+
inputImage: this.state.base64Image,
105105
});
106106
this.setState({ editedResult: result });
107107
} catch (e) {
@@ -177,11 +177,8 @@ class EditPhotoConfigurable extends React.Component<
177177
{editedResult && (
178178
<div style={{ marginTop: "16px" }}>
179179
<h4>Edited Photo:</h4>
180-
<p>
181-
<strong>Format:</strong> {editedResult.format}
182-
</p>
183180
<img
184-
src={`data:image/${editedResult.format};base64,${editedResult.base64String}`}
181+
src={`data:image/png;base64,${editedResult.outputImage}`}
185182
alt="Edited"
186183
style={{ width: "100%", maxHeight: "300px", objectFit: "contain" }}
187184
/>

example-app/src/components/camera/EditURIPhotoConfigurable.tsx

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,12 @@ import {
99
IonToggle,
1010
} from "@ionic/react";
1111
import React from "react";
12-
import { Camera } from "@capacitor/camera";
12+
import { Camera, MediaResult } from "@capacitor/camera";
1313
import { Filesystem, Directory } from "@capacitor/filesystem";
1414
import { Capacitor } from "@capacitor/core";
1515
import { TestImage } from "./TestImageData";
1616
import { MediaHistoryService } from "../../services/MediaHistoryService";
1717

18-
interface MediaResult {
19-
path: string;
20-
webPath: string;
21-
duration?: number;
22-
size: number;
23-
format: string;
24-
saved: boolean;
25-
}
26-
2718
interface EditURIPhotoConfig {
2819
saveToGallery: boolean;
2920
includeMetadata: boolean;
@@ -146,11 +137,13 @@ class EditURIPhotoConfigurable extends React.Component<
146137
MediaHistoryService.addMedia({
147138
mediaType: "photo",
148139
method: "editURIPhoto",
149-
path: result.path,
140+
uri: result.uri,
150141
webPath: result.webPath,
151-
format: result.format,
152-
size: result.size,
142+
thumbnail: result.thumbnail,
143+
format: result.metadata?.format,
144+
size: result.metadata?.size,
153145
saved: result.saved,
146+
metadata: result.metadata,
154147
});
155148
} catch (e) {
156149
const error = e as any;
@@ -257,14 +250,18 @@ class EditURIPhotoConfigurable extends React.Component<
257250
style={{ width: "100%", maxHeight: "300px", objectFit: "contain" }}
258251
/>
259252
<p>
260-
<strong>Path:</strong> {editedPhoto.path}
261-
</p>
262-
<p>
263-
<strong>Size:</strong> {editedPhoto.size} bytes
264-
</p>
265-
<p>
266-
<strong>Format:</strong> {editedPhoto.format}
253+
<strong>URI:</strong> {editedPhoto.uri}
267254
</p>
255+
{editedPhoto.metadata?.size && (
256+
<p>
257+
<strong>Size:</strong> {editedPhoto.metadata.size} bytes
258+
</p>
259+
)}
260+
{editedPhoto.metadata?.format && (
261+
<p>
262+
<strong>Format:</strong> {editedPhoto.metadata.format}
263+
</p>
264+
)}
268265
<p>
269266
<strong>Saved:</strong> {editedPhoto.saved ? "Yes" : "No"}
270267
</p>

example-app/src/components/camera/MediaCarousel.tsx

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState } from "react";
2-
import { GalleryPhoto } from "@capacitor/camera";
2+
import { MediaResult, MediaType } from "@capacitor/camera";
33
import { Swiper, SwiperSlide } from "swiper/react";
44
import { Navigation, Pagination } from "swiper/modules";
55
import PhotoWithMetadata from "./PhotoWithMetadata";
@@ -12,8 +12,7 @@ import "swiper/css/navigation";
1212
import "swiper/css/pagination";
1313

1414
interface IMediaCarouselProps {
15-
// TODO change this type to allow including future MediaResult in ChooseFromGallery
16-
media: GalleryPhoto[];
15+
media: MediaResult[];
1716
onEditPhoto?: (filePath: string) => void;
1817
}
1918

@@ -24,15 +23,25 @@ const MediaCarousel: React.FC<IMediaCarouselProps> = ({ media, onEditPhoto }) =>
2423
setCurrentIndex(swiper.activeIndex + 1);
2524
};
2625

27-
const isVideo = (format: string | undefined, filePath: string): boolean => {
26+
const isVideo = (item: MediaResult): boolean => {
27+
// Primary detection: check MediaType
28+
if (item.type === MediaType.video) {
29+
return true;
30+
}
31+
if (item.type === MediaType.picture) {
32+
return false;
33+
}
34+
35+
// Fallback: check format from metadata
2836
const videoFormats = ['mp4', 'mov', 'avi', 'webm', 'mkv', 'm4v', 'flv'];
37+
const format = item.metadata?.format;
2938

30-
// Check format first if available
3139
if (format) {
3240
return videoFormats.includes(format.toLowerCase());
3341
}
3442

35-
// Fall back to checking file extension from path
43+
// Last resort: check file extension from path
44+
const filePath = item.uri ?? item.webPath ?? '';
3645
const extension = filePath.split('.').pop()?.toLowerCase();
3746
return extension ? videoFormats.includes(extension) : false;
3847
};
@@ -52,19 +61,16 @@ const MediaCarousel: React.FC<IMediaCarouselProps> = ({ media, onEditPhoto }) =>
5261
onSlideChange={handleSlideChange}
5362
>
5463
{media.map((item, index) => {
55-
const filePath = item.path ?? item.webPath;
56-
const metadata = item.exif
57-
? JSON.stringify(item.exif, null, 2)
58-
: null;
64+
const filePath = item.uri ?? item.webPath ?? '';
5965

6066
return (
6167
<SwiperSlide key={index}>
62-
{isVideo(item.format, filePath) ? (
63-
<VideoWithMetadata filePath={filePath} metadata={metadata} />
68+
{isVideo(item) ? (
69+
<VideoWithMetadata filePath={filePath} metadata={item.metadata} />
6470
) : (
6571
<PhotoWithMetadata
6672
filePath={filePath}
67-
metadata={metadata}
73+
metadata={item.metadata}
6874
onEdit={onEditPhoto}
6975
/>
7076
)}

example-app/src/components/camera/PhotoWithMetadata.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import React from "react";
22
import { IonCard, IonCardContent, IonButton } from "@ionic/react";
33
import { Capacitor } from "@capacitor/core";
4+
import { MediaMetadata } from "@capacitor/camera";
45

56
interface IPhotoWithMetadataProps {
67
filePath: string;
7-
metadata?: string | null;
8+
metadata?: MediaMetadata | string | null;
89
onEdit?: (filePath: string) => void;
910
}
1011

@@ -13,6 +14,34 @@ const PhotoWithMetadata: React.FC<IPhotoWithMetadataProps> = ({
1314
metadata,
1415
onEdit,
1516
}) => {
17+
const formatMetadata = (meta: MediaMetadata | string | null | undefined): string => {
18+
if (!meta) return '';
19+
20+
// If it's already a string (legacy), return as-is
21+
if (typeof meta === 'string') {
22+
return meta;
23+
}
24+
25+
// Format MediaMetadata object
26+
const parts: string[] = [];
27+
if (meta.size !== undefined) {
28+
const sizeKB = (meta.size / 1024).toFixed(1);
29+
const sizeMB = (meta.size / (1024 * 1024)).toFixed(2);
30+
parts.push(`Size: ${meta.size < 1024 * 1024 ? sizeKB + ' KB' : sizeMB + ' MB'}`);
31+
}
32+
if (meta.format) parts.push(`Format: ${meta.format}`);
33+
if (meta.resolution) parts.push(`Resolution: ${meta.resolution}`);
34+
if (meta.creationDate) {
35+
const date = new Date(meta.creationDate);
36+
parts.push(`Created: ${date.toLocaleString()}`);
37+
}
38+
if (meta.exif) {
39+
parts.push(`\nEXIF Data:\n${JSON.stringify(meta.exif, null, 2)}`);
40+
}
41+
42+
return parts.join('\n');
43+
};
44+
1645
return (
1746
<IonCard>
1847
<IonCardContent>
@@ -31,7 +60,7 @@ const PhotoWithMetadata: React.FC<IPhotoWithMetadataProps> = ({
3160
)}
3261
{metadata && (
3362
<div>
34-
<pre>{metadata}</pre>
63+
<pre>{formatMetadata(metadata)}</pre>
3564
</div>
3665
)}
3766
</IonCardContent>

example-app/src/components/camera/RecordVideoConfigurable.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,17 @@ import {
77
IonToggle,
88
} from "@ionic/react";
99
import React from "react";
10-
import { Camera } from "@capacitor/camera";
10+
import { Camera, MediaResult } from "@capacitor/camera";
1111

1212
interface RecordVideoConfig {
1313
saveToGallery: boolean;
1414
includeMetadata: boolean;
15-
}
16-
17-
interface VideoResult {
18-
path: string;
19-
webPath: string;
20-
duration?: number;
21-
size: number;
22-
format: string;
23-
saved: boolean;
15+
isPersistent: boolean;
2416
}
2517

2618
interface RecordVideoConfigurableProps {
2719
buttonLabel?: string;
28-
onVideoResult: (result: VideoResult) => void;
20+
onVideoResult: (result: MediaResult) => void;
2921
}
3022

3123
interface RecordVideoConfigurableState {
@@ -42,6 +34,7 @@ class RecordVideoConfigurable extends React.Component<
4234
config: {
4335
saveToGallery: false,
4436
includeMetadata: true,
37+
isPersistent: true,
4538
},
4639
};
4740
}
@@ -69,6 +62,7 @@ class RecordVideoConfigurable extends React.Component<
6962
const result = await Camera.recordVideo({
7063
saveToGallery: config.saveToGallery,
7164
includeMetadata: config.includeMetadata,
65+
isPersistent: config.isPersistent,
7266
});
7367
this.props.onVideoResult(result);
7468
} catch (e) {
@@ -121,6 +115,16 @@ class RecordVideoConfigurable extends React.Component<
121115
/>
122116
</IonItem>
123117

118+
<IonItem>
119+
<IonLabel>Is Persistent</IonLabel>
120+
<IonToggle
121+
checked={config.isPersistent}
122+
onIonChange={(e) =>
123+
this.updateConfig("isPersistent", e.detail.checked)
124+
}
125+
/>
126+
</IonItem>
127+
124128
<IonButton
125129
expand="block"
126130
color="primary"

0 commit comments

Comments
 (0)