Skip to content

Commit

Permalink
refactor settings for multi-jinni support and update rendering image …
Browse files Browse the repository at this point in the history
…for new api url config
  • Loading branch information
kibagateaux committed Sep 22, 2024
1 parent bd0f57c commit 50645c5
Show file tree
Hide file tree
Showing 16 changed files with 400 additions and 230 deletions.
184 changes: 104 additions & 80 deletions src/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';

import { View, StyleSheet, Share, Image } from 'react-native';
import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
import { View, StyleSheet, Share, Image, Linking } from 'react-native';
import { isEmpty } from 'lodash/fp';

import { useHomeConfig } from 'hooks';
import { useAuth, useGameContent } from 'contexts';
import { WidgetConfig, WidgetIds, obj } from 'types/UserConfig';
import { getIconForWidget } from 'utils/rendering';

import { AvatarViewer, WidgetIcon } from 'components/index';
import DefaultAvatar from 'assets/avatars/red-yellow-egg';
import { AvatarViewer, AvatarViewerDefault, WidgetIcon } from 'components/index';
import WidgetContainer from 'components/home/WidgetContainer';
import OnboardingWizard from 'components/wizards/OnboardingWizard';
import {
STAGE_AVATAR_CONFIG,
TRACK_ONBOARDING_STAGE,
getAppConfig,
getStorage,
itemAbilityToWidgetConfig,
saveStorage,
} from 'utils/config';
import { debug } from 'utils/logging';
import { magicRug } from 'utils/zkpid';
import { Button } from '@rneui/themed';
import { useActiveJinni } from 'hooks/useActiveJinni';
import { UpdateWidgetConfigParams } from 'types/api';
import ModalRenderer from 'components/modals';

const HomeScreen = () => {
const { config: homeConfig, save: saveHomeConfig } = useHomeConfig();
const { setActiveModal } = useGameContent();
const { player, getSpellBook } = useAuth();
const eggRollAngle = useSharedValue(30);
const eggAnimatedStyles = useAnimatedStyle(() => ({
transform: [{ rotate: `${eggRollAngle.value}deg` }],
}));
const [widgetConfig, setWidgetConfig] = useState<WidgetConfig[]>([]);
const [onboardingStage, setOnboardingStage] = useState<string>();

const { config: homeConfig, save: saveHomeConfig } = useHomeConfig();
const { setActiveModal } = useGameContent();
const { player, isNPC, getSpellBook } = useAuth();
const { jid } = useActiveJinni();

const [appReady, setAppReady] = useState<boolean>(false);
console.log('loading app....', player, appReady);

Expand All @@ -53,6 +53,7 @@ const HomeScreen = () => {
setWidgetConfig(homeConfig.widgets);
}
}, [homeConfig, widgetConfig]);
console.log('page:home:widgi', widgetConfig);

useMemo(async () => {
const currentStage = await getStorage<obj>(TRACK_ONBOARDING_STAGE);
Expand All @@ -63,21 +64,6 @@ const HomeScreen = () => {
}
}, [onboardingStage]);

useEffect(() => {
const intervalId = setInterval(() => {
const otherSide = eggRollAngle.value < 0 ? 30 : -30;
eggRollAngle.value = withSpring(otherSide, {
duration: 2000, // 4 sec side to side,
dampingRatio: 0.4,
stiffness: 33,
overshootClamping: false,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 15.58,
});
}, 2500);
return () => clearInterval(intervalId);
});

// console.log('Home:widgi', widgetConfig);

