Skip to content

Commit

Permalink
feat: add Pals screen and related components
Browse files Browse the repository at this point in the history
  • Loading branch information
Mukhammadali committed Feb 10, 2025
1 parent bf3e88b commit 302e3a3
Show file tree
Hide file tree
Showing 73 changed files with 3,182 additions and 236 deletions.
17 changes: 10 additions & 7 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,14 @@ import {KeyboardProvider} from 'react-native-keyboard-controller';
import {useTheme} from './src/hooks';
import {Theme} from './src/utils/types';

import {
SidebarContent,
ModelsHeaderRight,
ChatHeader,
HeaderLeft,
} from './src/components';
import {SidebarContent, ModelsHeaderRight, HeaderLeft} from './src/components';
import {
ChatScreen,
ModelsScreen,
SettingsScreen,
BenchmarkScreen,
} from './src/screens';
import {PalsScreen} from './src/screens/PalsScreen';

const Drawer = createDrawerNavigator();

Expand Down Expand Up @@ -62,7 +58,7 @@ const App = observer(() => {
name="Chat"
component={gestureHandlerRootHOC(ChatScreen)}
options={{
header: () => <ChatHeader />,
headerShown: false,
}}
/>
<Drawer.Screen
Expand All @@ -87,6 +83,13 @@ const App = observer(() => {
headerStyle: styles.headerWithoutDivider,
}}
/>
<Drawer.Screen
name="Pals"
component={gestureHandlerRootHOC(PalsScreen)}
options={{
headerStyle: styles.headerWithoutDivider,
}}
/>
</Drawer.Navigator>
</NavigationContainer>
</BottomSheetModalProvider>
Expand Down
50 changes: 50 additions & 0 deletions __mocks__/stores/palStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {makeAutoObservable} from 'mobx';
import {v4 as uuidv4} from 'uuid';
import {
AssistantFormData,
RoleplayFormData,
} from '../../src/components/PalsSheets/types';
import {Pal} from '../../src/store/PalStore';

class MockPalStore {
pals: Pal[] = [];

constructor() {
makeAutoObservable(this);
}

addPal = jest.fn((data: AssistantFormData | RoleplayFormData) => {
const newPal = {
id: uuidv4(),
...data,
} as Pal;
this.pals.push(newPal);
});

updatePal = jest.fn(
(id: string, data: Partial<AssistantFormData | RoleplayFormData>) => {
const palIndex = this.pals.findIndex(p => p.id === id);
if (palIndex !== -1) {
const currentPal = this.pals[palIndex];
this.pals[palIndex] = {
...currentPal,
...data,
palType: currentPal.palType,
} as Pal;
}
},
);

deletePal = jest.fn((id: string) => {
const palIndex = this.pals.findIndex(p => p.id === id);
if (palIndex !== -1) {
this.pals.splice(palIndex, 1);
}
});

getPals = jest.fn(() => {
return this.pals;
});
}

export const mockPalStore = new MockPalStore();
4 changes: 2 additions & 2 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ PODS:
- hermes-engine (0.76.3):
- hermes-engine/Pre-built (= 0.76.3)
- hermes-engine/Pre-built (0.76.3)
- llama-rn (0.4.8-2):
- llama-rn (0.5.2):
- React-Core
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
Expand Down Expand Up @@ -2264,7 +2264,7 @@ SPEC CHECKSUMS:
GoogleAppMeasurement: ee5c2d2242816773fbf79e5b0563f5355ef1c315
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
hermes-engine: 0555a84ea495e8e3b4bde71b597cd87fbb382888
llama-rn: eb844b9cc4b240409b0411f16836416e567374f8
llama-rn: b6a8a6db8e1d0074aee1ac919f1a5de8e1a243c5
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
RCT-Folly: bf5c0376ffe4dd2cf438dcf86db385df9fdce648
Expand Down
34 changes: 33 additions & 1 deletion jest/fixtures/models.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {CompletionParams} from '@pocketpalai/llama.rn';
import {CompletionParams, NativeLlamaContext} from '@pocketpalai/llama.rn';

import {deviceInfo} from './device-info';

Expand All @@ -10,6 +10,38 @@ import {
ModelOrigin,
} from '../../src/utils/types';

export const mockContextModel: NativeLlamaContext['model'] = {
desc: '',
size: 0,
nEmbd: 0,
nParams: 0,
chatTemplates: {
llamaChat: false,
minja: {
default: false,
defaultCaps: {
tools: false,
toolCalls: false,
toolResponses: false,
systemRole: false,
parallelToolCalls: false,
toolCallId: false,
},
toolUse: false,
toolUseCaps: {
tools: false,
toolCalls: false,
toolResponses: false,
systemRole: false,
parallelToolCalls: false,
toolCallId: false,
},
},
},
isChatTemplateSupported: false,
metadata: {},
};

export const mockDefaultCompletionParams: CompletionParams = {
prompt: '',
n_predict: 400,
Expand Down
2 changes: 2 additions & 0 deletions jest/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {mockHFStore} from '../__mocks__/stores/hfStore';
import {mockModelStore} from '../__mocks__/stores/modelStore';
import {mockChatSessionStore} from '../__mocks__/stores/chatSessionStore';
import {benchmarkStore as mockBenchmarkStore} from '../__mocks__/stores/benchmarkStore';
import {mockPalStore} from '../__mocks__/stores/palStore';

jest.mock('@react-native-clipboard/clipboard', () => mockClipboard);

Expand All @@ -68,6 +69,7 @@ jest.mock('../src/store', () => {
chatSessionStore: mockChatSessionStore,
hfStore: mockHFStore,
benchmarkStore: mockBenchmarkStore,
palStore: mockPalStore,
};
});

Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"@dr.pogodin/react-native-fs": "^2.30.3",
"@flyerhq/react-native-link-preview": "^1.6.0",
"@gorhom/bottom-sheet": "^5.0.6",
"@pocketpalai/llama.rn": "^0.4.8-2",
"@hookform/resolvers": "^3.10.0",
"@pocketpalai/llama.rn": "^0.5.2",
"@react-native-async-storage/async-storage": "^2.1.0",
"@react-native-clipboard/clipboard": "^1.15.0",
"@react-native-community/blur": "^4.4.1",
Expand All @@ -47,6 +48,7 @@
"mobx-persist-store": "^1.1.5",
"mobx-react": "^9.1.1",
"react": "18.3.1",
"react-hook-form": "^7.54.2",
"react-native": "0.76.3",
"react-native-config": "^1.5.1",
"react-native-device-info": "^13.1.0",
Expand All @@ -68,7 +70,9 @@
"react-native-screens": "^4.4.0",
"react-native-svg": "^15.11.1",
"react-native-vector-icons": "^10.1.0",
"uuid": "^10.0.0"
"tinycolor2": "^1.6.0",
"uuid": "^10.0.0",
"zod": "^3.24.1"
},
"devDependencies": {
"@babel/core": "^7.25.2",
Expand Down
3 changes: 3 additions & 0 deletions src/assets/icons/chevron-down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/chevron-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/chevron-up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/assets/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ export {default as RefreshIcon} from './refresh.svg';
export {default as ReverseLeftIcon} from './reverse-left.svg';
export {default as CloseIcon} from './close.svg';
export {default as MenuIcon} from './menu.svg';
export {default as ChevronDownIcon} from './chevron-down.svg';
export {default as PlusIcon} from './plus.svg';
export {default as ChevronRightIcon} from './chevron-right.svg';
export {default as ChevronUpIcon} from './chevron-up.svg';
export {default as SendIcon} from './send.svg';
export {default as StopIcon} from './stop.svg';
3 changes: 3 additions & 0 deletions src/assets/icons/plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/send.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/icons/stop.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/pocketpal-dark-v2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions src/components/ChatEmptyPlaceholder/ChatEmptyPlaceholder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import {Image, View} from 'react-native';
import {Button, Text} from 'react-native-paper';
import {observer} from 'mobx-react';

import {useTheme} from '../../hooks';
import {createStyles} from './styles';
import {modelStore} from '../../store';

interface ChatEmptyPlaceholderProps {
onSelectModel: () => void;
bottomComponentHeight: number;
}

export const ChatEmptyPlaceholder = observer(
({onSelectModel, bottomComponentHeight}: ChatEmptyPlaceholderProps) => {
const theme = useTheme();
const styles = createStyles({theme});

const hasAvailableModels = modelStore.availableModels.length > 0;
const hasActiveModel = modelStore.activeModelId !== undefined;

const getContent = () => {
if (!hasAvailableModels) {
return {
title: 'No Models Available',
description: 'Download a model to start chatting with PocketPal',
buttonText: 'Download Model',
};
}

return {
title: 'Activate Model To Get Started',
description:
'Select the model and download it. After downloading, tap Load next to the model and start chatting.',
buttonText: 'Select Model',
};
};

const {title, description, buttonText} = getContent();

if (hasActiveModel) {
return null;
}
return (
<View
style={[styles.container, {marginBottom: bottomComponentHeight + 100}]}>
<Image
source={require('../../assets/pocketpal-dark-v2.png')}
style={styles.logo}
resizeMode="contain"
/>
<View>
<Text style={styles.title}>{title}</Text>
<Text style={styles.description}>{description}</Text>
</View>
<Button
mode="contained"
onPress={onSelectModel}
style={styles.button}
loading={modelStore.isContextLoading}
disabled={hasActiveModel}>
{modelStore.isContextLoading ? 'Loading...' : buttonText}
</Button>
</View>
);
},
);
1 change: 1 addition & 0 deletions src/components/ChatEmptyPlaceholder/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {ChatEmptyPlaceholder} from './ChatEmptyPlaceholder';
32 changes: 32 additions & 0 deletions src/components/ChatEmptyPlaceholder/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {StyleSheet} from 'react-native';
import {Theme} from '../../utils/types';

export const createStyles = ({theme}: {theme: Theme}) =>
StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 32,
gap: theme.spacing.default,
},
title: {
color: theme.colors.onSurface,
textAlign: 'center',
marginBottom: 8,
...theme.fonts.titleMedium,
},
description: {
color: theme.colors.onSurfaceVariant,
textAlign: 'center',
...theme.fonts.bodyMedium,
},
button: {
minWidth: 200,
},
logo: {
width: 112,
height: 112,
borderRadius: 30,
},
});
Loading

0 comments on commit 302e3a3

Please sign in to comment.