Skip to content

Commit

Permalink
[GAL-4500] Polish mention mobile (#2029)
Browse files Browse the repository at this point in the history
* Trigger on @

* Trigger on @ for post screen

* toss search title if no result

* Markdown support bold

* Highlight keyword on search

* Move text highlight to shared

* prettier

---------

Co-authored-by: Robinnnnn <[email protected]>
  • Loading branch information
jakzaizzat and Robinnnnn authored Oct 21, 2023
1 parent a74f998 commit a6a6b61
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,21 @@ export function CommentsBottomSheet({
<View className="flex-grow">
{isSelectingMentions ? (
<View className="flex-1 overflow-hidden">
<Suspense fallback={<SearchResultsFallback />}>
<SearchResults
keyword={aliasKeyword}
activeFilter="top"
onChangeFilter={noop}
blurInputFocus={noop}
onSelect={selectMention}
onlyShowTopResults
isMentionSearch
/>
</Suspense>
{aliasKeyword ? (
<Suspense fallback={<SearchResultsFallback />}>
<SearchResults
keyword={aliasKeyword}
activeFilter="top"
onChangeFilter={noop}
blurInputFocus={noop}
onSelect={selectMention}
onlyShowTopResults
isMentionSearch
/>
</Suspense>
) : (
<SearchResultsFallback />
)}
</View>
) : (
<View className="flex-1 space-y-2">
Expand Down
3 changes: 3 additions & 0 deletions apps/mobile/src/components/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const markdownStyles = {
fontSize: 14,
fontFamily: 'ABCDiatypeRegular',
},
strong: {
fontFamily: 'ABCDiatypeBold',
},
};

const darkModeMarkdownStyles = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import { SearchResult } from '../SearchResult';

type Props = {
communityRef: CommunitySearchResultFragment$key;
keyword: string;
onSelect?: (item: MentionType) => void;
};
export function CommunitySearchResult({ communityRef, onSelect }: Props) {
export function CommunitySearchResult({ communityRef, keyword, onSelect }: Props) {
const community = useFragment(
graphql`
fragment CommunitySearchResultFragment on Community {
Expand Down Expand Up @@ -61,6 +62,7 @@ export function CommunitySearchResult({ communityRef, onSelect }: Props) {
title={community?.name ?? ''}
description={community?.description ?? ''}
variant="Gallery"
keyword={keyword}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { SearchResult } from '../SearchResult';

type Props = {
galleryRef: GallerySearchResultFragment$key;
keyword: string;
};
export function GallerySearchResult({ galleryRef }: Props) {
export function GallerySearchResult({ galleryRef, keyword }: Props) {
const gallery = useFragment(
graphql`
fragment GallerySearchResultFragment on Gallery {
Expand All @@ -35,6 +36,7 @@ export function GallerySearchResult({ galleryRef }: Props) {
title={gallery?.name ?? ''}
description={gallery?.owner?.username ?? ''}
variant="Gallery"
keyword={keyword}
/>
);
}
44 changes: 24 additions & 20 deletions apps/mobile/src/components/Search/SearchDefault.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import { UserSearchResult } from './User/UserSearchResult';
type Props = {
queryRef: SearchDefaultFragment$key;
blurInputFocus: () => void;
keyword: string;
};

type ListItemType =
| { kind: 'header'; title: string }
| { kind: 'user'; user: UserSearchResultFragment$key };

export function SearchDefault({ queryRef, blurInputFocus }: Props) {
export function SearchDefault({ queryRef, blurInputFocus, keyword }: Props) {
const query = useFragment(
graphql`
fragment SearchDefaultFragment on Query {
Expand All @@ -35,25 +36,28 @@ export function SearchDefault({ queryRef, blurInputFocus }: Props) {
queryRef
);

const renderItem = useCallback<ListRenderItem<ListItemType>>(({ item }) => {
if (item.kind === 'header') {
return (
<View className="p-4">
<Typography
font={{
family: 'ABCDiatype',
weight: 'Medium',
}}
className="text-metal text-xs uppercase"
>
{item.title}
</Typography>
</View>
);
} else {
return <UserSearchResult userRef={item.user} />;
}
}, []);
const renderItem = useCallback<ListRenderItem<ListItemType>>(
({ item }) => {
if (item.kind === 'header') {
return (
<View className="p-4">
<Typography
font={{
family: 'ABCDiatype',
weight: 'Medium',
}}
className="text-metal text-xs uppercase"
>
{item.title}
</Typography>
</View>
);
} else {
return <UserSearchResult userRef={item.user} keyword={keyword} />;
}
},
[keyword]
);

const items = useMemo((): ListItemType[] => {
const items: ListItemType[] = [];
Expand Down
53 changes: 15 additions & 38 deletions apps/mobile/src/components/Search/SearchResult.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import { ReactNode, useMemo } from 'react';
import { StyleSheet, TouchableOpacityProps } from 'react-native';
import { View } from 'react-native';
import { sanitizeMarkdown } from 'src/utils/sanitizeMarkdown';

import { contexts } from '~/shared/analytics/constants';
import { getHighlightedDescription, getHighlightedName } from '~/shared/utils/highlighter';

import { GalleryTouchableOpacity } from '../GalleryTouchableOpacity';
import { Markdown } from '../Markdown';
import { useSearchContext } from './SearchContext';

type Props = {
title: string;
description: string;
variant: 'Gallery' | 'User';
profilePicture?: ReactNode;
keyword: string;
} & TouchableOpacityProps;

const MAX_DESCRIPTION_CHARACTER = 150;

const markdownStyles = StyleSheet.create({
paragraph: {
marginBottom: 0,
Expand All @@ -27,41 +25,20 @@ const markdownStyles = StyleSheet.create({
},
});

export function SearchResult({ title, description, variant, profilePicture, ...props }: Props) {
const { keyword } = useSearchContext();

const highlightedName = useMemo(() => {
if (!keyword) {
return title;
}
return title.replace(new RegExp(keyword, 'gi'), (match) => `**${match}**`);
}, [keyword, title]);

const highlightedDescription = useMemo(() => {
const regex = new RegExp(keyword, 'gi');

const unformattedDescription = sanitizeMarkdown(description ?? '');
if (!keyword) {
return unformattedDescription.substring(0, MAX_DESCRIPTION_CHARACTER);
}
export function SearchResult({
title,
description,
keyword,
variant,
profilePicture,
...props
}: Props) {
const highlightedName = useMemo(() => getHighlightedName(title, keyword), [keyword, title]);

const matchIndex = unformattedDescription.search(regex);
let truncatedDescription;

const maxLength = MAX_DESCRIPTION_CHARACTER;

if (matchIndex > -1 && matchIndex + keyword.length === unformattedDescription.length) {
const endIndex = Math.min(unformattedDescription.length, maxLength);
truncatedDescription = `...${unformattedDescription.substring(
endIndex - maxLength,
endIndex
)}`;
} else {
truncatedDescription = unformattedDescription.substring(0, maxLength);
}
// highlight keyword
return truncatedDescription.replace(regex, (match) => `**${match}**`);
}, [keyword, description]);
const highlightedDescription = useMemo(
() => getHighlightedDescription(description, keyword),
[keyword, description]
);

return (
<GalleryTouchableOpacity
Expand Down
20 changes: 13 additions & 7 deletions apps/mobile/src/components/Search/SearchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export function SearchResults({
// if there is no active filter, show both curators and galleries
// but only show a preview of the results
else if (activeFilter === 'top') {
if (hasUsers) {
if (hasUsers && searchUsers.results.length > 0) {
items.push({
kind: 'search-section-header',
sectionType: 'curator',
Expand All @@ -221,7 +221,7 @@ export function SearchResults({
}
}

if (hasGalleries && !isMentionSearch) {
if (hasGalleries && !isMentionSearch && searchGalleries.results.length > 0) {
items.push({
kind: 'search-section-header',
sectionType: 'gallery',
Expand All @@ -240,7 +240,7 @@ export function SearchResults({
}
}

if (hasCommunities) {
if (hasCommunities && searchCommunities.results.length > 0) {
items.push({
kind: 'search-section-header',
sectionType: 'community',
Expand Down Expand Up @@ -293,16 +293,22 @@ export function SearchResults({
/>
);
} else if (item.kind === 'user-search-result') {
return <UserSearchResult userRef={item.user} onSelect={onSelect} />;
return <UserSearchResult userRef={item.user} onSelect={onSelect} keyword={keyword} />;
} else if (item.kind === 'gallery-search-result') {
return <GallerySearchResult galleryRef={item.gallery} />;
return <GallerySearchResult galleryRef={item.gallery} keyword={keyword} />;
} else if (item.kind === 'community-search-result') {
return <CommunitySearchResult communityRef={item.community} onSelect={onSelect} />;
return (
<CommunitySearchResult
communityRef={item.community}
onSelect={onSelect}
keyword={keyword}
/>
);
}

return <View />;
},
[onChangeFilter, onSelect, showAllButton]
[onChangeFilter, keyword, onSelect, showAllButton]
);

if (isEmpty) {
Expand Down
4 changes: 3 additions & 1 deletion apps/mobile/src/components/Search/User/UserSearchResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { SearchResult } from '../SearchResult';
type Props = {
userRef: UserSearchResultFragment$key;
onSelect?: (item: MentionType) => void;
keyword: string;
};

export function UserSearchResult({ userRef, onSelect }: Props) {
export function UserSearchResult({ userRef, keyword, onSelect }: Props) {
const user = useFragment(
graphql`
fragment UserSearchResultFragment on GalleryUser {
Expand Down Expand Up @@ -51,6 +52,7 @@ export function UserSearchResult({ userRef, onSelect }: Props) {
title={user?.username ?? ''}
description={user?.bio ?? ''}
variant="User"
keyword={keyword}
/>
);
}
9 changes: 7 additions & 2 deletions apps/mobile/src/hooks/useMentionableMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function useMentionableMessage(queryRef: useMentionableMessageQueryFragme
setIsSelectingMentions(false);

setMentions([...adjustedMentions, newMention]);
setAliasKeyword('');
},
[mentions, message, setMessage, aliasKeyword, selection.start]
);
Expand All @@ -114,14 +115,17 @@ export function useMentionableMessage(queryRef: useMentionableMessageQueryFragme
.split(' ')
.pop();

if (wordAtCursor && wordAtCursor[0] === '@' && wordAtCursor.length > 1) {
setAliasKeyword(wordAtCursor);
if (wordAtCursor && wordAtCursor[0] === '@' && wordAtCursor.length > 0) {
setIsSelectingMentions(true);
} else {
setAliasKeyword('');
setIsSelectingMentions(false);
}

if (wordAtCursor && wordAtCursor?.length > 1) {
setAliasKeyword(wordAtCursor.slice(1)); // Remove the @
}

// Determine how many characters were added or removed
const diff = message.length - text.length;

Expand Down Expand Up @@ -159,6 +163,7 @@ export function useMentionableMessage(queryRef: useMentionableMessageQueryFragme
setMentions([]);
setIsSelectingMentions(false);
setMessage('');
setAliasKeyword('');
}, []);

const handleSelectionChange = useCallback((selection: { start: number; end: number }) => {
Expand Down
28 changes: 17 additions & 11 deletions apps/mobile/src/screens/PostScreen/PostComposerScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,23 @@ function PostComposerScreenInner() {
/>
<View className="py-4 flex-grow">
{isSelectingMentions ? (
<Suspense fallback={<SearchResultsFallback />}>
<SearchResults
keyword={aliasKeyword}
activeFilter="top"
onChangeFilter={noop}
blurInputFocus={noop}
onSelect={selectMention}
onlyShowTopResults
isMentionSearch
/>
</Suspense>
<View className="flex-1">
{aliasKeyword ? (
<Suspense fallback={<SearchResultsFallback />}>
<SearchResults
keyword={aliasKeyword}
activeFilter="top"
onChangeFilter={noop}
blurInputFocus={noop}
onSelect={selectMention}
onlyShowTopResults
isMentionSearch
/>
</Suspense>
) : (
<SearchResultsFallback />
)}
</View>
) : (
<Suspense fallback={<PostComposerNftFallback />}>
<PostTokenPreview />
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/src/screens/SearchScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function SearchScreen() {
/>
</View>
) : (
<SearchDefault queryRef={query} blurInputFocus={blurInputFocus} />
<SearchDefault queryRef={query} blurInputFocus={blurInputFocus} keyword={keyword} />
)}
</GalleryTouchableOpacity>
</KeyboardAvoidingView>
Expand Down
Loading

0 comments on commit a6a6b61

Please sign in to comment.