Skip to content

โš™๏ธ Running Local Development Version

Most components of Frankly can be run on your local machine. This section describes how to setup and run the Flutter client app and the Firebase emulators (which can be setup in place of a Firebase/GCP project). The app still connects to several third-party services, which are also described below.

Tools and Flutter Setup

Important

Frankly runs on Flutter 3.22.2. Please use this version of Flutter in order to avoid any unexpected errors.

This section covers setting up a new computer for Flutter development.

Part 1: Platform-specific

  1. Download and install Google Chrome here if itโ€™s not already pre-installed. This is used for live debugging on web.
  2. Download and install XCode from the Mac App Store. This is used for developing iOS apps and running on macOS as a desktop app.
  3. Optional, but recommended: Install Homebrew here.
  4. Xcode should've installed git automatically, but if not for some reason, you can install it via Homebrew:
    brew install git
    
  5. Clone the Frankly repo in a directory where you prefer your projects to live:
    git clone https://github.com/berkmancenter/frankly && cd frankly
    
  6. Follow the instructions here to install Flutter on your machine. You can choose iOS as your target platform.
    • This includes a link to install CocoaPods. However, you may run into issues with installing CocoaPods due to a Ruby version issue (the pre-installed Ruby on MacOS is too old). You can install ruby via Homebrew instead by running brew install cocoapods, which should alleviate those errors.
    • Recommended: Install the Flutter SDK in your home folder under a directory called dev (or something similar).
  7. Install VSCode here.
  8. Add Flutter to your PATH. For Mac with Zsh (you can also copy this command from here), create or open ~/.zshenv and add this line:
    export PATH=$HOME/dev/flutter/bin:$PATH
    
    Restart terminal sessions to see the changes.
  1. Download and install chromium and git if they're not already installed. Chromium is used for live debugging on web.
    sudo apt-get update && sudo apt-get upgrade && sudo apt-get install -y chromium git
    
    a. Set the path to chromium so flutter can find it:
    export CHROME_EXECUTABLE=$(which chromium)
    
  2. Clone the Frankly repo in a directory where you prefer your projects to live:
    git clone https://github.com/berkmancenter/frankly && cd frankly
    
  3. Follow the instructions here to install Flutter dependencies on your machine. You can choose web as your target platform.
  4. Install VSCode here.

    You may need to download the binary for your specific architecture here

  5. You will probably need to add both flutter and dart to your PATH. Run:

    export PATH="$PATH:$HOME/[path to flutter]/flutter/bin"
    

Part 2

  1. Install Node.js and npm. We strongly recommend that you do this via nvm (steps here) since it is the easiest end cleanest way to do so.
  2. Once nvm is installed and sourced to your CLI profile, run:
    nvm install --lts
    
  3. Install the Firebase CLI Tools: Most of the operations for development and deployment take place via the Firebase CLI. You can find documentation for the Firebase CLI here.

    npm install -g firebase-tools
    

    • You may run into permissions issues when installing this due to being a non-root user. To remedy this, reassign ownership of the relevant folders to yourself by running the following 2 commands before running npm install :

       sudo chown -R $USER /usr/local/bin/ && sudo chown -R $USER /usr/local/lib/node_modules
      

      For more information, you can read this Stack Overflow post.

  4. Activate the Firebase CLI for Flutter by following these steps.

    a. Sign into Firebase using your Google account and create a new project. Use a name like "frankly-dev".

    b. Log into Firebase. Run:

    firebase login
    
    c. Install the FlutterFire CLI by running:
    dart pub global activate flutterfire_cli
    
    d. From your Flutter project directory (client), run the to start the app configuration workflow:
    flutterfire configure
    
    - You can choose web as the platform for now.

Please check โ“Troubleshooting / FAQ for suggested resolutions to common Flutter installation errors.

Environment Setup

These are the steps for getting started with developing Frankly:

  1. ๐Ÿ“ฆ Build the data models
  2. ๐Ÿ”ฅ Setting up and connecting to the Firebase emulators
  3. ๐Ÿ”Œ Connecting to third-party services
  4. ๐Ÿฆ Running the frontend Flutter app

The following section will cover these steps for running the app for the first time.

๐Ÿ“ฆ Data Models

The first step in running the app is to build the data_models package. Some code in this package is auto-generated by Freezed. Run the following steps in the data_models directory to generate code and make the package available to the client and Firebase functions:

  1. To install all Dart dependencies run:
    flutter pub get
    
  2. Run:
    dart run build_runner build --delete-conflicting-outputs
    

You can also just run ./build.sh.

๐Ÿ”ฅ Firebase

