Skip to content

Commit

Permalink
Merge pull request #199 from cuappdev/main-copy
Browse files Browse the repository at this point in the history
Merge Main to Release
  • Loading branch information
rs929 authored May 23, 2024
2 parents ef1e414 + 5e6ada3 commit b4718f0
Show file tree
Hide file tree
Showing 79 changed files with 1,872 additions and 1,601 deletions.
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
.env
**/.env*
**/.env*.
**/google-services.json
**/GoogleService-Info.plist
config/google-services.json
**/resell-service-account-key.json

# Xcode
#
Expand Down Expand Up @@ -127,9 +128,5 @@ buck-out/
web-build/
dist/

# Google services
config/google-services.json
**/GoogleService-Info.plist

# @end expo-cli
package-lock.json
7 changes: 3 additions & 4 deletions App.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ApiClientProvider from "./api/ApiClientProvider";
import useCachedResources from "./hooks/useCachedResources";
import SignIn from "./screens/SignIn";
import { store } from "./state_manage/reducers/store";
import { getDeviceFCMToken, requestUserPermission } from "./api/FirebaseNotificationManager";
import { getDeviceFCMToken } from "./api/FirebaseNotificationManager";

export default function App() {
function handleDeepLink(event) {
Expand All @@ -17,9 +17,8 @@ export default function App() {
const [postID, setPostID] = useState(null);

useEffect(() => {
requestUserPermission()
getDeviceFCMToken()
})
getDeviceFCMToken();
});

useEffect(() => {
async function getInitialURL() {
Expand Down
95 changes: 69 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,84 @@
# Resell for iOS & Android
# Resell - Cornell Marketplace

<p align="center"><img src="https://github.com/cuappdev/assets/blob/master/app-icons/resell-icon.png" width=210 /></p>

> Resell is an app that collects, filters, and compares different items that people want to resell in order to connect sellers with buyers and to facilitate resource utilization.
Resell is an app that collects, filters, and compares different items that people want to resell in order to connect sellers with buyers and to facilitate resource utilization. Resell is one of the latest apps by [Cornell AppDev](http://cornellappdev.com), an engineering project team at Cornell University focused on mobile app development. It is Cornell AppDev's first app built using React Native to support both iOS and Android platforms simultaneoulsy, reduce code duplication, and ensure consistency across platforms. Download the current release on the [App Store](https://apps.apple.com/us/app/resell-cornell-marketplace/id1622452299)!

Resell is AppDev's first app built using React Native to support both iOS and Android platforms simultaneoulsy, reduce code duplication, and ensure consistency across platforms.
<br />

## Initializing the project
## System Requirements

```
git clone https://github.com/cuappdev/resell-react-native.git
cd resell-react-native
```
You do not need a MacOS device to work on this application. However, you will not be able to run the iOS simulator without a MacOS device. As of 4/19/24, NodeJS version v21.6.1 and yarn version 1.22.22 are compatible.

## Starting the development server
## Using Firebase

For sign in, we are using React Native Google Sign In, which requires custom native code.
Therefore you must build for Android or iOS.
Resell uses two databases per environment. We have a PostgreSQL database that is associated with our Digital Ocean backend server. We also have a Firebase Firestore database (a NoSQL database) under the Resell Firebase project in our Cornell AppDev Google acount.

### Steps for iOS:
For Firestore, the `(default)` database corresponds to our development environment and `resell-prod` corresponds to production. **Please be aware of which database to use since frontend is responsible for managing data in Firestore.**

1. Add necessary environment varibles in `.env`, and add your `GoogleService-Info.plist` file to the `config` folder.
2. Execute the following terminal commands:
3. `yarn`
4. `cd ios`
5. `pod install`
6. `cd ../`
7. `npx expo run:ios`
## Getting Started

### Steps for Android:
This app uses native application code, requiring a development build for iOS and Android instead of Expo Go. In order to run the application during development, an Android emulator and iOS simulator will be needed.

We are currently facing build issues for Android. Unfortunately we cannot just use Expo Go since this project uses React Native Google Sign In. We are going to prioritize iOS for now but when we get Android to consistently build this file will be updated.
### Install an Android Studio Emulator

## Troubleshooting
Set up instructions vary depending on whether or not the operating system is MacOS or Windows. If you don't have an Android device available to test with, use the default emulator that comes with Android Studio. Follow the instructions in [this guide](https://docs.expo.dev/workflow/android-studio-emulator/).

### Install an iOS Simulator

Note that the iOS Simulator can only be installed on macOS.

1. Install Xcode through the Mac App Store.
2. Open Xcode, choose **Settings…** from the Xcode menu. Go to **Locations** and install the tools by selecting the most recent version in the **Command Line Tools** dropdown.

If you are developing an iOS app from a Windows or a Linux machine, you will need a physical iOS device.

## Importing Environment Variables and Secrets

For AppDev members, you can find the files from the `#resell-frontend` Slack channel.

1. Create a `.env` file in the **root directory** and copy/paste the values from the pinned message.
1. Note that there are two different `.env` files: development and production.
2. Download `GoogleService-Info.plist` and `google-services.json` and place both files in the `/config` folder.

## Installing Dependencies

This codebase uses Node packages for dependencies. Android packages are managed through Gradle, and iOS packages are managed using CocoaPods.

It is highly recommended to use yarn instead of npm. If you do not have yarn installed, you can do it with `npm install --global yarn`. Note that you will need to have NodeJS and npm installed in order to install yarn. Then, run `yarn install` in the root directory to install dependencies.

You will also need to install CocoaPods dependencies: `cd ios` then `pod install`. If you don't have CocoaPods installed, you can install it with `sudo gem install cocoapods`.

## Creating and Running the Development Build

### Additional steps for Android:

If you are having issues after following the above steps, try checking the following:
If you plan to run the app on the Android simulator, you need to specify the location of your Android SDK. To do this, follow the steps [here](https://stackoverflow.com/a/48155800).

If you are still having issues building, make sure your Java version is compatible with the Gradle version. As of when this was written we were using Gradle version 8.0.1, so you need to have Java 19 or below to build the app. We recommend Java 17 since this is what we have been using.

### General instructions

If this is your first time running the application, you must first create the development build.

- For Android, run `yarn android`.
- For iOS, run `yarn ios`.

If you already have a development build created, you can just start the development server.

- `yarn start`

Once the development server is up, you can press `i` to open the iOS simulator, `a` to open the Android emulator, and `r` to hot reload the app. Make sure that the top says “Using development build.” Press `CTRL + C` on your keyboard to stop the server at any time.

### Switching Environments

If you need to switch between environments, make sure that you are using the correct environment variables in the `.env` file. Then, run `yarn start --reset-cache`. Make sure you include the flag to reset the cache!

## Deployment

TODO

## Troubleshooting

1. You have a `.env` in the root folder with all necessary environment variables, and you have the `GoogleService-Info.plist` file in the config folder.
2. Make sure your `yarn.lock` is synced with version control, delete `node_modules` and run `yarn` in the root folder.
3. Ensure that you have the pods installed by the following the provided steps.
- The Classic Fix — Delete `node_modules` and run `yarn install`.
- If iOS dependencies are not working, try `cd ios` and then `pod install`. You may also need to delete `Podfile.lock`.
3 changes: 2 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
apply plugin: 'com.google.firebase.crashlytics'

def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()

Expand Down Expand Up @@ -81,7 +82,7 @@ android {

compileSdkVersion rootProject.ext.compileSdkVersion

namespace "com.resell"
namespace "com.cornellappdev.resell.android"
defaultConfig {
applicationId 'com.cornellappdev.resell.android'
minSdkVersion rootProject.ext.minSdkVersion
Expand Down
47 changes: 47 additions & 0 deletions android/app/google-services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"project_info": {
"project_number": "1534649394",
"firebase_url": "https://resell-e99a2-default-rtdb.firebaseio.com",
"project_id": "resell-e99a2",
"storage_bucket": "resell-e99a2.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:1534649394:android:7b9c42cc2cc458e638082b",
"android_client_info": {
"package_name": "com.cornellappdev.resell.android"
}
},
"oauth_client": [
{
"client_id": "1534649394-krqg6jtvrjjfhlrqvtj552g7jfavpi2k.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCGVRvixQONN-Y6rXhDOhvVjTYGeWLaGfo"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "1534649394-krqg6jtvrjjfhlrqvtj552g7jfavpi2k.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "1534649394-98pts94l83s9309oijhplbo0duvu9abd.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.cornellappdev.resell"
}
}
]
}
}
}
],
"configuration_version": "1"
}
3 changes: 1 addition & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme">
<meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color"/>
<meta-data android:name="expo.modules.notifications.default_notification_icon" android:resource="@drawable/notification_icon"/>
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="49.0.0"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@cornellappdev/Resell"/>
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
Expand Down
Binary file modified android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.resell;
package com.cornellappdev.resell.android;

import android.content.Context;
import com.facebook.react.ReactInstanceManager;
Expand Down
7 changes: 6 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services' // apply after this line
apply plugin: 'com.google.firebase.crashlytics'

buildscript {
ext {
Expand All @@ -11,15 +14,17 @@ buildscript {

// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
googlePlayServicesAuthVersion = "20.7.0"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.google.gms:google-services:4.4.0'
classpath('com.android.tools.build:gradle:7.4.2')
classpath('com.facebook.react:react-native-gradle-plugin')
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
}
}

Expand Down
4 changes: 4 additions & 0 deletions api/ApiClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export default class ApiClient {
headers?: Record<string, string>,
options?: any
) {
if (!this.accessToken) {
await this.loadAccessToken();
}
return fetch(process.env.BASE_API_URL + route, {
method: type,
body: body ? JSON.stringify(body) : "",
Expand All @@ -55,6 +58,7 @@ export default class ApiClient {
},
...options,
}).then((response) => {
console.log(response.url);
return response.json();
});
}
Expand Down
77 changes: 43 additions & 34 deletions api/FirebaseNotificationManager.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,79 @@
import messaging from '@react-native-firebase/messaging';
import { userRef } from '../config/firebase';
import { doc, updateDoc } from 'firebase/firestore';
import messaging from "@react-native-firebase/messaging";
import { userRef } from "../config/firebase";
import { doc, updateDoc } from "firebase/firestore";
import { auth } from "../config/firebase";

export async function requestUserPermission() {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;

saveNotificationSettings(auth.currentUser.email, enabled);
if (enabled) {
console.log('Authorization status:', authStatus);
console.log("Authorization status:", authStatus);
}
}

export async function getDeviceFCMToken() {
const token = await messaging().getToken();
return token
return token;
}

export async function saveDeviceTokenToFireStore(userEmail, deviceToken) {
try {
const ref = doc(userRef, userEmail)
const ref = doc(userRef, userEmail);
updateDoc(ref, {
fcmToken: deviceToken
})
console.log('Device token saved successfully');
fcmToken: deviceToken,
});
console.log("Device token saved successfully");
} catch (error) {
console.error('Error saving device token:', error);
console.error("Error saving device token:", error);
}
};
}