const saveWidgets = useCallback(
Expand All @@ -88,10 +74,8 @@ const HomeScreen = () => {
);

const finalizeRenovation = useCallback(
(widgets?: WidgetConfig[], merge: boolean = true) => {
saveHomeConfig(widgets ?? widgetConfig, merge);
},
[widgetConfig, saveHomeConfig],
(updates: UpdateWidgetConfigParams) => saveHomeConfig(updates),
[saveHomeConfig],
);

const completeWizardStage = useCallback(
Expand All @@ -105,6 +89,78 @@ const HomeScreen = () => {
[setOnboardingStage],
);

const finalizeOnboarding = useCallback(
async (config: object) => {
try {
// if (!player?.id) {
// setActiveModal({
// name: 'create-spellbook',
// dialogueData: {
// title: 'A jinni is approaching',
// text: 'Wait for it to sniff you and say hi',
// },
// });
// await getSpellBook();
// setActiveModal(undefined);
// }

console.log('save config', player);
if (!player?.id || !jid) {
throw new Error('Player NPC profile not initiated yet!');
}

const newConfig = await finalizeRenovation({
jType: 'p2p',
summoner: player.id,
merge: true,
widgets: [
...(config.stats?.map((widgetId: string) =>
itemAbilityToWidgetConfig(
'MaliksMajik',
`stat-${widgetId.toLowerCase()}` as WidgetIds,
),
) ?? []),
{
id: 'maliksmajik-avatar-viewer',
provider: 'MaliksMajik',
routeName: 'index', // home page
title: 'Avatar Viewer',
path: '/',
config,
// They can't have registered yet so no jinni id to specify for config
// eventually need to link with jiini. Could do in activate_jinni but :Widget wont be there if they start from desktop flow (github/facrcaster)
// target_uuid: await getStorage(ID_JINNI_SLOT)
},
],
});
console.log('app:home:finalizeOnboarding:saved-config', newConfig);

if (!newConfig) {
// saving jinni config from onboarding failed. do not continue to home screen
throw new Error(
'Could not save settings to the Master Djinn. They have been stored locally so you dont need to fill them out again :)',
);
}
// complete onboarding only if config successfully saved on server
await completeWizardStage(STAGE_AVATAR_CONFIG);

Linking.openURL('https://t.me/+fkqlrBc4YYczM2Mx');
} catch (e) {
console.log('app:home:finalizeOnboarding:error', e);
setActiveModal({
name: 'api-error',
dialogueData: {
title: 'Majiq mesesage not sent over the cosmic wire',
text: e as string,
},
});
debug(e, { extra: { onboardingStage: STAGE_AVATAR_CONFIG } });
// throw(e);
}
},
[player, jid, finalizeRenovation, setActiveModal, completeWizardStage],
);

const HomeWidget = ({ id, title, path }: WidgetConfig) => {
return (
<WidgetIcon
Expand Down Expand Up @@ -137,52 +193,10 @@ const HomeScreen = () => {
// TODO abstrzct into onboarding component systemization
if (onboardingStage === STAGE_AVATAR_CONFIG)
return (
<OnboardingWizard
startIndex={0}
onComplete={async (config) => {
try {
console.log('on avatar config wizard complete', config.stats);
if (!player?.id)
setActiveModal({
name: 'create-spellbook',
dialogueData: {
title: 'A jinni is approaching',
text: 'Wait for it to sniff you and say hi',
},
});
await getSpellBook();
setActiveModal(undefined);

await Promise.all([
finalizeRenovation(
[
...(config.stats?.map((widgetId: string) =>
itemAbilityToWidgetConfig(
'MaliksMajik',
`stat-${widgetId.toLowerCase()}` as WidgetIds,
),
) ?? []),
{
id: 'maliksmajik-avatar-viewer',
provider: 'MaliksMajik',
routeName: 'index', // home page
title: 'Avatar Viewer',
path: '/',
config,
// They can't have registered yet so no jinni id to specify for config
// eventually need to link with jiini. Could do in activate_jinni but :Widget wont be there if they start from desktop flow (github/facrcaster)
// target_uuid: await getStorage(ID_JINNI_SLOT)
},
],
false,
), // overwrite current config with new onboarding config
completeWizardStage(STAGE_AVATAR_CONFIG),
]);
} catch (e) {
debug(e, { extra: { onboardingStage } });
}
}}
/>
<>
<ModalRenderer />
<OnboardingWizard startIndex={0} onComplete={finalizeOnboarding} />
</>
);

return (
Expand All @@ -205,9 +219,19 @@ const HomeScreen = () => {
}
/>
)}
<Animated.View style={[styles.avatar, eggAnimatedStyles]}>
<AvatarViewer uri={homeConfig?.jinniImage} SVG={DefaultAvatar} />
</Animated.View>
{/* TODO if not homeConfig.lastDiviTime then animated egg roll (no jinni evolution yet)
dont use API url bc thats actual avatar, client side option to not display actual avatar image
uri = egg
else normal avatar viewer. with uri = /avatars/{jid}
*/}
<View style={styles.avatar}>
{isNPC ? (
<AvatarViewerDefault />
) : (
<AvatarViewer uri={`${getAppConfig().API_URL}/avatars/${jid}`} />
)}
</View>

<Button color="purple" title="Speak Intention" onPress={onIntentionPress} />
</View>
<WidgetContainer
Expand Down
23 changes: 20 additions & 3 deletions src/components/common/AvatarViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { View, PanResponder, Text } from 'react-native';
import { View, PanResponder, Text, Image } from 'react-native';
// import { GLView } from 'expo-gl';

interface AvatarViewerProps {
Expand All @@ -23,7 +23,24 @@ const AvatarViewer = ({ SVG, uri, is3d }: AvatarViewerProps) => {

// console.log('AvatarViewer', { SVG, uri, is3d });

const SvgComponent = () => (!SVG ? null : <SVG />);
// const SvgComponent = () => (!SVG ? null : <SVG />);
const renderImg = () => {
if (SVG) return <SVG />;
if (uri)
return (
<Image
source={{ uri }}
style={{
// TODO unhardcode this and egg/avatar SVG height/width with Dimension.get('window')
width: 150,
height: 200,
// width: width * 0.5,
// height: height * 0.75,
transform: [{ rotateZ: `${rotation}deg` }],
}}
/>
);
};
// TODO add suppoort for rendering base64 encoded images
// <Image style={styles.image} source={{uri: `data:image/png;base64,${encodedBase64}`}} />
// https://stackoverflow.com/questions/29380265/does-react-native-support-base64-encoded-images
Expand All @@ -40,7 +57,7 @@ const AvatarViewer = ({ SVG, uri, is3d }: AvatarViewerProps) => {
}}
>
{!is3d ? (
<SvgComponent />
renderImg()
) : (
<Text>3d Avatar Model</Text>
// <GLView
Expand Down
35 changes: 35 additions & 0 deletions src/components/common/AvatarViewerDefault.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useEffect } from 'react';
import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';

import { AvatarViewer } from 'components/index';
import DefaultAvatar from 'assets/avatars/red-yellow-egg';

const AvatarViewerDefault = () => {
const eggRollAngle = useSharedValue(30);
const eggAnimatedStyles = useAnimatedStyle(() => ({
transform: [{ rotate: `${eggRollAngle.value}deg` }],
}));

useEffect(() => {
const intervalId = setInterval(() => {
const otherSide = eggRollAngle.value < 0 ? 30 : -30;
eggRollAngle.value = withSpring(otherSide, {
duration: 2000, // 4 sec side to side,
dampingRatio: 0.4,
stiffness: 33,
overshootClamping: false,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 15.58,
});
}, 2500);
return () => clearInterval(intervalId);
});

return (
<Animated.View style={[eggAnimatedStyles]}>
<AvatarViewer SVG={DefaultAvatar} />
</Animated.View>
);
};

export default AvatarViewerDefault;
7 changes: 5 additions & 2 deletions src/components/home/WidgetContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { useInventory } from 'hooks/useInventory';
import SelectMultiModal from 'components/modals/SelectMultiModal';
import { WidgetConfig, WidgetIds } from 'types/UserConfig';
import { itemAbilityToWidgetConfig } from 'utils/config';
import { UpdateWidgetConfigParams } from 'types/api';

interface WidgetContainerProps {
widgets: WidgetConfig[];
saveWidgets?: (widgets: WidgetConfig[]) => void;
finalizeRenovation?: (widgets?: WidgetConfig[], merge?: boolean) => void; // send to server
finalizeRenovation?: (updates: UpdateWidgetConfigParams) => void; // send to server
WidgetRenderer: React.FC<WidgetConfig>;
renovationConfig?: object; // options for DraggableFlatList
}
Expand Down Expand Up @@ -91,7 +92,7 @@ const WidgetContainer = ({
const onEditModeEnd = () => {
setEditMode(false);
// undefined = use widgetConfig in homepage, false = overwrite storage
if (finalizeRenovation) finalizeRenovation(undefined, false);
if (finalizeRenovation) finalizeRenovation({ widgets: [], merge: true });
};

const renderRenovationMode = () => {
Expand Down Expand Up @@ -121,6 +122,8 @@ const WidgetContainer = ({

const renderBaseMode = () => {
// console.log('Widgi:base');
console.log('WidgiContainer:RenderBase:widgi', widgets, allWidgets);

return (
// <gesture={longPress}>
<FlatList
Expand Down
1 change: 1 addition & 0 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// alphabetical order
export { default as AvatarViewer } from './common/AvatarViewer';
export { default as AvatarViewerDefault } from './common/AvatarViewerDefault';
export { default as Card } from './common/Card';
export { default as Link } from './common/Link';
export { default as Pill } from './common/Pill';
Expand Down
10 changes: 9 additions & 1 deletion src/components/wizards/BaseScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ export interface BaseWizardScreenProps {
onBack: () => void;
backButtonStyle?: object;
nextButtonStyle?: object;
children?: React.ReactElement;
}

const BaseWizardScreen = ({ dialogueData, step, onNext, onBack }: BaseWizardScreenProps) => {
const BaseWizardScreen = ({
dialogueData,
step,
onNext,
onBack,
children,
}: BaseWizardScreenProps) => {
return (
<View style={styles.container}>
{!dialogueData ? null : (
Expand All @@ -27,6 +34,7 @@ const BaseWizardScreen = ({ dialogueData, step, onNext, onBack }: BaseWizardScre
))}
</View>
)}
{children}
<View style={styles.buttonContainer}>
<Button
title="Back"
Expand Down
Loading

0 comments on commit 50645c5

Please sign in to comment.