Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New Profile Screen (read-only) #1519

Merged
merged 26 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
257801e
Change the profile screen from appearing as a modal to displaying in …
lourou Jan 13, 2025
b9e5b66
Name the dm/group chat screen so it has a name in the navigation stack
lourou Jan 13, 2025
421fd3d
WIP implement profile screen header with the design system along with…
lourou Jan 13, 2025
eb64fba
Header design fixes and text presets
lourou Jan 13, 2025
cf95354
Fix going back not to top
lourou Jan 13, 2025
679f608
Remove Privy and balance utils from Profile screen
lourou Jan 14, 2025
2f847ab
Implement `ContactCard` component
lourou Jan 14, 2025
2a3961f
Better styling
lourou Jan 14, 2025
8c499e2
Fix shadow offset issue
lourou Jan 16, 2025
11ae686
Use the QR code or compose button based on whether you are viewing yo…
lourou Jan 16, 2025
c998ad3
feat(Button): add `link.bare` variant, enable style override and styl…
lourou Jan 16, 2025
1cc0bf1
Maintains credit card aspect ratio
lourou Jan 16, 2025
e1b68ae
Create `ContactCard` and `FullWidthTable` components, WIP Profile ref…
lourou Jan 16, 2025
d301be7
Implement profile log out
lourou Jan 16, 2025
0d5ec12
feat(Profile): update context menu styling and fix edit mode
lourou Jan 16, 2025
32c4ba5
Make block/unblock work in context menu
lourou Jan 16, 2025
3b64b5c
Adopt `DropdownMenu` to make the context menu work on Android
lourou Jan 16, 2025
ee8ed92
Improvements
lourou Jan 16, 2025
fe484ac
Revert Button style changes, keep the `link.bare` variant
lourou Jan 16, 2025
7e6dbbf
Fix typecheck issue
lourou Jan 16, 2025
a889c73
feat(table): create a new Table component in the design system
lourou Jan 16, 2025
6ae6e71
Implement enable notifications on own profile
lourou Jan 16, 2025
8c5a55b
Add name chips
lourou Jan 16, 2025
0fdafe1
Type fixes
lourou Jan 16, 2025
708b676
Various sets of fixes
lourou Jan 16, 2025
f9bdac9
Rename component `Table` to `settings-list`
lourou Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/TransactionPreview/TransactionResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const $failure: ThemedStyle<ViewStyle> = ({ spacing, borderRadius }) => ({
marginBottom: spacing.md,
padding: spacing.sm,
backgroundColor: "#FFF5F5",
borderRadius: borderRadius.xs,
borderRadius: borderRadius.xxs,
});

