-
Notifications
You must be signed in to change notification settings - Fork 4
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
Added store hours and store details #96
Conversation
… card for some reason
LOL Just remembered I'm using the wrong-facing phone icon... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
THIS IS SO GREAT! The comments you left are SUPER helpful for documentation and helping me figure out how it all works. I really appreciate that you took the time to add those!
The Open Now
isn't working for me right now...I noted why I think this might be happening but it's very strange. Besides that, I noted a few ways we can maybe accommodate some different input styles/edge cases (better for handoff) + cleanup. Great work Thu 😍 !
screens/map/MapScreen.js
Outdated
@@ -197,7 +191,13 @@ export default class MapScreen extends React.Component { | |||
|
|||
renderContent = () => { | |||
return ( | |||
<BottomSheetContainer> | |||
<TouchableOpacity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now the whole map drawer becomes the button -- probably add this touchable opacity to StoreCard
instead (so it also works from StoreListScreen
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/mapUtils.js
Outdated
} | ||
|
||
const convertedOpeningTime = openingHour * 60 + openingMin; | ||
const convertedClosingTime = closingHour * 60 + closingMin; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I added parseInt
for min, it works well!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah shoot I forgot the minutes were strings RIP thanks for catching that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh also the issue was that some of the times don't have minutes so I didn't check for minutes before converting something that didn't exist! So that's why it's NaN but that's fixed now tyty
@@ -50,6 +51,7 @@ export default function StoresStackNavigator() { | |||
}, | |||
}} | |||
/> | |||
<StoresStack.Screen name="StoreScreen" component={StoreScreen} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm could you rename this to StoreDetailsScreen
for consistency with ProductDetailsScreen
? StoreScreen
could maybe get confused with StoreListScreen
.
screens/map/StoreScreen.js
Outdated
</NavButton> | ||
<NavTitle>{store.storeName}</NavTitle> | ||
</NavHeaderContainer> | ||
<View style={{ marginLeft: 16, marginTop: 30 }}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make this a ScrollView
? The size is fine not scrolling on a larger phone, but it would get cut off on smaller devices.
components/store/StoreCard.js
Outdated
<StoreDetailText>{storeHours}</StoreDetailText> | ||
<StoreDetailText | ||
color={ | ||
storeOpenStatus === 'Open 24/7' ? Colors.primaryGreen : Colors.black |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I was also having this issue with the 'Earn and redeem Healthy Rewards here' line, so I added a prop greenText
to StoreDetailText
(see line 114), so you can probably just move this logic into something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And probably change to a .includes('Open')
lib/mapUtils.js
Outdated
const closingMin = closingHourMinSplit[1]; | ||
|
||
// Convert to 24 hour time | ||
if (openingSuffix === 'pm') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General comment: We'll definitely give the NPO a strict guide on how they have to input hours data into Airtable, but when possible, we should try to leave a bit of room for error. As you noticed when pulling the phone numbers from the original sheet, the data was quite messy and inconsistent and I had to clean everything up first.
In places like this, could you try to make things like this not case sensitive (like maybe add a toLowerCase
toopeningSuffix
in case they try to enter things with uppercase PM)? Maybe same for the abbreviations in daysOfTheWeek
.
Also, the original spacing format they gave us was very inconsistent (some with spaces before hyphens, some with spaces after, some both/neither etc). To correct for this, could you add something like hours.replace('- ', '-').replace(' -', '-')
to get rid of spaces around hyphens in both hours and days before you even try to parse it to make sure it doesn't break? (also there might be a cleaner way to do this than replace)
I know these seem like small details, but if it takes a few small changes to avoid NPO user error causing crashes, let's try to make their experiences easier whenever we can : )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the airtable it looked like the hours were inputted in the form of:
7:30am-10pm Daily
7am-10pm Mon-Sat, 9am-9pm Sun
Would it be safe for me to assume that it would follow the timeInterval Day
format (regardless of weird spacing issues)? I haven't accounted for the ones you linked in the picture
screens/map/StoreScreen.js
Outdated
</TouchableOpacity> | ||
{/* Phone Number */} | ||
<InLineContainer style={{ alignItems: 'center', paddingBottom: 32 }}> | ||
<FontAwesome5 name="phone" solid size={24} color={Colors.black} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change all instances of Colors.black
to activeText
components/store/StoreHours.js
Outdated
return ( | ||
<View style={{ display: 'flex', flexDirection: 'row' }}> | ||
{/* Days of the week */} | ||
<View style={{ width: '50%' }}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Maybe adjust this 50-50 ratio to more 40-60 just to make sure we don't get weird wrapping/truncating edge cases on narrower screens.
components/store/StoreHours.js
Outdated
</View> | ||
<View style={{ width: '50%' }}> | ||
{daysOfTheWeekFull.map((day, _) => { | ||
if (day == todaysDay) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should make sure all of these are ===
instead of ==
bc eslint haha
lib/mapUtils.js
Outdated
@@ -43,3 +43,156 @@ export async function getProductData(store) { | |||
export const getMaxWidth = function sync(screenWidth, ebt, seeProducts) { | |||
return screenWidth - 32 - 12 + (ebt ? -48 : 0) + (seeProducts ? -130 : 0); | |||
}; | |||
|
|||
export function constructHoursDict(hours) { | |||
const daysOfTheWeek = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if we should consolidate some of these things into a file in constants? Lots of repeated code between these different functions.
…d for those that are not provided
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SO SORRY here are some more fun edge cases :')
components/store/StoreHours.js
Outdated
|
||
const parseHours = () => { | ||
// Case: hours = "Open 24/7" | ||
if (hours === 'Open 24/7' || hours === 'Store hours unavailable') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm then I'm not sure why it isn't working? When I make a field in Airtable blank and tap on that store on the map, it breaks.
I think it's bc parseHours isn't called when trying to find if the store is currently open. You'd also need to check when computeStoreOpen
is called if hours
is blank or somehow invalid. I would suggest maybe puttting a try/catch around the whole computeStoreOpen
for any parsing/date issues, and returning 'Store hours unavailable' in the catch.
lib/mapUtils.js
Outdated
let tmrwDay = daysOfTheWeekFull[tmrwDayIndex]; | ||
let tmrwStoreHours = hoursPerDayDict[tmrwDay]; | ||
// Loop until next open day is found. | ||
while (tmrwStoreHours === 'Closed') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NICE that you catch this! I was expecting this edge case 9am-7pm Sun, 9am-8pm Tu-Thurs, Closed Fri, Closed Sat
to break but that's very cool that it works! Still, I'd probably rename this to be more specific like next opening day bc 'tmrw' is a bit deceiving.
components/store/StoreCard.js
Outdated
@@ -55,89 +64,108 @@ export default function StoreCard({ | |||
}); | |||
}; | |||
|
|||
const storeOpenStatus = computeStoreOpen(store.storeHours); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm getting REALLY SLOW loading! Is there a way we can make this only call once? It seems to be called with every movement on the map and gets REALLY REALLY slow on the store list screen. When I type to search, it's so slow that it doesn't even respond at all. It gets to a point where I can't load all the stores on storelist and then it fully stops responding altogether like I can't tap map icons or close the Find a Store page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah good point! I removed one call in the StoreDetailsScreen
and pass the summary line in as a prop from StoreCard
. Hopefully this helps!
components/store/StoreCard.js
Outdated
{`${distance} miles away`} | ||
</Caption> | ||
)} | ||
<TouchableHighlight |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok sorry this totally my bad this was a bad suggestion. This turns black on clicking 😅 TouchableOpacity would work just fine instead since it's on StoreCard and not transparent to the whole mapscreen now 😂
lib/mapUtils.js
Outdated
@@ -155,7 +152,8 @@ function computeNextOpenDay(todaysDayIndex, hoursPerDayDict) { | |||
return 'Closed until ' | |||
.concat(tmrwDay) | |||
.concat(' ') | |||
.concat(tmrwOpeningTime); | |||
.concat(tmrwOpeningTime) | |||
.replace('12am', 'Midnight'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm p sure you actually wouldn't need this here or in 'Open now until' below bc doing this in the final StoreHours
does the final change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I added it there because I don't display StoreHours
on the StoreCard
(this method primarily gives you the summary, aka "Open 24/7" or whatever, and StoreHours
gives you the whole schedule)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small details I forgot to submit
if (hoursStrSplit.length === 2 && hoursStrSplit.includes('Daily')) { | ||
const time = hoursStrSplit[0]; | ||
|
||
daysOfTheWeekFull.map((day, _) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WRT the 'Each child in an array should have a unique "key" prop.' issue, could you set key={day}
since that should (hopefully) always be unique from the dictionary? Or you could do this like renderProductList
in ProductsScreen
but eslint will yell at you for Do not use Array index in keys
return products.map((product, i) => {
return (
<ProductCard
key={i}
product={product}
navigation={this.props.navigation}
store={store}
displayPoints
/>
);
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added keys to all the component children of the loop in StoreHours
and it seems to get rid of the warning so we good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GREAT WORK THU thank you for all of your time and energy on this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh damn this is such a great PR Thu! Really helpful documentation and very clean/nice code to read! Left literally 2 nits/question that I don't think it's crucial to address in this PR but probably nice – otherwise good to go! Awesome!
<TouchableOpacity style={{ paddingBottom: 32 }}> | ||
<InLineContainer style={{ alignItems: 'center' }}> | ||
<FontAwesome5 | ||
name="directions" | ||
size={24} | ||
color={Colors.activeText} | ||
/> | ||
<Body style={{ marginLeft: 12 }}>{store.address}</Body> | ||
</InLineContainer> | ||
</TouchableOpacity> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a functionality that is associated with this TouchableOpacity
? Best guess is that it should mimic that of what happens when you do it for the BottomSheet, but it's doing nothing for now.
return hoursList(hoursPerDayDict); | ||
} | ||
console.log( | ||
'[Storehours] parseHours: Issue parsing store hours. Hours were: ', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: StoreHours
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
amazing @thumn !! I'm not sure on the React details but if things are still kind of slow, I would guess that you don't want StoreHours
to be a functional component, and instead have it maintain its state so it doesn't re-parse/construct the dictionary every time.
Another alternative is to parse the hours in lib/mapUtils
's updateStoreData
function, and set a new property that is an enum telling you what category of 'hours' to show the parsed hours is (seems like it's something like 24/7, daily, or other?). Then you can pass the flag / enum property, check that, and then manipulate the store.hours
property as needed (at that point it could be a string or a dictionary).
Also, a note for the future @wangannie : for deciding whether to extract constants/etc to files, it’s usually fine to keep it in the lib/_utils
file unless the constants are used independent of the lib/_utils
; otherwise it’s just always gonna be two imports: one from the constants/file
and one from the lib/_utils
file.
Another reason is that constants are hardcoded values that might be updated (e.g rewardPointValue
)
From what I can tell it wasn't strictly necessary to bring the days of the week out but it's fine either way LOL
Getting this in the StoreList sometimes
|
What's new in this PR
Relevant Links
N/A
Online sources
Date
Related PRs
N/A
How to review
Navigate through stores and make sure each store's card can successfully navigate to its corresponding store screen. The current day should be correctly bolded, and the store open status summary should correctly reflect whether the store is open or when it'll next be open. For example, Yang Market has a particular complicated store hours input.
Next steps
Start work on tags for accepted programs in the stores list. This will also be applied to the store screens.
NOTE: I'm getting a "each child in a list should have a unique 'key' prop" error -- does anyone know how to fix this? The file is
StoreHours.js
. I also can't figure out how to make the<StoreDetailText>
primary green on theStoreCard
, but it works fine on the store screen. (Might be passing in the wrong prop, but the component seems to take incolor
as a prop so I'm not too sure.)Also, I sometimes get a weird "store is undefined" error on
MapScreen
that goes away if I reload the app. Sadly this might end up circling back to theuseFocusEffect
hook issue/refactoring task we set aside :(Tests Performed, Edge Cases
Tested with hard-coded store hour inputs:
9am-8pm Tu-Sat, 9am-7pm Sun, Closed Mon
8am-7pm Daily
Open 24/7
7am-Midnight Daily
Also manually compared hours between Stores table in the Airtable and the ones that showed up in the store screens. I looked up the more edge-case-y stores and compared the info on the store card and corresponding store screen. (e.g. Yang Market, Mellon Market, T & G Grocery, Smiley Mart).
Screenshots
CC: @tommypoa @wangannie