@plusscommunities/pluss-feature-builder-app-d 8.0.6-beta.0 → 8.0.9

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 (34) hide show
  1. package/dist/module/actions/featureBuilderActions.js +1 -1
  2. package/dist/module/actions/featureBuilderActions.js.map +1 -1
  3. package/dist/module/components/FeatureListScreen.js +69 -40
  4. package/dist/module/components/FeatureListScreen.js.map +1 -1
  5. package/dist/module/components/Widget.js +8 -0
  6. package/dist/module/components/Widget.js.map +1 -0
  7. package/dist/module/components/WidgetLarge.js +80 -163
  8. package/dist/module/components/WidgetLarge.js.map +1 -1
  9. package/dist/module/components/WidgetSmall.js +82 -208
  10. package/dist/module/components/WidgetSmall.js.map +1 -1
  11. package/dist/module/components/layouts/CondensedList.js +3 -2
  12. package/dist/module/components/layouts/CondensedList.js.map +1 -1
  13. package/dist/module/components/layouts/FeatureImageList.js +3 -2
  14. package/dist/module/components/layouts/FeatureImageList.js.map +1 -1
  15. package/dist/module/components/layouts/RoundImageList.js +3 -2
  16. package/dist/module/components/layouts/RoundImageList.js.map +1 -1
  17. package/dist/module/components/layouts/SquareImageList.js +3 -2
  18. package/dist/module/components/layouts/SquareImageList.js.map +1 -1
  19. package/dist/module/feature.config.js +3 -2
  20. package/dist/module/feature.config.js.map +1 -1
  21. package/dist/module/index.js +2 -2
  22. package/dist/module/index.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/actions/featureBuilderActions.js +1 -1
  25. package/src/components/FeatureListScreen.js +85 -60
  26. package/src/components/Widget.js +6 -0
  27. package/src/components/WidgetLarge.js +81 -193
  28. package/src/components/WidgetSmall.js +82 -233
  29. package/src/components/layouts/CondensedList.js +2 -1
  30. package/src/components/layouts/FeatureImageList.js +2 -1
  31. package/src/components/layouts/RoundImageList.js +2 -1
  32. package/src/components/layouts/SquareImageList.js +2 -1
  33. package/src/feature.config.js +2 -1
  34. package/src/index.js +2 -2
@@ -1,23 +1,20 @@
1
- import React, { Component } from "react";
2
- import {
3
- Text,
4
- View,
5
- ScrollView,
6
- StyleSheet,
7
- TouchableOpacity,
8
- Image,
9
- } from "react-native";
10
- import { connect } from "react-redux";
11
- import _ from "lodash";
12
- import { FontAwesome } from "@expo/vector-icons";
13
- import { Services } from "../feature.config";
14
- import { Components, Colours } from "../core.config";
15
- import { values } from "../values.config";
16
- import { selectListings, selectFeatureDefinition } from "../utils/selectors";
17
- import { SPACING } from "../js/spacing";
18
- import { getMainBrandingColourFromState } from "../js";
19
- import { getSummaryFieldValue } from "../js/helpers";
20
- import { loadTargetFeature } from "../actions/featureBuilderActions";
1
+ import React, { Component } from 'react';
2
+ import { Text, View, StyleSheet } from 'react-native';
3
+ import { connect } from 'react-redux';
4
+ import _ from 'lodash';
5
+ import { Services } from '../feature.config';
6
+ import { Components, Colours } from '../core.config';
7
+ import { values } from '../values.config';
8
+ import { selectListings, selectFeatureDefinition } from '../utils/selectors';
9
+ import { SPACING } from '../js/spacing';
10
+ import { getMainBrandingColourFromState } from '../js';
11
+ import { loadTargetFeature } from '../actions/featureBuilderActions';
12
+
13
+ // Layouts
14
+ import RoundImageList from './layouts/RoundImageList';
15
+ import CondensedList from './layouts/CondensedList';
16
+ import SquareImageList from './layouts/SquareImageList';
17
+ import FeatureImageList from './layouts/FeatureImageList';
21
18
 