export async function saveNotificationSettings(userEmail, notificationsEnabled) {
export async function saveNotificationSettings(
userEmail,
notificationsEnabled
) {
try {
const ref = doc(userRef, userEmail)
const ref = doc(userRef, userEmail);
updateDoc(ref, {
notificationsEnabled: notificationsEnabled
})
console.log('Notifications Settings saved successfully');
notificationsEnabled: notificationsEnabled,
});
} catch (error) {
console.error('Error saving device token:', error);
console.error("Error updating notification settings:", error);
}
}

export async function sendNotification(recipientToken, title, body, nav, chat, notificationsEnabled) {
export async function sendNotification(
recipientToken,
title,
body,
nav,
chat,
notificationsEnabled
) {
const message = {
to: recipientToken,
notification: notificationsEnabled ? { body: body, title: title, } : null,
notification: notificationsEnabled ? { body: body, title: title } : null,
data: {
navigationId: nav,
chat: chat
}
}
chat: chat,
},
};

try {
const response = await fetch(
"https://fcm.googleapis.com/fcm/send",
{
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "key=AAAAAFt45DI:APA91bEK63cgXhAPZQ3KpC92q6RjrNOehM2H8zFqytJDuthFEvIBPaEaKfvNplZP90q74WdPcGhoeEo4iFOCks9DIpD9nyLoQpGe4pu6p3_BQGiYSvIx_YxrmLElgPgmv4Hz1P0LFQVO"
},
body: JSON.stringify(message),
}
);
const response = await fetch("https://fcm.googleapis.com/fcm/send", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization:
"key=AAAAAFt45DI:APA91bEK63cgXhAPZQ3KpC92q6RjrNOehM2H8zFqytJDuthFEvIBPaEaKfvNplZP90q74WdPcGhoeEo4iFOCks9DIpD9nyLoQpGe4pu6p3_BQGiYSvIx_YxrmLElgPgmv4Hz1P0LFQVO",
},
body: JSON.stringify(message),
});
const json = await response.json();
} catch (error) {
console.error(error);
Expand Down
Loading

0 comments on commit b4718f0

Please sign in to comment.