diff --git a/App.js b/App.js index 55f4ab2e..70a04e4d 100644 --- a/App.js +++ b/App.js @@ -1,18 +1,30 @@ import React, { useState, useEffect, useRef } from "react"; -import { View, Text } from "react-native"; -import 'react-native-gesture-handler'; -import { StatusBar } from 'expo-status-bar'; -import { SafeAreaProvider } from 'react-native-safe-area-context'; +import { Image } from "react-native"; +import { StatusBar } from "expo-status-bar"; +import { SafeAreaProvider } from "react-native-safe-area-context"; import Navigation, { navigate } from "./navigation"; -import * as Font from 'expo-font'; -import * as Device from 'expo-device' -import * as Notifications from 'expo-notifications' +import * as Font from "expo-font"; +import * as Device from "expo-device" +import * as Notifications from "expo-notifications" import { initializeApp } from "firebase/app"; -import { getDatabase, ref, push, set } from 'firebase/database' -import { getAuth, signInWithCustomToken, signInWithEmailAndPassword } from 'firebase/auth' -import { APIKEY, MESSAGING_SENDER_ID, APP_ID, MEASUREMENT_ID, FIREBASE_PASSWORD, SERVICE_ACCOUNT_ID } from '@env' -import { getPostAsync } from './helpers/wpapi' -import { Strings } from './constants' +import { getDatabase, ref, push, set } from "firebase/database" +import { getAuth, signInWithEmailAndPassword } from "firebase/auth" +import { APIKEY, MESSAGING_SENDER_ID, APP_ID, MEASUREMENT_ID, FIREBASE_PASSWORD, SERVICE_ACCOUNT_ID } from "@env" +import { getPostAsync } from "./helpers/wpapi" +import { Strings } from "./constants" +import * as eva from "@eva-design/eva"; +import { ApplicationProvider, IconRegistry, useTheme } from "@ui-kitten/components"; +import { EvaIconsPack } from "@ui-kitten/eva-icons"; +import { DailyBread as bread } from "./theme" +import { default as mapping } from "./mapping.json" +import { NavigationContainer } from "@react-navigation/native"; +import { createStackNavigator } from "@react-navigation/stack"; +import ContentStack from "./navigation/ContentStack"; +import SearchStack from "./navigation/SearchStack"; +import Post from "./screens/Post"; +import Home from "./screens/Home"; +import Section from "./screens/Section"; +import { ThemeContext } from "./theme-context"; Notifications.setNotificationHandler({ handleNotification: async () => ({ @@ -23,8 +35,6 @@ Notifications.setNotificationHandler({ }); const firebaseConfig = { - // This needs to be updated/redacted for security reasons. - // Planning to use an environment variable. apiKey: APIKEY, authDomain: "daily-mobile-app-notifications.firebaseapp.com", databaseURL: "https://daily-mobile-app-notifications-default-rtdb.firebaseio.com", @@ -36,32 +46,68 @@ const firebaseConfig = { serviceAccountId: SERVICE_ACCOUNT_ID }; +const cardinalLogo = require("./assets/media/DailyLogoCardinal.png") +const whiteLogo = require("./assets/media/DailyLogoWhite.png") +const Stack = createStackNavigator() export default function App() { - // const isLoadingComplete = useLoadedAssets(); - // const colorScheme = useColorScheme(); - const [fontsLoaded, setFontsLoaded] = useState(false) - const [expoPushToken, setExpoPushToken] = useState(''); + const [expoPushToken, setExpoPushToken] = useState(""); const [notification, setNotification] = useState(false); const notificationListener = useRef(); const responseListener = useRef(); + // const isLoadingComplete = useLoadedAssets(); + // const colorScheme = useColorScheme(); + const [theme, setTheme] = useState("light") + + const toggleTheme = () => { + const next = theme === "light" ? "dark" : "light" + setTheme(next) + } + + const headerOptions = { + headerTitle: () => (), + headerStyle: { + backgroundColor: bread[theme]["background-basic-color-1"] + }, + headerTintColor: eva[theme]["color-primary-500"] + } + + const detailHeaderOptions = { + headerTitle: "", + headerTransparent: true, + headerTintColor: "white", + headerBackTitleVisible: false + } + + const sectionHeaderOptions = { + headerStyle: { + backgroundColor: bread[theme]["background-basic-color-1"] + }, + headerTintColor: bread[theme]["color-primary-500"], + headerTitleStyle: { + color: eva[theme][theme === "light" ? "color-basic-800" : "color-basic-100"] + } + } useEffect(() => { Font.loadAsync({ // Loads fonts from static resource. - MinionProDisp: require('./assets/fonts/Minion_Pro/MinionPro-Disp.ttf'), - MinionProRegular: require('./assets/fonts/Minion_Pro/MinionPro-Regular.ttf'), - MinionProItDisp: require('./assets/fonts/Minion_Pro/MinionPro-ItDisp.ttf'), - MinionProBoldDisp: require('./assets/fonts/Minion_Pro/MinionPro-BoldDisp.ttf'), - MinionProBoldItDisp: require('./assets/fonts/Minion_Pro/MinionPro-BoldItDisp.ttf'), - MinionProMediumDisp: require('./assets/fonts/Minion_Pro/MinionPro-MediumDisp.ttf'), - MinionProMediumItDisp: require('./assets/fonts/Minion_Pro/MinionPro-MediumItDisp.ttf'), - MinionProSemiboldDisp: require('./assets/fonts/Minion_Pro/MinionPro-SemiboldDisp.ttf'), - MinionProSemiboldItDisp: require('./assets/fonts/Minion_Pro/MinionPro-SemiboldItDisp.ttf'), - LibreFranklinRegular: require('./assets/fonts/Libre_Franklin/LibreFranklin-Regular.ttf'), - LibreFranklinBold: require('./assets/fonts/Libre_Franklin/LibreFranklin-Bold.ttf'), - LibreFranklinItalic: require('./assets/fonts/Libre_Franklin/LibreFranklin-Italic.ttf'), + MinionProDisp: require("./assets/fonts/Minion_Pro/MinionPro-Disp.ttf"), + MinionProRegular: require("./assets/fonts/Minion_Pro/MinionPro-Regular.ttf"), + MinionProItDisp: require("./assets/fonts/Minion_Pro/MinionPro-ItDisp.ttf"), + MinionProBoldDisp: require("./assets/fonts/Minion_Pro/MinionPro-BoldDisp.ttf"), + MinionProBoldItDisp: require("./assets/fonts/Minion_Pro/MinionPro-BoldItDisp.ttf"), + MinionProMediumDisp: require("./assets/fonts/Minion_Pro/MinionPro-MediumDisp.ttf"), + MinionProMediumItDisp: require("./assets/fonts/Minion_Pro/MinionPro-MediumItDisp.ttf"), + MinionProSemiboldDisp: require("./assets/fonts/Minion_Pro/MinionPro-SemiboldDisp.ttf"), + MinionProSemiboldItDisp: require("./assets/fonts/Minion_Pro/MinionPro-SemiboldItDisp.ttf"), + LibreFranklinRegular: require("./assets/fonts/Libre_Franklin/LibreFranklin-Regular.ttf"), + LibreFranklinBold: require("./assets/fonts/Libre_Franklin/LibreFranklin-Bold.ttf"), + LibreFranklinItalic: require("./assets/fonts/Libre_Franklin/LibreFranklin-Italic.ttf"), }).then(setFontsLoaded(true)); registerForPushNotificationsAsync().then(token => { setExpoPushToken(token) @@ -77,7 +123,7 @@ export default function App() { const tokenRef = ref(db, "ExpoPushTokens/" + submatch, userCredential) set(tokenRef, Date()) }).catch((error) => { - console.log("error with signing in: ", error) + console.log("Could not sign in: ", error) }) } } @@ -101,41 +147,54 @@ export default function App() { }; }, []); - return ( - - - ) - + return (fontsLoaded && + + + + + + + + + + ({ title: route.params.category.name, ...sectionHeaderOptions })}/> + + + + + + ) } async function registerForPushNotificationsAsync() { - let token; if (Device.isDevice) { const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; - if (existingStatus !== 'granted') { + if (existingStatus !== "granted") { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } - if (finalStatus !== 'granted') { - alert('Failed to get push token for push notification!'); + if (finalStatus !== "granted") { + alert("Failed to get push token for push notification!"); return; } token = (await Notifications.getExpoPushTokenAsync()).data; } else { - alert('Must use physical device for Push Notifications'); + alert("Must use physical device for Push Notifications"); } - if (Platform.OS === 'android') { - Notifications.setNotificationChannelAsync('default', { - name: 'default', + if (Platform.OS === "android") { + Notifications.setNotificationChannelAsync("default", { + name: "default", importance: Notifications.AndroidImportance.MAX, vibrationPattern: [0, 250, 250, 250], - lightColor: '#FF231F7C', + lightColor: "#FF231F7C", }); } return token; - } \ No newline at end of file diff --git a/Model.js b/Model.js new file mode 100644 index 00000000..5611fd58 --- /dev/null +++ b/Model.js @@ -0,0 +1,3 @@ +var WPAPI = require("wpapi") +var wp = new WPAPI({ endpoint: "https://stanforddaily.com/wp-json" }) +export default wp \ No newline at end of file diff --git a/README.md b/README.md index 1612aaf4..ada53256 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ Supports Expo Android + + + Runs with Expo +

Official mobile app of the independent, student-run newspaper of Stanford University. Contributions are welcome! diff --git a/components/CardRow.js b/components/CardRow.js deleted file mode 100644 index 1569d86e..00000000 --- a/components/CardRow.js +++ /dev/null @@ -1,155 +0,0 @@ -import React, { Component, useRef } from 'react'; -import { View, Dimensions, FlatList, StyleSheet } from 'react-native'; -import { Alignments, Fonts, FontSizes, Margins } from '../constants'; -import "moment-timezone"; -import _ from "lodash"; - -const { width, height } = Dimensions.get('window'); - -export default class CardRow extends Component { - - constructor(props) { - super(props) - this.state = { - scrollPosition: 0 - } - } - - render() { - const { data, renderItem, title, onPress } = this.props; - - return ( - - {this.setState({scrollPosition: e.nativeEvent.contentOffset.x})}} - /> - - ); - } -} - -const styles = ({ - content: { - width: '100%', - paddingVertical: 2, - backgroundColor: "rgb(0,0,0,0)", - marginLeft: 0, - marginRight: 0 - }, - categoryLabel: { - flexDirection: 'row', - justifyContent: 'space-between', - // width: width - (2 * Margins.default) - }, - dateAndAuthor: { - flexDirection: Alignments.column, - justifyContent: Alignments.spaceBetween, - marginTop: Margins.default, - marginHorizontal: Margins.articleSides, - }, - - author: { - fontFamily: "MinionProDisp", - fontSize: 10, - marginLeft: -2, - // color: THEME.SECONDARY_LABEL, - }, - - date: { - fontFamily: "MinionProDisp", - fontSize: 10, - // color: THEME.SECONDARY_LABEL, - }, - - header: { - fontFamily: "MinionProDisp", - fontSize: FontSizes.large + 10, - // color: THEME.LABEL - }, - - titleFont: { - fontFamily: "MinionProDisp", - fontSize: FontSizes.mediumSmall, - // color: THEME.LABEL - }, - titleContainer: { - marginTop: Margins.defaultSmall, - marginLeft: Margins.articleSides, - width: width/2.25 - Margins.articleSides - Margins.defaultLarge - }, - - descriptionContainer: { - opacity: 0.80, - marginHorizontal: Margins.articleSides - }, - descriptionFont: { - fontSize: FontSizes.defaultMediumSmall - }, - - image: { - marginBottom: Margins.default, - width: width/2.25 - Margins.articleSides - Margins.defaultLarge, - marginLeft: Margins.articleSides, - marginRight: 0, - height: 3/4 * width/2.2 - }, - - imageContainer: { - - }, - searchContainer: { - // borderBottomColor: COLORS.SECONDARY_LABEL, - borderBottomWidth: 1, - // backgroundColor: THEME.BACKGROUND, - width: '100%', - flexDirection: Alignments.row, - maxHeight: 122 - }, - searchContent: { - flexDirection: Alignments.column, - width: width - 120, - marginLeft: Margins.default, - marginRight: Margins.default, - }, - searchImage: { - width: 120, - height: 120, - }, - searchDateAndAuthor: { - flexDirection: Alignments.row, - justifyContent: Alignments.spaceBetween, - marginTop: 2, - }, - searchTitle: { - fontFamily: "MinionProDisp", - fontSize: 14, - marginTop: 2, - }, - searchDescription: { - fontFamily: "MinionProDisp", - fontSize: 12, - marginTop: 2, - opacity: 0.80, - }, - more: { - // backgroundColor: THEME.BUTTON, - marginTop: Margins.defaultSmall, - marginHorizontal: Margins.articleSides, - justifyContent: 'center', - borderRadius: 10 - }, - seeAll: { - paddingHorizontal: 15, - fontFamily: "MinionProDisp", - // color: THEME.LABEL - } -}) \ No newline at end of file diff --git a/components/Carousel.js b/components/Carousel.js new file mode 100644 index 00000000..ec08155a --- /dev/null +++ b/components/Carousel.js @@ -0,0 +1,106 @@ +import React from "react" +import { Card, Button, Layout, Text, useTheme } from "@ui-kitten/components" +import { Image, View, StyleSheet } from "react-native" +import PagerView from "react-native-pager-view" +import moment from "moment" +import _ from "lodash" +import { decode } from "html-entities" +import { Sections } from "../constants" + +// Going to rename to Carousel. +export default function Carousel(props) { + const theme = useTheme() + const { navigation, articles } = props + + const Header = (props) => ( + + + + ) + + const Footer = (props) => ( + + {"Scoop Scooperstein".toUpperCase()} + + + ) + + const Slide = (props) => ( + + ) + + const ObliqueSlide = (props) => ( + + ) + + const largestImageAvailable = (details) => { + return _.maxBy(details.media_details.sizes, "width").source_url + } + + return ( + + {articles.map((item, index) => { + return ( + + } + footer={