22
19
  class WidgetSmall extends Component {
23
20
  constructor(props) {
@@ -42,22 +39,34 @@ class WidgetSmall extends Component {
42
39
  if (featureDefinition?.title || featureDefinition?.displayName) {
43
40
  return featureDefinition.title || featureDefinition.displayName;
44
41
  }
45
- return values.featureName || "Features";
42
+ return values.featureName || 'Features';
46
43
  };
47
44
 
48
45
  getEmptyStateText = () => {
49
- const { options } = this.props;
46
+ const { options, featureDefinition } = this.props;
50
47
  if (options && !_.isEmpty(options.EmptyText)) return options.EmptyText;
51
- return values.labels.emptyState;
48
+
49
+ const featureName = featureDefinition?.title || featureDefinition?.displayName || values.featureName || 'Feature';
50
+ return `No listings for ${featureName}`;
51
+ };
52
+
53
+ getLayoutComponent = layoutType => {
54
+ switch (layoutType) {
55
+ case values.layoutTypes.round:
56
+ return RoundImageList;
57
+ case values.layoutTypes.square:
58
+ return SquareImageList;
59
+ case values.layoutTypes.feature:
60
+ return FeatureImageList;
61
+ default:
62
+ return CondensedList;
63
+ }
52
64
  };
53
65
 
54
66
  refresh = () => {
55
67
  this.onLoadingChanged(true, async () => {
56
68
  try {
57
- await loadTargetFeature()(
58
- this.props.dispatch,
59
- () => this.props.getState,
60
- );
69
+ await this.props.dispatch(loadTargetFeature());
61
70
  } finally {
62
71
  this.onLoadingChanged(false);
63
72
  }
@@ -66,156 +75,65 @@ class WidgetSmall extends Component {
66
75
 
67
76
  onLoadingChanged = (loading, callback) => {
68
77
  this.setState({ loading }, () => {
69
- if (this.props.onLoadingChanged)
70
- this.props.onLoadingChanged(this.state.loading);
78
+ if (this.props.onLoadingChanged) this.props.onLoadingChanged(this.state.loading);
71
79
  if (callback) callback();
72
80
  });
73
81
  };
74
82
 
75
- onPressAll = () => {
76
- Services.navigation.navigate(values.screens.featureList, {
77
- options: this.props.options,
78
- });
79
- };
80
-
81
- getImageSource = (imageField) => {
82
- if (!imageField) return null;
83
-
84
- if (typeof imageField === "string") {
85
- return { uri: imageField };
86
- }
87
-
88
- if (typeof imageField === "object" && imageField.uri) {
89
- return imageField;
90
- }
91
-
92
- if (typeof imageField === "object" && imageField.url) {
93
- return { uri: imageField.url };
94
- }
95
-
96
- return null;
97
- };
98
-
99
- onListingPress = (listing) => {
83
+ onItemPress = item => {
100
84
  const { featureDefinition } = this.props;
101
- const title = this.getTitle();
102
- Services.navigation.navigate(values.screens.featureDetail, {
103
- listing: listing,
104
- featureDefinition: featureDefinition,
105
- featureTitle: title,
106
- });
107
- };
108
-
109
- renderListingCard = (listing) => {
110
- const { colourBrandingMain, featureDefinition } = this.props;
111
- const title = listing.fields?.[values.mandatoryFields.title] || "Untitled";
112
- const summary = getSummaryFieldValue(listing, featureDefinition);
113
- const imageField = listing.fields?.[values.mandatoryFields.featureImage];
114
- const imageSource = this.getImageSource(imageField);
115
-
116
- return (
117
- <TouchableOpacity
118
- key={listing.id}
119
- style={styles.cardContainer}
120
- onPress={() => this.onListingPress(listing)}
121
- >
122
- <View style={styles.borderContainer}>
123
- {imageSource ? (
124
- <Image
125
- source={imageSource}
126
- style={styles.cardImage}
127
- resizeMode="cover"
128
- />
129
- ) : (
130
- <View style={[styles.cardImage, styles.placeholderImage]}>
131
- <FontAwesome name="image" size={24} color={Colours.TEXT_LIGHT} />
132
- </View>
133
- )}
134
- <Text style={styles.cardTitle} numberOfLines={1}>
135
- {title}
136
- </Text>
137
- {summary && (
138
- <Text style={styles.cardSummary} numberOfLines={1}>
139
- {summary}
140
- </Text>
141
- )}
142
- </View>
143
- </TouchableOpacity>
144
- );
85
+ if (featureDefinition) {
86
+ Services.navigation.navigate(values.screens.featureDetail, {
87
+ listing: item,
88
+ featureDefinition: featureDefinition,
89
+ featureTitle: featureDefinition.title || featureDefinition.displayName,
90
+ });
91
+ }
145
92
  };
146
93
 
147
94
  renderContent() {
148
- const { listings } = this.props;
95
+ const { listings, featureDefinition, colourBrandingMain } = this.props;
149
96
 
150
- // Don't render widget if no listings are available (and not loading)
151
- if (_.isEmpty(listings) && !this.state.loading) {
152
- return null;
97
+ // Show loading state if no definition or loading
98
+ if (!featureDefinition || this.state.loading) {
99
+ return (
100
+ <View style={styles.loadingPadding}>
101
+ <Components.LoadingStateWidget height={180} />
102
+ </View>
103
+ );
153
104
  }
154
105
 
106
+ // Show empty state if no listings are available
155
107
  if (_.isEmpty(listings)) {
156
- if (this.state.loading) {
157
- return (
158
- <View style={styles.loadingPadding}>
159
- <Components.LoadingStateWidget height={180} />
160
- </View>
161
- );
162
- }
163
- return null;
108
+ return (
109
+ <View style={styles.emptyPadding}>
110
+ <Components.EmptyStateWidget title={this.getTitle()} height={120} />
111
+ </View>
112
+ );
164
113
  }
165
114
 
166
- // Show first 3 listings with peeking indicator
167
- const listingsToShow = listings.slice(0, values.widget.maxItems);
115
+ const layoutType = featureDefinition.layout?.type || 'condensed';
116
+ const LayoutComponent = this.getLayoutComponent(layoutType);
117
+ const title = this.getTitle();
168
118
 
169
119
  return (
170
- <ScrollView
171
- horizontal
172
- contentContainerStyle={{
173
- paddingLeft: SPACING.XS + 2,
174
- paddingRight: SPACING.SM,
175
- }}
176
- showsHorizontalScrollIndicator={false}
177
- >
178
- {listingsToShow.map((listing) => this.renderListingCard(listing))}
179
- {listings.length > values.widget.maxItems && (
180
- <TouchableOpacity
181
- style={[styles.cardContainer, styles.viewMoreCard]}
182
- onPress={this.onPressAll}
183
- >
184
- <View style={[styles.borderContainer, styles.viewMoreContainer]}>
185
- <Text style={styles.viewMoreText}>View all</Text>
186
- <Text style={styles.viewMoreCount}>{listings.length} items</Text>
187
- </View>
188
- </TouchableOpacity>
189
- )}
190
- </ScrollView>
120
+ <LayoutComponent
121
+ listings={listings}
122
+ featureDefinition={featureDefinition}
123
+ colourBrandingMain={colourBrandingMain}
124
+ title={title}
125
+ onItemPress={this.onItemPress}
126
+ />
191
127
  );
192
128
  }
193
129
 
194
130
  render() {
195
- const { colourBrandingMain } = this.props;
196
-
197
- const content = this.renderContent();
198
- // Don't render widget section if content is null (no features available)
199
- if (content === null) {
200
- return null;
201
- }
202
-
203
131
  return (
204
132
  <View style={styles.sectionContainer}>
205
133
  <View style={styles.sectionPadding}>
206
- <View style={styles.sectionHeading}>
207
- <Text style={styles.sectionTitle}>{this.getTitle()}</Text>
208
- <Components.InlineButton
209
- onPress={this.onPressAll}
210
- color={colourBrandingMain}
211
- touchableStyle={{ paddingTop: SPACING.XS + 2 }}
212
- textStyle={{ color: "#fff" }}
213
- >
214
- {values.labels.viewAll}
215
- </Components.InlineButton>
216
- </View>
134
+ <Text style={styles.sectionTitle}>{this.getTitle()}</Text>
217
135
  </View>
218
- {content}
136
+ {this.renderContent()}
219
137
  </View>
220
138
  );
221
139
  }
@@ -223,7 +141,7 @@ class WidgetSmall extends Component {
223
141
 
224
142
  const styles = StyleSheet.create({
225
143
  sectionContainer: {
226
- backgroundColor: "#fff",
144
+ backgroundColor: '#fff',
227
145
  paddingTop: SPACING.MD,
228
146
  },
229
147
  sectionPadding: {
@@ -233,98 +151,29 @@ const styles = StyleSheet.create({
233
151
  loadingPadding: {
234
152
  paddingHorizontal: SPACING.MD,
235
153
  },
236
- sectionHeading: {
237
- marginBottom: SPACING.XS,
238
- flexDirection: "row",
239
- alignContent: "flex-start",
240
- justifyContent: "space-between",
154
+ emptyPadding: {
155
+ paddingHorizontal: SPACING.MD,
241
156
  },
242
157
  sectionTitle: {
243
- fontFamily: "sf-bold",
158
+ fontFamily: 'sf-bold',
244
159
  fontSize: 24,
245
- color: "#000",
246
- },
247
- // Listing card styles
248
- cardContainer: {
249
- marginRight: SPACING.SM + 4,
250
- width: values.widget.itemMaxWidth,
251
- },
252
- borderContainer: {
253
- borderRadius: SPACING.SM,
254
- backgroundColor: "#fff",
255
- shadowColor: "#000",
256
- shadowOffset: {
257
- width: 0,
258
- height: 2,
259
- },
260
- shadowOpacity: 0.1,
261
- shadowRadius: 3.84,
262
- elevation: 4,
263
- padding: SPACING.SM,
264
- margin: SPACING.SM,
265
- minHeight: values.widget.itemMinWidth,
266
- },
267
- cardImage: {
268
- width: 88,
269
- height: 88,
270
- borderRadius: 44,
271
- backgroundColor: "#f0f0f0",
272
- alignSelf: "center",
273
- marginBottom: SPACING.XS,
274
- },
275
- placeholderImage: {
276
- justifyContent: "center",
277
- alignItems: "center",
278
- backgroundColor: "#f0f0f0",
279
- },
280
- cardTitle: {
281
- fontFamily: "sf-semibold",
282
- fontSize: 14,
283
- color: "#000",
284
- textAlign: "center",
285
- marginBottom: 4,
286
- },
287
- cardSummary: {
288
- fontFamily: "sf-regular",
289
- fontSize: 12,
290
- color: "#666",
291
- textAlign: "center",
292
- },
293
- // View more card
294
- viewMoreCard: {
295
- justifyContent: "center",
296
- },
297
- viewMoreContainer: {
298
- justifyContent: "center",
299
- alignItems: "center",
300
- minHeight: values.widget.itemMinWidth,
301
- },
302
- viewMoreText: {
303
- fontFamily: "sf-semibold",
304
- fontSize: 14,
305
- color: Colours.TEXT_DARK || "#000",
306
- },
307
- viewMoreCount: {
308
- fontFamily: "sf-regular",
309
- fontSize: 12,
310
- color: "#666",
311
- marginTop: 2,
160
+ color: '#000',
312
161
  },
313
162
  });
314
163
 
315
- const mapStateToProps = (state) => {
164
+ const mapStateToProps = state => {
316
165
  const { user, notifications } = state;
166
+ const listings = selectListings(state);
167
+ const featureDefinition = selectFeatureDefinition(state);
317
168
 
318
169
  return {
319
170
  colourBrandingMain: getMainBrandingColourFromState(state),
320
- listings: selectListings(state),
321
- featureDefinition: selectFeatureDefinition(state),
171
+ listings: listings,
172
+ featureDefinition: featureDefinition,
322
173
  site: user.site,
323
174
  dataUpdated: notifications.dataUpdated[values.notificationKey],
324
175
  strings: state.strings?.config || {},
325
176
  };
326
177
  };
327
178
 
328
- export default connect(mapStateToProps, null, null, { forwardRef: true })(
329
- WidgetSmall,
330
- );
179
+ export default connect(mapStateToProps, null, null, { forwardRef: true })(WidgetSmall);
@@ -107,6 +107,7 @@ const CondensedList = ({
107
107
  onRefresh,
108
108
  featureDefinition,
109
109
  title,
110
+ isWidget,
110
111
  }) => {
111
112
  const renderListingItem = ({ item: listing }) => {
112
113
  // Get feature definition for this listing
@@ -136,7 +137,7 @@ const CondensedList = ({
136
137
  if (onRefresh) {
137
138
  return (
138
139
  <View style={styles.container}>
139
- <Components.LoadingIndicator visible={refreshing} />
140
+ {!isWidget && <Components.LoadingIndicator visible={refreshing} />}
140
141
  <FlatList
141
142
  data={listings}
142
143
  keyExtractor={(item) => item.id}
@@ -31,6 +31,7 @@ const FeatureImageList = ({
31
31
  onRefresh,
32
32
  featureDefinition,
33
33
  title,
34
+ isWidget,
34
35
  }) => {
35
36
  const getImageSource = (imageField) => {
36
37
  if (!imageField) return null;
@@ -99,7 +100,7 @@ const FeatureImageList = ({
99
100
  if (onRefresh) {
100
101
  return (
101
102
  <View style={styles.container}>
102
- <Components.LoadingIndicator visible={refreshing} />
103
+ {!isWidget && <Components.LoadingIndicator visible={refreshing} />}
103
104
  <FlatList
104
105
  data={listings}
105
106
  keyExtractor={(item) => item.id}
@@ -26,6 +26,7 @@ const RoundImageList = ({
26
26
  onRefresh,
27
27
  title,
28
28
  featureDefinition,
29
+ isWidget,
29
30
  }) => {
30
31
  const getImageSource = (imageField) => {
31
32
  if (!imageField) {
@@ -102,7 +103,7 @@ const RoundImageList = ({
102
103
  if (onRefresh) {
103
104
  return (
104
105
  <View style={styles.container}>
105
- <Components.LoadingIndicator visible={refreshing} />
106
+ {!isWidget && <Components.LoadingIndicator visible={refreshing} />}
106
107
  <FlatList
107
108
  data={listings}
108
109
  keyExtractor={(item) => item.id}
@@ -26,6 +26,7 @@ const SquareImageList = ({
26
26
  onRefresh,
27
27
  title,
28
28
  featureDefinition,
29
+ isWidget,
29
30
  }) => {
30
31
  const getImageSource = (imageField) => {
31
32
  if (!imageField) return null;
@@ -100,7 +101,7 @@ const SquareImageList = ({
100
101
  if (onRefresh) {
101
102
  return (
102
103
  <View style={styles.container}>
103
- <Components.LoadingIndicator visible={refreshing} />
104
+ {!isWidget && <Components.LoadingIndicator visible={refreshing} />}
104
105
  <FlatList
105
106
  data={listings}
106
107
  keyExtractor={(item) => item.id}
@@ -121,7 +121,8 @@ const FeatureConfig = {
121
121
 
122
122
  afterStoreInit: (dispatch, getState) => {
123
123
  // Get the site from state (guaranteed to be available due to polling)
124
- const site = getState().user?.site;
124
+ const state = getState();
125
+ const site = state?.user?.site;
125
126
 
126
127
  if (site) {
127
128
  // Show loading state before fetching
package/src/index.js CHANGED
@@ -14,8 +14,8 @@ export const Screens = {
14
14
  };
15
15
 
16
16
  export { default as Config } from "./feature.config";
17
- export { default as WidgetSmall } from "./components/WidgetSmall";
18
- export { default as WidgetLarge } from "./components/WidgetLarge";
17
+ export { default as WidgetSmall } from "./components/Widget";
18
+ export { default as WidgetLarge } from "./components/Widget";
19
19
  export { default as WidgetGrid } from "./components/WidgetGrid";
20
20
 
21
21
  export { FeatureConfig };