Firebase Functions Installation

Firebase Functions are built on top of Cloud Functions (GCP's serverless functions product), which is why there are references to Cloud Functions tooling below. Functions are written in dart and are compiled to javascript with dart2js.

For the following sub-section, switch to the firebase/functions directory to run all commands.

  • To install all Javascript dependencies, run:
    npm install
    
  • To install all Dart dependencies, run:
    flutter pub get
    

You don't need to run npm install again unless you've added new dependencies or made updates to existing ones. Same applies to flutter pub get, but for changes to any function dependencies.

Emulators

Firebase has a full suite of emulators called Firebase Local Emulator Suite. You can find the full description of the Firebase Local Emulator Suite and its capabilities here.

You should emulate services locally for development purposes, and set up the client to use these emulators instead of connecting to a live Firebase project. By default, the emulators will run against the default project "dev," specified in the .firebaserc file.

Using config in emulators

You do not need to run functions:config:set. as the emulators are configured by a file.

  • To configure the emulators, create the file firebase/functions/.runtimeconfig.json.
  • A sample file containing the config properties described above can be found in firebase/functions/.runtimeconfig.json.local.example.

Running the emulators

Important

Do this before running the client.

To run the emulators locally, run the following while in the firebase/functions directory:

dart run build_runner build --output=build
firebase emulators:start --only firestore,functions,auth,pubsub,database

You can also just run npm run emulators.

We recommend using the emulators import and export functionality to make development easier.

Please refer to the Cloud Functions Emulator section at โ“Troubleshooting / FAQ for common issues and resolutions!

Optional: Setup Firebase Cloud Project

In order to allow the capability to run the app locally without needing to create/modify a live Firebase project, emulators for all Google Cloud services that are needed (Functions, Auth, Realtime Database, etc.) suffice for most development task.

If you plan on using Mux within your local app, however, the emulator version of the functions host is inadequate, since that service needs an actual deployed URL to send webhooks to. You will need a Firebase project of your own.

  1. Create a new Firebase project here .
  2. Make a note of the unique ID that is created for your project. It will be in the format of my-dev-project-d2f8c.
  3. You may need to create a default realtime database.
  4. From your command line within the firebase/functions directory, run:

    firebase login
    

    Logging In

    When the login window appears, ensure you are logging in as the same user that created your project.

  5. Now run:

    firebase use <project_id>
    
    You should see a message like Now using project my-dev-project-d2f8c

You can follow the official documentation to find out how to deploy, but you might use a command like this: firebase deploy --only functions.

๐Ÿ”Œ Third Party Services

The Firebase Functions and/or Flutter client app connect to the following third party services, which must be set up and configured for local development.

Agora

Sign up for Agora and open the Agora console. The following instructions are geared towards using V2 of the Agora console.

The following instructions will guide you through retrieving the values to fill in the following command for setting Agora-related values in your Functions configuration:

agora.app_id="<YOUR_VALUE_HERE>"
agora.app_certificate="<YOUR_VALUE_HERE>"
agora.rest_key="<YOUR_VALUE_HERE>"
agora.rest_secret="<YOUR_VALUE_HERE>"
agora.storage_bucket_name="<YOUR_VALUE_HERE>"
agora.storage_access_key="<YOUR_VALUE_HERE>"
agora.storage_secret_key="<YOUR_VALUE_HERE>"

๐Ÿ”ง Setting up the integration

  • Agora Create a new project in the Agora console. For Authentication Mode, select Secure Mode: App ID + Token.

  • app_id: Copy the App ID from the Projects list in the console home.

  • app_certificate: Select Configure on your project. Copy the value under Security > Primary Certificate.
  • rest_key: In the left navigation panel, select Restful API under either Developer Toolkit or Developer Resources, depending on your screen size. Click Add a Secret. Download the Customer Secret, and input the value for Key.
  • rest_secret: From the Customer Secret file, input the value for Secret.

  • Google Cloud Storage Create a Google Cloud Storage bucket to store event recordings. Navigate to Google Cloud Storage and select Create a Bucket. Provide a bucket name. Then, configure the bucket with your desired settings for the remaining options.

  • storage_bucket_name: Enter the bucket name you selected.
  • storage_access_key: Select Settings under the Cloud Storage left-side settings panel. Click on the Interopability tab. You may choose to either create an access key for a service account, or create a key for your user account. For whichever method you have opted to use, select Create a Key. Then, paste the generated Access key here.
  • storage_secret_key: From the generated key, paste the Secret.
  • In the codebase In client/lib/app/community/admin/conversations_tab.dart, change the URIs in the _buildRecordingSection method (replacing the ASML values appearing ahead of /us-central1/downloadRecording) to reflect your staging and prod Firebase project IDs.

๐Ÿ‘พ Testing the integration

Once you have the keys set up, you can follow the below checklist to test that key behaviors that depend on Agora are working successfully.

  • Basic video functionality: Create a community, start an event, and join the event from two different browser windows with two different users. Verify that when video and audio are enabled, both parties can see and hear each other.
  • Mobile size: Verify that video still works on mobile size.
  • Adjusting AV settings: In the bottom navigation bar, adjust your microphone and video input. Verify that the change occurs successfully.
  • Breakout rooms: Start breakout rooms. Verify that users are assigned to breakout rooms and video still works.
  • Recording: Record the meeting by going into Settings and toggling the "Recording" option on before joining the event. After joining the meeting, verify that the top right corner says "Recording".
  • Go to your Settings view, select Conversations, and select 'Download'. Verify that a .zip file should be downloaded with several different files, including audio and video.
  • Bottom navigation: Interact with everything on the bottom navigation bar, including chat and emoji reactions. Verify that behavior works as expected.
  • Show participant info: In the right side bar, click on a participant to show their user info. Verify that user details show as expected.
  • Kicking a user: In a hosted meeting, kick a user. Verify that the host sees the user disappear, and the user should see that they are banned if they try to navigate back to the event.

Optional: Mux

๐Ÿ”ง Setting up the integration

Mux streaming is used when a customer wants to stream video from a third party streaming service, such as Zoom, to Frankly. Essentially the customer will record video from the third party platform, the data is sent to Mux, which will then notify Frankly's MuxWebhook Firebase function that a stream has started. Once the stream has started, the Frankly event page will display the streaming video.

  1. Using Mux's instructions, get a new access token. Use the environment of your choice and set the permission level to "Mux Video".

  2. Set up Mux secrets for your local development environment, either by running the firebase command line or copying and pasting the information.

    As the names suggest, the mux.token_id corresponds to your Mux token ID and mux.secret corresponds to your Mux token secret.

    firebase functions:config:set mux.secret="<YOUR_VALUE_HERE>" mux.token_id="<YOUR_VALUE_HERE>"
    

    Or, paste your token and secret into the .runtimeconfig.json file where the mux field is.

      "mux": {
        "secret": "...",
        "token_id": "..."
      },
    

  3. To connect Mux to the MuxWebhooks cloud function, the function first needs to be deployed to your Google Cloud Project. Get the URL of the deployed function provided by Google Cloud, which should resemble this format: https://us-central1-myproject.cloudfunctions.net/MuxWebhooks.

  4. Login to Mux and go to Settings > Webhooks. Select the environment for which you want to use the webhook, then click โ€œCreate new webhook.โ€ For the URL to Notify field, provide the URL for your deployed MuxWebhooks function. Then click "Create webhook."

๐Ÿ‘พ Testing your setup

You can verify the integration is working by manually triggering a new call directly from Mux.

  • Visit your Google Cloud Platform Logging page so you can scan for any errors and expected logs during the live stream test.
  • In the Mux dashboard, go to Video > Live Streams. Click "Create your first live stream."
  • Run the default request.

The following should be true if your Mux setup works as expected: The logs displayed on the Logging page should indicate that the MuxWebhook Firebase function was called. You can filter the logs by function name in the Google Cloud Console to find logs associated to this function. When viewing the logs, you will likely observe the following error message: "Error: Unexpected number of documents matching livestream ID". This is due to the liveStreamId not matching an id associated to an existing LiveStreamInfo.kFieldMuxId value in the database. This is an expected error. For more thorough testing, we recommend the steps below.

(Recommended) You can also use the following steps to set up a live stream in Zoom and test your Mux integration end-to-end:

  1. Create a new event in Frankly and configure it for livestreaming using steps 1-2 in these instructions.
  2. Open Zoom and verify you have livestreaming enabled using these steps. Then follow these steps to setup your livestreaming event on Zoom. Use the following values:
  3. For Stream URL, use the Stream URL provided on the Frankly event page.
  4. For Stream Key, use the Stream Key provided on the Frankly event page.
  5. For โ€œLive streaming page URL,โ€ use the page URL of the event setup page where you got the Streaming values above. The URL should look like this: https://gen-hls-bkc-7627.web.app/space//discuss/?status=joined
  6. Visit your Google Cloud Platform Logging page so you can scan for any errors during the live stream test.
  7. When you are ready, start the live stream on Zoom using these steps

The following should be true if your Mux setup works as expected:

  • The live streaming page on Frankly is now showing your streaming video from Zoom
  • The logs displayed on the Logging page should not display any errors related to the MuxWebhook function. Be sure to query logs from the past 1 hour or longer. You can also use this query to include only Errors.

Cloudinary

  1. Sign up for Cloudinary.
  2. Create two upload presets here, one for images and one for videos.

    You can learn about upload presets here.

    1. On the General panel, use this configuration for images:

      - Name: "frankly-image-default" (or whatever you'd like)
      - Signing mode: Unsigned
      - Disallow public ID: โœ”๏ธ
      - Asset folder: empty
      - Generated public ID: Auto-generate
      - Generated display name: Use the last segment of the public ID
      

      1. Now, on the Transform panel, under "Incoming transformation", enter c_crop,g_custom and click Save.

      These settings are required so that users are able to crop images. They also ensure that all images, cropped or not, are compressed before storage).

    2. On the General panel, use this configuration for videos:

      - Name: "frankly-video-default" (or whatever you'd like)
      - Signing mode: Unsigned
      - Disallow public ID: โœ”๏ธ
      - Asset folder: "videos/uploads" (this value doesn't matter, just somewhere unique to store your videos)
      - Generated public ID: Auto-generate
      - Generated display name: Use the last segment of the public ID
      

      1. Click Save.
  3. Now update the following in client/.env:

    Your CLOUDINARY_CLOUD_NAME is found here under "Product environment cloud name".

CLOUDINARY_IMAGE_PRESET=frankly-image-default (or name you used)
CLOUDINARY_VIDEO_PRESET=frankly-video-default
CLOUDINARY_DEFAULT_PRESET=frankly-video-default
CLOUDINARY_CLOUD_NAME=<value>

SendGrid

  • Uses a Firestore extension. Emails definitions are written to the firestore collection sendgridemail.
  • Configure the firestore extension "Trigger Email" firebase/firestore-send-email@0.1.9 with your sendgrid info

Stripe

Stripe is currently disabled for the platform. The following instructions will apply if you choose to enable Stripe:

  • Set your Stripe secret key in functions config by replacing placeholder values in the following command:
firebase functions:config:set stripe.connected_account_webhook_key="<YOUR_CONNECTED_ACCOUNT_WEBHOOK_SECRET_KEY>" stripe.pub_key="<YOUR_STRIPE_PUBLISHABLE_KEY>" stripe.secret_key="<YOUR_STRIPE_SECRET_KEY>" stripe.webhook_key="<YOUR_WEBHOOK_SECRET_KEY>"
  • Set up products for each type with a metadata field "plan_type" of individual, club, pro and prices for each one

๐Ÿฆ Running and building the Client

โ” But first, if using, have you setup and run the emulators?

Recommended instructions (debug configs)

In general, you can use the configs defined in .vscode/launch.json to run debug mode. We have defined 2 environments for you:

  1. Client
  2. ๐ŸŒŸ Client Dev (Emulators) - this connects to functions, firestore, database, and auth emulators

Note

The default debug platform is Web (Chrome), so please ensure it is selected as the target platform when running. We do not currently officially support any other platform.

.env File

You will need to create a .env file for client configuration. Copy client/.env.example.local to client/.env and update the missing secrets marked with <value> accordingly. The VSCode profiles assume the .env file lives in the client directory.

You can also add an EMULATORS environment variable to override the default Emulators profile behavior of running firestore, auth, functions, database. Set the value to any desired combination of emulators.

Manual instructions

If you want to use emulators, ensure you start the emulators first. Then run the following commands in the /client directory.

To run the app with backend pointing at staging.

flutter run -d chrome --release --web-renderer html -t lib/main.dart --dart-define-from-file=.env

To run the app with locally running functions, firestore, and auth emulators

flutter run -d chrome --release --web-renderer html -t lib/dev_emulators_main.dart --dart-define-from-file=.env

Supported browsers

The client app runs only on the Flutter web platform. Flutter uses Chrome for debugging web apps, but it does support all major browsers in production Web FAQ | Flutter; Chrome, Firefox, Safari, Edge.

Testing

End-to-End Tests

See instructions here for developing and running end-to-end Playwright tests.

Flutter Unit Tests

The client/test directory holds Flutter unit and widget tests.

To run existing tests, you can run the following command from the client/test directory:

flutter pub run build_runner build
cd ../
flutter test --platform chrome

To run newly added tests:

flutter test <optional path to test files>

To run unit tests with locally generated HTML coverage report:

flutter test --coverage && format_coverage --in=coverage && genhtml coverage/lcov.info -o coverage/html