const styles = StyleSheet.create({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ const $failure: ThemedStyle<ViewStyle> = ({ spacing, borderRadius }) => ({
marginBottom: spacing.md,
padding: spacing.sm,
backgroundColor: "#FFF5F5",
borderRadius: borderRadius.xs,
borderRadius: borderRadius.xxs,
});
1 change: 1 addition & 0 deletions design-system/Button/Button.props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type IButtonVariant =
| "outline"
| "fill"
| "link"
| "link.bare"
/** @deprecated */
| "secondary"
| "secondary-danger"
Expand Down
10 changes: 10 additions & 0 deletions design-system/Button/Button.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ export const getButtonViewStyle =
size === "md" || size === "sm" ? spacing.xs : spacing.sm,
};

// Special case for bare link text buttons - no padding or other decorations
if (variant === "link.bare") {
return {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: spacing.xxs,
};
}
Comment on lines +57 to +64
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure about this variant and style 🤔

Wonder if we should just have around a for now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a button with no extra padding/margin, which helps align things. I used i in the ContactCard, but design has changed. Although this will be needed in the future!


if (action === "primary") {
switch (variant) {
case "fill":
Expand Down
6 changes: 4 additions & 2 deletions design-system/Icon/Icon.android.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const iconRegistry: Record<
link: "link",
paperplane: "send",
account_circle: "account-circle",
pencil: "edit",
"square.and.pencil": "edit",
qrcode: "qr-code",
"message.circle.fill": "chat",
Expand Down Expand Up @@ -58,6 +59,7 @@ export const iconRegistry: Record<
"arrowshape.turn.up.left": "reply",
"arrowshape.turn.up.left.fill": "reply",
"person.crop.circle.badge.plus": "group-add",
"person.crop.circle.badge.xmark": "person-remove",
tray: "inbox",
cloud: "cloud",
"exclamationmark.triangle": "warning",
Expand Down Expand Up @@ -89,8 +91,8 @@ export function Icon(props: IIconProps) {
const iconName = icon
? iconRegistry[icon]
: picto
? iconRegistry[picto]
: null;
? iconRegistry[picto]
: null;

if (!iconName) {
logger.warn(`Invalid icon name ${icon || picto}`);
Expand Down
6 changes: 4 additions & 2 deletions design-system/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const iconRegistry: Record<IIconName, string> = {
link: "link",
paperplane: "paperplane",
account_circle: "person.circle",
pencil: "pencil",
"square.and.pencil": "square.and.pencil",
qrcode: "qrcode",
"message.circle.fill": "message.circle.fill",
Expand Down Expand Up @@ -55,6 +56,7 @@ export const iconRegistry: Record<IIconName, string> = {
"arrowshape.turn.up.left": "arrowshape.turn.up.left",
"arrowshape.turn.up.left.fill": "arrowshape.turn.up.left.fill",
"person.crop.circle.badge.plus": "person.crop.circle.badge.plus",
"person.crop.circle.badge.xmark": "person.crop.circle.badge.xmark",
tray: "tray",
cloud: "cloud",
"exclamationmark.triangle": "exclamationmark.triangle",
Expand Down Expand Up @@ -98,8 +100,8 @@ export function Icon(props: IIconProps) {
const iconName = icon
? iconRegistry[icon]
: picto
? iconRegistry[picto]
: null;
? iconRegistry[picto]
: null;

if (!iconName) {
logger.warn(
Expand Down
2 changes: 2 additions & 0 deletions design-system/Icon/Icon.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type IIconName =
| "link"
| "paperplane"
| "account_circle"
| "pencil"
| "square.and.pencil"
| "qrcode"
| "message.circle.fill"
Expand Down Expand Up @@ -52,6 +53,7 @@ export type IIconName =
| "arrowshape.turn.up.left"
| "arrowshape.turn.up.left.fill"
| "person.crop.circle.badge.plus"
| "person.crop.circle.badge.xmark"
| "tray"
| "cloud"
| "exclamationmark.triangle"
Expand Down
2 changes: 1 addition & 1 deletion design-system/TextField/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ const $inputWrapperStyle: ThemedStyle<ViewStyle> = ({
spacing,
}) => ({
borderWidth: borderWidth.sm,
borderRadius: borderRadius.xs,
borderRadius: borderRadius.xxs,
backgroundColor: colors.background.surface,
borderColor: colors.border.subtle,
overflow: "hidden",
Expand Down
12 changes: 8 additions & 4 deletions design-system/chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ export function Chip({
{avatarUrl && (
<Avatar size={theme.avatarSize.xs} uri={avatarUrl} name={text} />
)}
<Text style={themed(isActive ? $chipTextActive : $chipText)}>
<Text
preset="smaller"
style={themed(isActive ? $chipTextActive : $chipText)}
>
{text}
</Text>
</HStack>
Expand All @@ -73,9 +76,10 @@ const $chip: ThemedStyle<ViewStyle> = ({
borderWidth: borderWidth.sm,
borderColor: colors.border.subtle,
paddingVertical: spacing.xxs,
paddingHorizontal: spacing.xs,
minHeight: 36,
// ...debugBorder("orange"),
paddingHorizontal: spacing.sm,
minHeight: 36, // from Figma
justifyContent: "center",
backgroundColor: colors.background.surface,
});

const $chipActive: ThemedStyle<ViewStyle> = ({ colors }) => ({
Expand Down
80 changes: 80 additions & 0 deletions design-system/settings-list/settings-list-row.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { memo } from "react";
import { View, ViewStyle, Switch, TouchableOpacity } from "react-native";
import { Text } from "@/design-system/Text";
import { useAppTheme, ThemedStyle } from "@/theme/useAppTheme";
import { HStack } from "@/design-system/HStack";
import { VStack } from "@/design-system/VStack";
import { Icon } from "@/design-system/Icon/Icon";
import { ISettingsListRow } from "./settings-list.types";

type ISettingsListRowProps = {
row: ISettingsListRow;
editMode?: boolean;
};

export const SettingsListRow = memo(function SettingsListRow({
row,
editMode,
}: ISettingsListRowProps) {
const { theme, themed } = useAppTheme();

const content = (
<HStack style={themed($innerRowContainer)}>
<VStack style={{ flex: 1 }}>
<Text color={row.isWarning ? "caution" : "primary"}>{row.label}</Text>
{row.value && (
<Text
preset="formLabel"
color={row.isWarning ? "caution" : "secondary"}
>
{row.value}
</Text>
)}
</VStack>
<View style={themed($rightContentContainer)}>
{row.isSwitch ? (
<Switch
value={!!row.value}
onValueChange={row.onValueChange}
disabled={row.disabled}
/>
) : (
<Icon
icon="chevron.right"
size={theme.iconSize.sm}
color={theme.colors.text.secondary}
/>
)}
</View>
</HStack>
);

if (row.onPress) {
return (
<TouchableOpacity style={themed($rowContainer)} onPress={row.onPress}>
{content}
</TouchableOpacity>
);
}

return <HStack style={themed($rowContainer)}>{content}</HStack>;
});

const $rowContainer: ThemedStyle<ViewStyle> = ({ spacing }) => ({
paddingVertical: spacing.md,
width: "100%",
flexDirection: "row",
alignItems: "center",
});

const $innerRowContainer: ThemedStyle<ViewStyle> = () => ({
width: "100%",
justifyContent: "space-between",
alignItems: "center",
});

const $rightContentContainer: ThemedStyle<ViewStyle> = () => ({
marginLeft: "auto",
alignItems: "flex-end",
justifyContent: "center",
});
33 changes: 33 additions & 0 deletions design-system/settings-list/settings-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { memo } from "react";
import { View, ViewStyle } from "react-native";
import { useAppTheme, ThemedStyle } from "@/theme/useAppTheme";
import { SettingsListRow } from "./settings-list-row";
import { ISettingsListRow } from "./settings-list.types";

type ISettingsListProps = {
rows: ISettingsListRow[];
editMode?: boolean;
};

export const SettingsList = memo(function SettingsList({
rows,
editMode,
}: ISettingsListProps) {
const { themed } = useAppTheme();

return (
<View style={themed($container)}>
{rows.map((row, index) => (
<SettingsListRow
key={row.label + index}
row={row}
editMode={editMode}
/>
))}
Comment on lines +20 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid using array index in key prop.

Using array index as part of the key prop can lead to issues with component reordering and state preservation. Consider using a unique identifier from the row data instead.

 {rows.map((row, index) => (
   <SettingsListRow
-    key={row.label + index}
+    key={row.id || row.label} // Add an id property to ISettingsListRow
     row={row}
     editMode={editMode}
   />
 ))}

Committable suggestion skipped: line range outside the PR's diff.

</View>
);
});

const $container: ThemedStyle<ViewStyle> = () => ({
width: "100%",
});
9 changes: 9 additions & 0 deletions design-system/settings-list/settings-list.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type ISettingsListRow = {
label: string;
value?: string | boolean;
onPress?: () => void;
onValueChange?: (value: boolean) => void;
isWarning?: boolean;
isSwitch?: boolean;
disabled?: boolean;
};
2 changes: 1 addition & 1 deletion features/ExternalWalletPicker/ExternalWalletPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export function ExternalWalletPicker(props: IExternalWalletPickerProps) {
style={{
width: theme.avatarSize.md,
height: theme.avatarSize.md,
borderRadius: theme.borderRadius.xs,
borderRadius: theme.borderRadius.xxs,
}}
/>
<Text preset="smaller" style={$globalStyles.flex1 as TextStyle}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function useHeaderWrapper() {
{
displayInline: true,
id: "app-settings",
title: translate("App settings"),
title: translate("app_settings"),
image: iconRegistry["settings"],
},
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ const ReplyPreviewEndContent = memo(function ReplyPreviewEndContent(props: {
style: {
height: theme.avatarSize.md,
width: theme.avatarSize.md,
borderRadius: theme.borderRadius.xs,
borderRadius: theme.borderRadius.xxs,
},
}}
/>
Expand All @@ -215,7 +215,7 @@ const ReplyPreviewEndContent = memo(function ReplyPreviewEndContent(props: {
style: {
height: theme.avatarSize.md,
width: theme.avatarSize.md,
borderRadius: theme.borderRadius.xs,
borderRadius: theme.borderRadius.xxs,
},
}}
/>
Expand Down
13 changes: 11 additions & 2 deletions features/conversation/conversation.nav.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { ConversationScreen } from "@/features/conversation/conversation.screen";
import { NativeStack } from "@/screens/Navigation/Navigation";
import type { ConversationTopic } from "@xmtp/react-native-sdk";
import { translate } from "@/i18n";
import { useAppTheme } from "@/theme/useAppTheme";
import { HStack } from "@design-system/HStack";
import { HeaderAction } from "@/design-system/Header/HeaderAction";
import { useNavigation } from "@react-navigation/native";
import { Platform } from "react-native";
import { ConversationTopic } from "@xmtp/react-native-sdk";

export type ConversationNavParams = {
topic?: ConversationTopic;
Expand All @@ -19,11 +25,14 @@ export const ConversationScreenConfig = {
};

export function ConversationNav() {
const { theme } = useAppTheme();
const navigation = useNavigation();

return (
<NativeStack.Screen
options={{
title: "",
headerBackTitle: "",
headerTitle: translate("chat"),
// headerBackVisible: false,
}}
name="Conversation"
Expand Down
51 changes: 51 additions & 0 deletions features/notifications/hooks/use-notifications-permission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Platform, Linking } from "react-native";
import { useAppStore } from "@/data/store/appStore";
import { requestPushNotificationsPermissions } from "../utils/requestPushNotificationsPermissions";
import { useSettingsStore } from "@/data/store/accountsStore";

type UseNotificationsPermissionReturn = {
notificationsPermissionStatus: "granted" | "undetermined" | "denied";
requestPermission: () => Promise<void>;
setNotificationsSettings: (settings: {
showNotificationScreen: boolean;
}) => void;
};

export function useNotificationsPermission(): UseNotificationsPermissionReturn {
const notificationsPermissionStatus = useAppStore(
(s) => s.notificationsPermissionStatus
);
const setNotificationsPermissionStatus = useAppStore(
(s) => s.setNotificationsPermissionStatus
);
const setNotificationsSettings = useSettingsStore(
(s) => s.setNotificationsSettings
);

const requestPermission = async () => {
if (notificationsPermissionStatus === "denied") {
if (Platform.OS === "android") {
// Android 13 is always denied first so let's try to show
const newStatus = await requestPushNotificationsPermissions();
if (newStatus === "denied") {
Linking.openSettings();
} else if (newStatus) {
setNotificationsPermissionStatus(newStatus);
}
} else {
Linking.openSettings();
}
} else if (notificationsPermissionStatus === "undetermined") {
// Open popup
const newStatus = await requestPushNotificationsPermissions();
if (!newStatus) return;
setNotificationsPermissionStatus(newStatus);
}
};

return {
notificationsPermissionStatus,
requestPermission,
setNotificationsSettings,
};
}
Comment on lines +1 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Migration needed for notification permission handling

The new hook is properly used in UI components (Profile, NotificationsScreen, OnboardingNotificationsScreen) but several files still use direct permission management:

  • components/AccountSettingsButton.tsx: Direct usage of permission request and status management
  • features/notifications/utils/subscribeToNotifications.ts: Direct permission request
  • features/notifications/utils/saveNotificationsStatus.ts: Direct status management

These files should be refactored to use the new hook for consistent permission handling across the codebase.

🔗 Analysis chain

Verify the impact of this new hook across the codebase.

Let's ensure all notification permission-related code is migrated to use this new hook.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for direct usage of notification permission-related functions
rg "requestPushNotificationsPermissions|setNotificationsPermissionStatus|setNotificationsSettings" --type ts

Length of output: 3684


Script:

#!/bin/bash
# Check Profile.tsx implementation
rg "useNotificationsPermission|setNotificationsSettings" -B 2 -A 2 screens/Profile.tsx

Length of output: 586

Loading
Loading