diff --git a/app.json b/app.json index c15f0bb4..2da1417b 100644 --- a/app.json +++ b/app.json @@ -4,7 +4,7 @@ "description": "The Stanford Daily Mobile App", "slug": "StanfordDaily", "privacy": "public", - "version": "1.3.2", + "version": "1.4.0", "platforms": [ "ios", "android" @@ -31,14 +31,14 @@ ], "ios": { "bundleIdentifier": "com.Stanford.Daily.App", - "buildNumber": "14", + "buildNumber": "15", "appStoreUrl": "https://itunes.apple.com/app/stanford-daily/id1341270063", "supportsTablet": true }, "android": { "package": "com.Stanford.Daily.App", "googleServicesFile": "./google-services.json", - "versionCode": 14, + "versionCode": 15, "playStoreUrl": "https://play.google.com/store/apps/details?id=com.Stanford.Daily.App", "permissions": [ "ACCESS_COARSE_LOCATION", diff --git a/app/HTML.js b/app/HTML.js index 78e057dc..8f52d0ca 100644 --- a/app/HTML.js +++ b/app/HTML.js @@ -33,7 +33,7 @@ const renderers = { else if (typeof htmlAttribs.class !== 'undefined' && htmlAttribs.class.includes("wp-block-embed__wrapper") && typeof passProps.rawChildren[0].attribs.title !== 'undefined' && passProps.rawChildren[0].attribs.title.includes("Spotify Embed")) { // hacky way of displaying Spotify players for podcasts let uri = passProps.rawChildren[0].attribs.src; return ( - + ) } else { diff --git a/app/config/router.js b/app/config/router.js index e3e514a4..21553705 100644 --- a/app/config/router.js +++ b/app/config/router.js @@ -85,6 +85,7 @@ export const Tabs = createBottomTabNavigator({ inactiveTintColor: '#000000', inactiveBackgroundColor: 'white', activeBackgroundColor: 'white', + keyboardHidesTabBar: true, labelStyle: { fontFamily: "Open Sans", // FONTS.PT_SERIF marginBottom: labelBottomMargin @@ -98,7 +99,7 @@ export const Tabs = createBottomTabNavigator({ marginBottom: iphone_x ? -34 : 0 } }, - lazy: true + lazy: true, }); const Root = createStackNavigator({ diff --git a/app/helper/PushNotification.js b/app/helper/PushNotification.js index 46112df7..22c63105 100644 --- a/app/helper/PushNotification.js +++ b/app/helper/PushNotification.js @@ -7,7 +7,7 @@ export async function registerForPushNotificationsAsync() { Permissions.NOTIFICATIONS ); let finalStatus = existingStatus; - + // only ask if permissions have not already been determined, because // iOS won't necessarily prompt the user a second time. if (existingStatus !== 'granted') { diff --git a/app/screens/FollowInfoStorage.js b/app/screens/FollowInfoStorage.js index 97904cc0..958ba4d5 100644 --- a/app/screens/FollowInfoStorage.js +++ b/app/screens/FollowInfoStorage.js @@ -1,8 +1,25 @@ -import {AsyncStorage} from "react-native" +import { AsyncStorage, Linking, Alert } from "react-native" import { STRINGS } from '../assets/constants.js' import { Notifications } from "expo"; +import * as Permissions from 'expo-permissions'; export async function getToken() { + const { status: existingStatus } = await Permissions.getAsync( + Permissions.NOTIFICATIONS + ); + // if push notifications aren't on, alert user and give them the option to turn them on + if (existingStatus !== 'granted') { // code adapted from https://stackoverflow.com/questions/59980039/permissions-askasync-not-working-as-expected + Alert.alert( + "Please turn on notifications for this app in settings", + "", + [ + { text: "Cancel" }, + { text: "Settings", onPress: () => Linking.openURL("app-settings:") }, + ], + { cancelable: false } + ); + } + let token = await Notifications.getExpoPushTokenAsync(); if (__DEV__) { console.log(token); @@ -34,7 +51,7 @@ export async function followCategory(category_id) { } export async function unfollowCategory(category_id) { - let categories_followed = await getCategoriesFollowed(); + let categories_followed = await getCategoriesFollowed(); var index = categories_followed.indexOf(category_id); if (index !== -1) categories_followed.splice(index, 1); // remove category_id from list await updateBackend(); @@ -42,14 +59,14 @@ export async function unfollowCategory(category_id) { } export async function followAuthor(author_id) { - let authors_followed = await getAuthorsFollowed(); + let authors_followed = await getAuthorsFollowed(); authors_followed.push(author_id); // add author_id to authors_followed await updateBackend(); return await AsyncStorage.setItem('authors_followed', JSON.stringify(authors_followed)); } export async function unfollowAuthor(author_id) { - let authors_followed = await getAuthorsFollowed(); + let authors_followed = await getAuthorsFollowed(); var index = authors_followed.indexOf(author_id); if (index !== -1) authors_followed.splice(index, 1); // remove author_id from list await updateBackend(); @@ -57,16 +74,16 @@ export async function unfollowAuthor(author_id) { } export async function followLocation(location_id) { - let locations_followed = await getLocationsFollowed(); - locations_followed.push(location_id); + let locations_followed = await getLocationsFollowed(); + locations_followed.push(location_id); await updateBackend(); return await AsyncStorage.setItem('locations_followed', JSON.stringify(locations_followed)); } export async function unfollowLocation(location_id) { - let locations_followed = await getLocationsFollowed(); + let locations_followed = await getLocationsFollowed(); var index = locations_followed.indexOf(location_id); - if (index !== -1) locations_followed.splice(index, 1); + if (index !== -1) locations_followed.splice(index, 1); await updateBackend(); return await AsyncStorage.setItem('locations_followed', JSON.stringify(locations_followed)); } @@ -93,14 +110,14 @@ export async function isFollowingLocation(location_id) { } export async function addNotificationSetting(notification_id) { - let notification_settings = await getNotificationSettings(); - notification_settings.push(notification_id); + let notification_settings = await getNotificationSettings(); + notification_settings.push(notification_id); await updateBackend(); return await AsyncStorage.setItem('notification_settings', JSON.stringify(notification_settings)); } export async function removeNotificationSetting(notification_id) { - let notification_settings = await getNotificationSettings(); + let notification_settings = await getNotificationSettings(); var index = notification_settings.indexOf(notification_id); if (index !== -1) notification_settings.splice(index, 1); // remove notification option from list await updateBackend(); @@ -115,11 +132,11 @@ export async function isBeingNotified(notification_id) { } async function updateBackend() { - let response = await fetch(STRINGS.DAILY_URL + 'wp-json/tsd/v1/push-notification/users/' + await getToken(), { + let response = await fetch(STRINGS.DAILY_URL + 'wp-json/tsd/v1/push-notification/users/' + await getToken(), { // test notifications at "http://stanforddaily2.staging.wpengine.com/"" method: 'PUT', headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', + Accept: 'application/json', + 'Content-Type': 'application/json', }, body: JSON.stringify({ subscribing: { diff --git a/app/screens/Headlines.js b/app/screens/Headlines.js index bf2e6849..adcf2bbf 100644 --- a/app/screens/Headlines.js +++ b/app/screens/Headlines.js @@ -97,7 +97,7 @@ export default (props) => { color: COLORS.BLACK, marginLeft: MARGINS.ARTICLE_SIDES }}> - Settings + Notification Settings @@ -127,8 +127,9 @@ export default (props) => { const {posts} = await getCategoryAsync([category.slug], pageNumber); setArticles(posts); } + setRefreshing(false); })(); - }, [pageNumber, category]); + }, [pageNumber, category, refreshing]); return ( { onCloseStart={() => StatusBar.setHidden(false)} > - {/* setModalVisible(!modalVisible)} - />*/} + />
{ ref={listRef} removeClippedSubviews={false} disableVirtualization={true} - // refreshing={refreshing} + refreshing={refreshing} keyExtractor={item => `list-key-${item.id}`} - // onRefresh={() => setRefreshing(true)} + onRefresh={() => setRefreshing(true)} // onEndReached={() => setPageNumber(pageNumber + 1)} sections={[{ data: articles, key: `category-list-${pageNumber}-${category}` }]} renderItem={_renderRow} diff --git a/app/screens/Post.js b/app/screens/Post.js index c7b32bc4..14fbc1c9 100644 --- a/app/screens/Post.js +++ b/app/screens/Post.js @@ -111,7 +111,7 @@ class Post extends Component { tagsStyles={{ p: { marginBottom: MARGINS.ARTICLE_SIDES }, a: { color: COLORS.CARDINAL }, - strong: { fontFamily: FONTS.PT_SERIF_BOLD }, + strong: { fontFamily: FONTS.PT_SERIF_BOLD }, em: { fontFamily: FONTS.PT_SERIF_ITALIC }, img: { marginHorizontal: -1 * MARGINS.ARTICLE_SIDES }, figure: { marginVertical: MARGINS.ARTICLE_SIDES }, diff --git a/app/screens/SettingsPage.js b/app/screens/SettingsPage.js index b3712208..36f8f3d0 100644 --- a/app/screens/SettingsPage.js +++ b/app/screens/SettingsPage.js @@ -1,21 +1,24 @@ -import {STRINGS, CATEGORIES, REFS, KEYS, ALIGNMENTS, FONTS, COLORS, PN_RECEIVER_GROUPS} from '../assets/constants.js'; -import React, {Component} from 'react'; -import {Image} from 'react-native'; +import { STRINGS, CATEGORIES, REFS, KEYS, ALIGNMENTS, MARGINS, FONTS, FONT_SIZES, COLORS, PN_RECEIVER_GROUPS } from '../assets/constants.js'; +import React, { Component } from 'react'; +import { Ionicons } from '@expo/vector-icons'; import Modal from "react-native-modal" import ToggleSwitch from 'toggle-switch-react-native' import { - Alert, - View, - Text, - Dimensions, - RefreshControl, - StatusBar, - ActivityIndicator, - NetInfo, - FlatList, - TouchableOpacity, - TouchableHighlight, - SectionList + Alert, + Image, + Platform, + View, + Text, + Dimensions, + RefreshControl, + StatusBar, + ActivityIndicator, + NetInfo, + FlatList, + TouchableOpacity, + TouchableHighlight, + Switch, + SectionList } from 'react-native'; import _ from 'lodash'; @@ -25,176 +28,190 @@ import { isBeingNotified, addNotificationSetting, removeNotificationSetting } fr const amplitude = Amplitude.initialize(KEYS.AMPLITUDE_API); //A map between categories names and their codes -const {width, height} = Dimensions.get('window'); +const { width, height } = Dimensions.get('window'); var selectedCategory = STRINGS.FEATURED_HEADLINES; //The currently selected category const styles = { - listItem: { flex: 1, maxHeight: 60, flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: 'grey'} + listItem: { + flex: 1, + maxHeight: 72, + flexDirection: 'row', + alignItems: 'center', + borderBottomWidth: 1, + borderBottomColor: COLORS.LIGHT_GRAY + }, + notifHeader: { + fontSize: FONT_SIZES.DEFAULT_MEDIUM, + fontFamily: FONTS.OPEN_SANS_BOLD + }, + notifText: { + fontSize: 13, + fontFamily: FONTS.OPEN_SANS + } } export default class SettingsPage extends Component { - constructor(props) { - super(props); - this.state = { - isOn: { - [PN_RECEIVER_GROUPS.BREAKING]: false, - [PN_RECEIVER_GROUPS.DAILY]: false, - [PN_RECEIVER_GROUPS.WEEKLY]: false - } - }; - } - - async componentDidMount() { - this.updateNotificationSettings(); - } - - async updateNotificationSettings() { - this.setState({ - isOn: { - [PN_RECEIVER_GROUPS.BREAKING]: await isBeingNotified(PN_RECEIVER_GROUPS.BREAKING), - [PN_RECEIVER_GROUPS.DAILY]: await isBeingNotified(PN_RECEIVER_GROUPS.DAILY), - [PN_RECEIVER_GROUPS.WEEKLY]: await isBeingNotified(PN_RECEIVER_GROUPS.WEEKLY) - } - }); - } - - async toggleNotificationSetting(name) { - if (this.state.isOn[name]) { - await removeNotificationSetting(name); + constructor(props) { + super(props); + this.state = { + isOn: { + [PN_RECEIVER_GROUPS.BREAKING]: false, + [PN_RECEIVER_GROUPS.DAILY]: false, + [PN_RECEIVER_GROUPS.WEEKLY]: false } - else { - await addNotificationSetting(name); + }; + } + + async componentDidMount() { + this.updateNotificationSettings(); + } + + async updateNotificationSettings() { + this.setState({ + isOn: { + [PN_RECEIVER_GROUPS.BREAKING]: await isBeingNotified(PN_RECEIVER_GROUPS.BREAKING), + [PN_RECEIVER_GROUPS.DAILY]: await isBeingNotified(PN_RECEIVER_GROUPS.DAILY), + [PN_RECEIVER_GROUPS.WEEKLY]: await isBeingNotified(PN_RECEIVER_GROUPS.WEEKLY) } - await this.updateNotificationSettings(); - } + }); + } - ToggleSwitch = ({receiverGroup}) => { - return { + return { this.toggleNotificationSetting(receiverGroup) }} + style={{ transform: [{ scaleX: .8 }, { scaleY: .8 }] }} + /> + {/* this.toggleNotificationSetting(receiverGroup) } - /> - } - - render() { - return ( - - - {/* Header */} - - - Notifications + onToggle={ () => {this.toggleNotificationSetting(receiverGroup)} } + />*/} + + } + + render() { + return ( + + + {/* Header */} + + + Notifications + + How often do you want to hear from The Daily? + + + + + + + + - How often do you want to hear from The Daily? + + Breaking News + Important stories, as they happen + + + + - - - - - - - - - Breaking News - Important stories, as they happen - - - - - - + + + - - - - - - - Every day - Daily news roundup - - - - - - + + Every day + Daily news roundup - - - - - - - Every week - Weekend roundup - + + + + - - - + + + + + + Every week + Weekend roundup + + + + - - {this.props.setModalVisible(!this.props.modalVisible); - - }}> - Close - - - - ) - } + + + { + this.props.setModalVisible(!this.props.modalVisible); + }}> + Close + + + + ) + } } diff --git a/app/screens/styles/header.js b/app/screens/styles/header.js index 60fd678b..5832ff56 100755 --- a/app/screens/styles/header.js +++ b/app/screens/styles/header.js @@ -28,7 +28,7 @@ const styles = StyleSheet.create({ }, wordsTitle: { color: COLORS.BLACK, - fontFamily: FONTS.OPEN_SANS_BOLD, + fontFamily: FONTS.PT_SERIF_BOLD, fontSize: FONT_SIZES.DEFAULT_SMALL_MEDIUM, textAlign: ALIGNMENTS.CENTER }, diff --git a/app/screens/styles/headlines.js b/app/screens/styles/headlines.js index c3ea26a1..b024689c 100755 --- a/app/screens/styles/headlines.js +++ b/app/screens/styles/headlines.js @@ -32,7 +32,7 @@ const styles= StyleSheet.create({ }, sideBarTitleText: { color: COLORS.BLACK, - fontFamily: FONTS.OPEN_SANS_BOLD, + fontFamily: FONTS.PT_SERIF_BOLD, fontSize: FONT_SIZES.DEFAULT_SMALL_MEDIUM, //textAlign: 'center', flex: 1, diff --git a/app/screens/styles/newsfeeditem.js b/app/screens/styles/newsfeeditem.js index 4ab17b64..54c58542 100755 --- a/app/screens/styles/newsfeeditem.js +++ b/app/screens/styles/newsfeeditem.js @@ -12,6 +12,7 @@ const styles = ({ }, dateAndAuthor: { flexDirection: ALIGNMENTS.ROW, + flexWrap: 'wrap', justifyContent: ALIGNMENTS.SPACE_BETWEEN, marginTop: MARGINS.DEFAULT_MARGIN, marginHorizontal: MARGINS.ARTICLE_SIDES,