Compare commits

...

1 Commits

Author SHA1 Message Date
Brad Anderson
5a4c78f21f repro: progressive-img 2025-06-14 13:40:57 -04:00
6 changed files with 209 additions and 8 deletions

View File

@@ -11,18 +11,22 @@
"dependencies": {
"@azure/core-asynciterator-polyfill": "^1.0.2",
"@bacons/text-decoder": "^0.0.0",
"@bam.tech/react-native-image-resizer": "^3.0.11",
"@craftzdog/react-native-buffer": "^6.0.5",
"@react-native-community/netinfo": "11.4.1",
"expo": "~53.0.9",
"expo-clipboard": "^7.1.4",
"expo-image-picker": "~16.1.4",
"expo-secure-store": "~14.2.3",
"expo-sqlite": "~15.2.10",
"expo-status-bar": "~2.2.3",
"jazz-expo": "workspace:*",
"jazz-react-native-media-images": "workspace:*",
"jazz-tools": "workspace:*",
"react": "19.0.0",
"react-native": "0.79.2",
"react-native-get-random-values": "^1.11.0",
"react-native-svg": "^15.12.0",
"readable-stream": "^4.7.0"
},
"devDependencies": {

View File

@@ -2,6 +2,7 @@ import { JazzProvider } from "jazz-expo";
import React, { StrictMode } from "react";
import { apiKey } from "./apiKey";
import ChatScreen from "./chat";
import { ChatAccount } from "./schema";
export default function App() {
return (
@@ -10,6 +11,7 @@ export default function App() {
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
AccountSchema={ChatAccount}
>
<ChatScreen />
</JazzProvider>

View File

@@ -0,0 +1,97 @@
import * as ImagePicker from "expo-image-picker";
import { ProgressiveImg } from "jazz-expo";
import { createImage } from "jazz-react-native-media-images";
import { Group, ImageDefinition } from "jazz-tools";
import React, { useState } from "react";
import { Dimensions, Pressable, StyleSheet, Text, View } from "react-native";
import Svg, { Defs, Mask, Path, Image as SvgImage } from "react-native-svg";
import { ChatAccount } from "./schema";
function MaskedBackgroundPhoto({ imageUri }: { imageUri: string }) {
const windowWidth = Dimensions.get("window").width;
const height = Dimensions.get("window").height * 0.4;
return (
<View style={[styles.backgroundContainer, { width: windowWidth, height }]}>
<Svg height={height} width={windowWidth}>
<Defs>
<Mask id="mask" x="0" y="0" height="100%" width="100%">
<Path
d={`M0 0 L${windowWidth} 0 L${windowWidth} ${height - 40} Q${windowWidth / 2} ${height + 50} 0 ${height - 40} Z`}
fill="white"
/>
</Mask>
</Defs>
<SvgImage
width="100%"
height="100%"
href={{ uri: imageUri }}
preserveAspectRatio="xMidYMid slice"
mask="url(#mask)"
/>
</Svg>
</View>
);
}
export function BackgroundPhoto({
image,
owner,
}: {
image: ImageDefinition | null;
owner: ChatAccount | Group | undefined | null;
}) {
const [img, setImg] = useState<ImageDefinition | null>(image);
const handleImageUpload = async () => {
try {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ["images"],
base64: true,
quality: 0.8,
});
if (!result.canceled && owner && result.assets[0].base64) {
const base64Uri = `data:image/jpeg;base64,${result.assets[0].base64}`;
const img = await createImage(base64Uri, {
owner,
maxSize: 2048,
});
setImg(img);
}
} catch (error) {
console.error("Failed to upload image:", error);
}
};
return (
<Pressable onPress={handleImageUpload}>
{img ? (
<ProgressiveImg
image={img}
targetWidth={Dimensions.get("window").width}
>
{({ src }) => (src ? <MaskedBackgroundPhoto imageUri={src} /> : null)}
</ProgressiveImg>
) : (
<View style={styles.noPhotoContainer}>
<Text style={styles.noPhotoText}>No background photo</Text>
</View>
)}
</Pressable>
);
}
const styles = StyleSheet.create({
backgroundContainer: {
position: "relative",
},
noPhotoContainer: {
width: "100%",
height: Dimensions.get("window").height * 0.2,
backgroundColor: "lightgray",
justifyContent: "center",
},
noPhotoText: {
textAlign: "center",
},
});

View File

@@ -1,24 +1,26 @@
import * as Clipboard from "expo-clipboard";
import { Account, Group } from "jazz-tools";
import { useAccount, useCoState } from "jazz-expo";
import { Group } from "jazz-tools";
import { useState } from "react";
import React, {
Alert,
Button,
FlatList,
KeyboardAvoidingView,
SafeAreaView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
Alert,
StyleSheet,
} from "react-native";
import { useAccount, useCoState } from "jazz-expo";
import { Chat, Message } from "./schema";
import { BackgroundPhoto } from "./background";
import { Chat, ChatAccount, Message } from "./schema";
export default function ChatScreen() {
const { me, logOut } = useAccount(Account, { resolve: { profile: true } });
const { me, logOut } = useAccount(ChatAccount, {
resolve: { profile: true },
});
const [chatId, setChatId] = useState<string>();
const [chatIdInput, setChatIdInput] = useState<string>();
const loadedChat = useCoState(Chat, chatId, { resolve: { $each: true } });
@@ -96,6 +98,10 @@ export default function ChatScreen() {
<View style={styles.container}>
{!loadedChat ? (
<View style={styles.welcomeContainer}>
<BackgroundPhoto
image={me?.profile.backgroundPhoto ?? null}
owner={me}
/>
<Text style={styles.usernameTitle}>Username</Text>
<TextInput
style={styles.usernameInput}

View File

@@ -1,4 +1,5 @@
import { co, z } from "jazz-tools";
import { ImageDefinition } from "jazz-tools";
export const Message = co.map({
text: z.string(),
@@ -7,3 +8,11 @@ export type Message = co.loaded<typeof Message>;
export const Chat = co.list(Message);
export type Chat = co.loaded<typeof Chat>;
export const ChatAccount = co.account({
root: co.map({}),
profile: co.profile({
backgroundPhoto: z.optional(ImageDefinition),
}),
});
export type ChatAccount = co.loaded<typeof ChatAccount>;

85
pnpm-lock.yaml generated
View File

@@ -386,6 +386,9 @@ importers:
'@bacons/text-decoder':
specifier: ^0.0.0
version: 0.0.0(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))
'@bam.tech/react-native-image-resizer':
specifier: ^3.0.11
version: 3.0.11(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)
'@craftzdog/react-native-buffer':
specifier: ^6.0.5
version: 6.0.5(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)
@@ -398,6 +401,9 @@ importers:
expo-clipboard:
specifier: ^7.1.4
version: 7.1.4(expo@53.0.9(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0))(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)
expo-image-picker:
specifier: ~16.1.4
version: 16.1.4(expo@53.0.9(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0))
expo-secure-store:
specifier: ~14.2.3
version: 14.2.3(expo@53.0.9(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0))
@@ -410,6 +416,9 @@ importers:
jazz-expo:
specifier: workspace:*
version: link:../../packages/jazz-expo
jazz-react-native-media-images:
specifier: workspace:*
version: link:../../packages/jazz-react-native-media-images
jazz-tools:
specifier: workspace:*
version: link:../../packages/jazz-tools
@@ -422,6 +431,9 @@ importers:
react-native-get-random-values:
specifier: ^1.11.0
version: 1.11.0(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))
react-native-svg:
specifier: ^15.12.0
version: 15.12.0(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)
readable-stream:
specifier: ^4.7.0
version: 4.7.0
@@ -9333,6 +9345,9 @@ packages:
css-in-js-utils@3.1.0:
resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==}
css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
css-shorthand-properties@1.1.2:
resolution: {integrity: sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==}
@@ -9343,6 +9358,10 @@ packages:
css-value@0.0.1:
resolution: {integrity: sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==}
css-what@6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
css.escape@1.5.1:
resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
@@ -9646,6 +9665,19 @@ packages:
dom-accessibility-api@0.6.3:
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
dotenv-expand@11.0.7:
resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==}
engines: {node: '>=12'}
@@ -13687,6 +13719,12 @@ packages:
react: 19.0.0
react-native: '*'
react-native-svg@15.12.0:
resolution: {integrity: sha512-iE25PxIJ6V0C6krReLquVw6R0QTsRTmEQc4K2Co3P6zsimU/jltcDBKYDy1h/5j9S/fqmMeXnpM+9LEWKJKI6A==}
peerDependencies:
react: 19.0.0
react-native: '*'
react-native-url-polyfill@2.0.0:
resolution: {integrity: sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==}
peerDependencies:
@@ -22735,7 +22773,7 @@ snapshots:
sirv: 3.0.1
tinyglobby: 0.2.14
tinyrainbow: 2.0.0
vitest: 3.1.3(@types/node@22.15.18)(@vitest/browser@3.1.3)(@vitest/ui@3.1.3)(happy-dom@17.4.4)(jiti@2.4.2)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.7.0(@types/node@22.15.18)(typescript@5.6.2))(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1)
vitest: 3.1.3(@types/node@22.15.18)(@vitest/browser@3.1.3)(@vitest/ui@3.1.3)(happy-dom@17.4.4)(jiti@2.4.2)(jsdom@25.0.1)(lightningcss@1.30.1)(msw@2.7.0(@types/node@22.15.18)(typescript@5.8.3))(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1)
'@vitest/utils@3.1.1':
dependencies:
@@ -24137,6 +24175,14 @@ snapshots:
dependencies:
hyphenate-style-name: 1.1.0
css-select@5.1.0:
dependencies:
boolbase: 1.0.0
css-what: 6.1.0
domhandler: 5.0.3
domutils: 3.2.2
nth-check: 2.1.1
css-shorthand-properties@1.1.2: {}
css-tree@1.1.3:
@@ -24146,6 +24192,8 @@ snapshots:
css-value@0.0.1: {}
css-what@6.1.0: {}
css.escape@1.5.1: {}
cssesc@3.0.0: {}
@@ -24356,6 +24404,24 @@ snapshots:
dom-accessibility-api@0.6.3: {}
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
entities: 4.5.0
domelementtype@2.3.0: {}
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
dotenv-expand@11.0.7:
dependencies:
dotenv: 16.4.7
@@ -25134,11 +25200,20 @@ snapshots:
dependencies:
expo: 53.0.8(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)
expo-image-loader@5.1.0(expo@53.0.9(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)):
dependencies:
expo: 53.0.9(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)
expo-image-picker@16.1.4(expo@53.0.8(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)):
dependencies:
expo: 53.0.8(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)
expo-image-loader: 5.1.0(expo@53.0.8(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0))
expo-image-picker@16.1.4(expo@53.0.9(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)):
dependencies:
expo: 53.0.9(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0)
expo-image-loader: 5.1.0(expo@53.0.9(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0))
expo-json-utils@0.15.0: {}
expo-keep-awake@14.1.4(expo@53.0.8(@babel/core@7.26.0)(@expo/metro-runtime@5.0.4(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)))(graphql@16.11.0)(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0))(react@19.0.0):
@@ -29301,6 +29376,14 @@ snapshots:
react-native: 0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.6.2))(@types/react@19.0.0)(react@19.0.0)
warn-once: 0.1.1
react-native-svg@15.12.0(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0))(react@19.0.0):
dependencies:
css-select: 5.1.0
css-tree: 1.1.3
react: 19.0.0
react-native: 0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.8.3))(@types/react@19.0.0)(react@19.0.0)
warn-once: 0.1.1
react-native-url-polyfill@2.0.0(react-native@0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.6.2))(@types/react@19.0.0)(react@19.0.0)):
dependencies:
react-native: 0.79.2(@babel/core@7.26.0)(@react-native-community/cli@15.0.1(typescript@5.6.2))(@types/react@19.0.0)(react@19.0.0)