@mereb/app-feed 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.tsx +104 -0
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -50,10 +50,69 @@ type FeedScreenProps = {
|
|
|
50
50
|
onSelectAuthor?: (handle: string) => void;
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
type Suggestion = {
|
|
54
|
+
handle: string;
|
|
55
|
+
hint?: string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const FALLBACK_SUGGESTIONS: Suggestion[] = [
|
|
59
|
+
{handle: 'mereb-team', hint: 'Product updates'},
|
|
60
|
+
{handle: 'designers', hint: 'Design drops'},
|
|
61
|
+
{handle: 'dev-journal', hint: 'Engineering notes'}
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
function SuggestionsSection({
|
|
65
|
+
suggestions,
|
|
66
|
+
onSelectAuthor
|
|
67
|
+
}: Readonly<{
|
|
68
|
+
suggestions: Suggestion[];
|
|
69
|
+
onSelectAuthor?: (handle: string) => void;
|
|
70
|
+
}>) {
|
|
71
|
+
if (suggestions.length === 0) return null;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<View style={styles.suggestionsCard}>
|
|
75
|
+
<Text style={styles.suggestionsTitle}>Discover people to follow</Text>
|
|
76
|
+
<Text style={styles.suggestionsBody}>
|
|
77
|
+
Follow a few profiles to keep your feed fresh.
|
|
78
|
+
</Text>
|
|
79
|
+
<View style={styles.suggestionsList}>
|
|
80
|
+
{suggestions.map((sugg) => (
|
|
81
|
+
<TouchableOpacity
|
|
82
|
+
key={sugg.handle}
|
|
83
|
+
style={styles.suggestionPill}
|
|
84
|
+
onPress={() => onSelectAuthor?.(sugg.handle)}
|
|
85
|
+
accessibilityRole="button"
|
|
86
|
+
>
|
|
87
|
+
<Text style={styles.suggestionHandle}>@{sugg.handle}</Text>
|
|
88
|
+
{sugg.hint ? (
|
|
89
|
+
<Text style={styles.suggestionHint}>{sugg.hint}</Text>
|
|
90
|
+
) : null}
|
|
91
|
+
</TouchableOpacity>
|
|
92
|
+
))}
|
|
93
|
+
</View>
|
|
94
|
+
</View>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
53
98
|
export function FeedScreen({onSelectAuthor}: Readonly<FeedScreenProps>) {
|
|
54
99
|
const {data, loading, error, refetch} = useFeedQuery();
|
|
55
100
|
|
|
56
101
|
const posts = useMemo(() => selectFeedNodes(data), [data]);
|
|
102
|
+
const suggestions = useMemo(() => {
|
|
103
|
+
const seen = new Set<string>();
|
|
104
|
+
const derived: Suggestion[] = [];
|
|
105
|
+
for (const post of posts) {
|
|
106
|
+
if (seen.has(post.author.handle)) continue;
|
|
107
|
+
seen.add(post.author.handle);
|
|
108
|
+
derived.push({handle: post.author.handle});
|
|
109
|
+
}
|
|
110
|
+
const combined = [...derived, ...FALLBACK_SUGGESTIONS].filter(
|
|
111
|
+
(s) => !seen.has(s.handle) // avoid dupes with fallbacks
|
|
112
|
+
);
|
|
113
|
+
return combined.slice(0, 5);
|
|
114
|
+
}, [posts]);
|
|
115
|
+
const shouldShowSuggestions = !loading && posts.length < 5;
|
|
57
116
|
|
|
58
117
|
const handleRefresh = useCallback(() => {
|
|
59
118
|
void refetch();
|
|
@@ -74,6 +133,14 @@ export function FeedScreen({onSelectAuthor}: Readonly<FeedScreenProps>) {
|
|
|
74
133
|
contentContainerStyle={styles.listContent}
|
|
75
134
|
ItemSeparatorComponent={SeparatorWithStyle}
|
|
76
135
|
refreshControl={<RefreshControl refreshing={loading} onRefresh={handleRefresh}/>}
|
|
136
|
+
ListHeaderComponent={
|
|
137
|
+
shouldShowSuggestions ? (
|
|
138
|
+
<SuggestionsSection
|
|
139
|
+
suggestions={suggestions}
|
|
140
|
+
onSelectAuthor={onSelectAuthor}
|
|
141
|
+
/>
|
|
142
|
+
) : null
|
|
143
|
+
}
|
|
77
144
|
renderItem={({item}) => (
|
|
78
145
|
<PostCard post={item} onSelectAuthor={onSelectAuthor}/>
|
|
79
146
|
)}
|
|
@@ -153,5 +220,42 @@ const styles = StyleSheet.create({
|
|
|
153
220
|
fontSize: 14,
|
|
154
221
|
color: color.textMuted,
|
|
155
222
|
textAlign: 'center'
|
|
223
|
+
},
|
|
224
|
+
suggestionsCard: {
|
|
225
|
+
backgroundColor: color.surface,
|
|
226
|
+
borderRadius: radius.lg,
|
|
227
|
+
padding: spacing.lg,
|
|
228
|
+
marginBottom: spacing.lg
|
|
229
|
+
},
|
|
230
|
+
suggestionsTitle: {
|
|
231
|
+
fontSize: 18,
|
|
232
|
+
fontWeight: '700',
|
|
233
|
+
color: color.text,
|
|
234
|
+
marginBottom: spacing.xs
|
|
235
|
+
},
|
|
236
|
+
suggestionsBody: {
|
|
237
|
+
color: color.textMuted,
|
|
238
|
+
marginBottom: spacing.md
|
|
239
|
+
},
|
|
240
|
+
suggestionsList: {
|
|
241
|
+
flexDirection: 'row',
|
|
242
|
+
flexWrap: 'wrap',
|
|
243
|
+
gap: spacing.sm
|
|
244
|
+
},
|
|
245
|
+
suggestionPill: {
|
|
246
|
+
paddingVertical: spacing.xs,
|
|
247
|
+
paddingHorizontal: spacing.md,
|
|
248
|
+
borderRadius: radius.pill ?? radius.lg,
|
|
249
|
+
backgroundColor: color.neutralMuted ?? color.surfaceMuted,
|
|
250
|
+
flexDirection: 'column',
|
|
251
|
+
gap: spacing.xxs
|
|
252
|
+
},
|
|
253
|
+
suggestionHandle: {
|
|
254
|
+
color: color.text,
|
|
255
|
+
fontWeight: '600'
|
|
256
|
+
},
|
|
257
|
+
suggestionHint: {
|
|
258
|
+
color: color.textMuted,
|
|
259
|
+
fontSize: 12
|
|
156
260
|
}
|
|
157
261
|
});
|