@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.tsx +104 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mereb/app-feed",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Feed experience building blocks for Mereb apps",
5
5
  "type": "module",
6
6
  "main": "src/index.tsx",
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
  });