├── .nvmrc
├── android
├── gradle.properties
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── res
│ │ └── drawable
│ │ │ └── ic_daily_videocam_24dp.xml
│ │ └── java
│ │ └── com
│ │ └── daily
│ │ └── reactlibrary
│ │ ├── DailyNativeUtilsPackage.java
│ │ ├── DailyOngoingMeetingForegroundService.java
│ │ └── DailyNativeUtils.java
└── build.gradle
├── ios
├── DailyNativeUtils.h
├── DailyJs.xcworkspace
│ └── contents.xcworkspacedata
├── DailyNativeUtils.m
└── DailyJs.xcodeproj
│ └── project.pbxproj
├── .gitignore
├── scripts
└── tag
├── tsconfig.json
├── .npmrc
├── .github
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── react-native-daily-js.podspec
├── LICENSE
├── package.json
├── src
├── DailyMediaView.tsx
├── iOSCallObjectBundleCache.ts
└── index.ts
├── CONTRIBUTING.md
├── README.md
└── type-overrides
└── @daily-co
└── daily-js
└── index.d.ts
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18.19.0
2 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Enabling android x
2 | android.useAndroidX=true
3 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/ios/DailyNativeUtils.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface DailyNativeUtils : RCTEventEmitter
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 |
3 | # node.js
4 | #
5 | node_modules/
6 | npm-debug.log
7 | yarn-error.log
8 |
9 | android/build
10 | *.iml
11 |
--------------------------------------------------------------------------------
/ios/DailyJs.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/scripts/tag:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | TAG_NAME=$(echo ${npm_package_name} | sed -E 's|^.+/||')-$(date -u +'%Y-%m-%d')-${npm_package_version} && \
4 | git tag -a -m "${npm_package_name} ${npm_package_version} published to npmjs" ${TAG_NAME} HEAD && \
5 | git push origin ${TAG_NAME}
6 |
--------------------------------------------------------------------------------
/android/src/main/res/drawable/ic_daily_videocam_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "outDir": "./dist",
6 | "lib": ["es2021"],
7 | "strict": true,
8 | "jsx": "react-native",
9 | "esModuleInterop": true,
10 | "allowSyntheticDefaultImports": true,
11 | "baseUrl": ".",
12 | "declaration": true,
13 | "paths": {
14 | "@daily-co/daily-js": ["type-overrides/@daily-co/daily-js"]
15 | },
16 | "types": ["react", "react-native"]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | # We need that because after npm 7, npm automatically tries to install peer dependencies.
2 | # More details can be found here: https://github.blog/2021-02-02-npm-7-is-now-generally-available/#peer-dependencies
3 | # But the problem is that several modules that react-native-daily-js depends on, like, @react-native-async-storage/async-storage,
4 | # react-native-background-timer, react-native-url-polyfill, and—yes—@daily-co/react-native-webrtc, all declare a peer dependency on react-native.
5 | # If we remove that we are going to receive errors of different version creating conflict.
6 | legacy-peer-deps=true
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Tell us if there's a feature you'd like to add or change that would be helpful.
4 | title: '[FEATURE-REQUEST]'
5 | labels: enhancement
6 | assignees: kimberleejohnson
7 | ---
8 |
9 | # Feature request
10 |
11 |
12 |
13 | # Why you need this
14 |
15 |
16 |
17 | # Alternatives you've considered
18 |
19 |
20 |
21 | # Additional context
22 |
23 |
24 |
--------------------------------------------------------------------------------
/android/src/main/java/com/daily/reactlibrary/DailyNativeUtilsPackage.java:
--------------------------------------------------------------------------------
1 | package com.daily.reactlibrary;
2 |
3 | import java.util.Arrays;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import com.facebook.react.ReactPackage;
8 | import com.facebook.react.bridge.NativeModule;
9 | import com.facebook.react.bridge.ReactApplicationContext;
10 | import com.facebook.react.uimanager.ViewManager;
11 |
12 | public class DailyNativeUtilsPackage implements ReactPackage {
13 | @Override
14 | public List createNativeModules(ReactApplicationContext reactContext) {
15 | return Arrays.asList(new DailyNativeUtils(reactContext));
16 | }
17 |
18 | @Override
19 | public List createViewManagers(ReactApplicationContext reactContext) {
20 | return Collections.emptyList();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/react-native-daily-js.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "react-native-daily-js"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 | s.description = <<-DESC
10 | react-native-daily-js
11 | DESC
12 | s.homepage = "https://github.com/daily-co/react-native-daily-js"
13 | # brief license entry:
14 | s.license = "MIT"
15 | # optional - use expanded license entry instead:
16 | # s.license = { :type => "MIT", :file => "LICENSE" }
17 | s.authors = { "Paul Kompfner" => "paul@daily.co" }
18 | s.platforms = { :ios => "12.0" }
19 | s.source = { :git => "https://github.com/daily-co/react-native-daily-js.git", :tag => "#{s.version}" }
20 |
21 | s.source_files = "ios/**/*.{h,c,m,swift}"
22 | s.requires_arc = true
23 |
24 | s.dependency "React-Core"
25 | s.dependency "ReactNativeDailyJSScreenShareExtension", "0.0.1"
26 | # ...
27 | # s.dependency "..."
28 | end
29 |
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: BUG
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
12 | # Expected behavior
13 |
14 |
15 |
16 | # Describe the bug (unexpected behavior)
17 |
18 |
19 |
20 | # Steps to reproduce
21 |
22 |
23 |
24 |
25 |
26 |
27 | # Screenshots
28 |
29 |
30 |
31 | # System information
32 |
33 |
34 |
35 | * Device:
36 | * OS, version:
37 | * Browser, version:
38 |
39 | # Additional context
40 |
41 |
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2019, daily-co
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@daily-co/react-native-daily-js",
3 | "private": true,
4 | "//": "^ COMMENT OUT 'private: true' BEFORE RUNNING NPM PUBLISH",
5 | "version": "0.82.0",
6 | "description": "React Native library for making video calls using Daily",
7 | "main": "dist/index.js",
8 | "types": "dist/index.d.ts",
9 | "files": [
10 | "dist",
11 | "type-overrides",
12 | "android/src",
13 | "android/build.gradle",
14 | "android/gradle.properties",
15 | "ios",
16 | "react-native-daily-js.podspec",
17 | "react-native.config.js",
18 | "README.md",
19 | "LICENSE"
20 | ],
21 | "scripts": {
22 | "build": "tsc",
23 | "postbuild": "sed -E -i.bak 's|@daily-co/daily-js|../type-overrides/@daily-co/daily-js|g' ./dist/index.d.ts && rm ./dist/index.d.ts.bak",
24 | "prepare": "npm run build",
25 | "tag": "scripts/tag",
26 | "prepublishOnly": "npm run tag"
27 | },
28 | "license": "BSD-2-Clause",
29 | "repository": {
30 | "type": "git",
31 | "url": "git+https://github.com/daily-co/react-native-daily-js.git",
32 | "baseUrl": "https://github.com/daily-co/react-native-daily-js"
33 | },
34 | "dependencies": {
35 | "@daily-co/daily-js": "^0.85.0",
36 | "@types/react-native-background-timer": "^2.0.0",
37 | "base-64": "^1.0.0",
38 | "react-native-url-polyfill": "^1.1.2"
39 | },
40 | "///": "WHEN ONE OF THESE CHANGE, PLEASE UPDATE README.md INSTALL SNIPPET ACCORDINGLY",
41 | "peerDependencies": {
42 | "@daily-co/react-native-webrtc": "^124.0.6-daily.1",
43 | "@react-native-async-storage/async-storage": "^1.24.0",
44 | "react-native": ">=0.68.0",
45 | "react-native-background-timer": "^2.4.1",
46 | "react-native-get-random-values": "^1.11.0"
47 | },
48 | "devDependencies": {
49 | "@daily-co/react-native-webrtc": "^124.0.6-daily.1",
50 | "@react-native-async-storage/async-storage": "^1.24.0",
51 | "@types/base-64": "^1.0.0",
52 | "@types/react": "18.3.21",
53 | "react-native": "0.76.9",
54 | "react-native-background-timer": "^2.4.1",
55 | "typescript": "^5.8.3"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/DailyMediaView.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useEffect, useState } from 'react';
3 | import { ViewStyle, View, Platform } from 'react-native';
4 | import {
5 | MediaStreamTrack,
6 | RTCView,
7 | MediaStream,
8 | RTCViewProps,
9 | } from '@daily-co/react-native-webrtc';
10 |
11 | type Props = {
12 | videoTrack: MediaStreamTrack | null;
13 | audioTrack: MediaStreamTrack | null;
14 | mirror?: RTCViewProps['mirror'];
15 | zOrder?: RTCViewProps['zOrder'];
16 | objectFit?: RTCViewProps['objectFit'];
17 | style?: ViewStyle;
18 | };
19 |
20 | export default function DailyMediaView(props: Props) {
21 | const [stream, setStream] = useState(null);
22 |
23 | useEffect(() => {
24 | const tracks = [props.videoTrack, props.audioTrack].filter(
25 | (t) => t
26 | ) as MediaStreamTrack[];
27 | const stream = tracks.length > 0 ? new MediaStream(tracks) : null;
28 | setStream(stream);
29 | }, [props.videoTrack, props.audioTrack]);
30 |
31 | const rtcView = stream ? (
32 |
48 | ) : null;
49 |
50 | // provide empty placeholder when no video is playing, to try to avoid
51 | // messing with any layout that depends on this DailyMediaView's style
52 | const placeholderView = props.videoTrack ? null : (
53 |
54 | );
55 |
56 | return (
57 | <>
58 | {rtcView}
59 | {placeholderView}
60 | >
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | def DEFAULT_COMPILE_SDK_VERSION = 34
2 | def DEFAULT_BUILD_TOOLS_VERSION = '34.0.0'
3 | def DEFAULT_MIN_SDK_VERSION = 24
4 | def DEFAULT_TARGET_SDK_VERSION = 34
5 |
6 | def safeExtGet(prop, fallback) {
7 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
8 | }
9 |
10 | apply plugin: 'com.android.library'
11 |
12 | buildscript {
13 | // The Android Gradle plugin is only required when opening the android folder stand-alone.
14 | // This avoids unnecessary downloads and potential conflicts when the library is included as a
15 | // module dependency in an application project.
16 | // ref: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies
17 | if (project == rootProject) {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | }
22 | dependencies {
23 | classpath "com.android.tools.build:gradle:8.6.0"
24 | }
25 | }
26 | }
27 |
28 | android {
29 | namespace 'com.daily.reactlibrary'
30 | compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
31 | buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
32 |
33 | defaultConfig {
34 | minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
35 | targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
36 | versionCode 1
37 | versionName "1.0"
38 | }
39 | lintOptions {
40 | abortOnError false
41 | }
42 | compileOptions {
43 | sourceCompatibility = 1.8
44 | targetCompatibility = 1.8
45 | }
46 | buildFeatures {
47 | buildConfig true
48 | }
49 | }
50 |
51 | repositories {
52 | // ref: https://www.baeldung.com/maven-local-repository
53 | mavenLocal()
54 | maven {
55 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
56 | url "$rootDir/../node_modules/react-native/android"
57 | }
58 | maven {
59 | // Android JSC is installed from npm
60 | url "$rootDir/../node_modules/jsc-android/dist"
61 | }
62 | google()
63 | mavenCentral()
64 | }
65 |
66 | dependencies {
67 | //noinspection GradleDynamicVersion
68 | implementation 'com.facebook.react:react-native:+' // From node_modules
69 |
70 | //For dev only
71 | //implementation 'com.facebook.react:react-android:0.76.9' // From node_modules
72 | }
73 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thank you for looking into contributing to`react-native-daily-js`! We welcome your contributions toward making [Daily](https://www.daily.co) the easiest real-time video API for developers to use in their products.
4 |
5 | **Before contributing:**
6 |
7 | - [Read our code of conduct](#read-our-code-of-conduct)
8 |
9 | **How to contribute:**
10 |
11 | - [Open or claim an issue](#open-or-claim-an-issue)
12 | - [Open a pull request](#open-a-pull-request)
13 |
14 | ## Before contributing
15 |
16 | ### Read our code of conduct
17 |
18 | We use the [Contributor Covenant](https://www.contributor-covenant.org/) for our Code of Conduct. Before contributing, [please read it](CODE_OF_CONDUCT.md).
19 |
20 | ## How to contribute
21 |
22 | ### Open or claim an issue
23 |
24 | #### Open an issue
25 |
26 | Today we work off two main issue templates: _bug reports_ and _feature requests_.
27 |
28 | _Bug reports_
29 |
30 | Before creating a new bug report, please do two things:
31 |
32 | 1. If you want to report a bug you experienced while on a Daily call, try out these [troubleshooting tips](https://help.daily.co/en/articles/2303117-top-troubleshooting-tips) to see if that takes care of the bug.
33 | 2. If you're still seeing the error, check to see if somebody else has [already filed the issue](https://github.com/daily-co/react-native-daily-js/issues) before creating a new one.
34 |
35 | If you've done those two things and need to create an issue, we'll ask you to tell us:
36 |
37 | - What you expected to happen
38 | - What actually happened
39 | - Steps to reproduce the error
40 | - Screenshots that illustrate where and what we should be looking for when we reproduce
41 | - System information, like your device, OS, and browser
42 | - Any additional context that you think could help us work through this
43 |
44 | _Feature requests_
45 |
46 | We're always happy to hear about new ways you'd like to use Daily. If you'd like a feature that we don't have yet, we'll ask you to let us know:
47 |
48 | - If the feature will help you solve a particular problem
49 | - Alternative solutions you've considered
50 | - Any additional context that might help us understand this ask
51 |
52 | #### Claim an issue
53 |
54 | All issues labeled `good-first-issue` are up for grabs. If you'd like to tackle an existing issue, feel free to assign yourself, and please leave a comment letting everyone know that you're on it.
55 |
56 | ### Open a pull request
57 |
58 | - If it's been a minute or if you haven't yet cloned, forked, or branched a repository, GitHub has some [docs to help](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests).
59 | - When creating commit messages and pull request titles, please follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) standard.
60 |
61 |
--------------------------------------------------------------------------------
/src/iOSCallObjectBundleCache.ts:
--------------------------------------------------------------------------------
1 | import AsyncStorage from '@react-native-async-storage/async-storage';
2 |
3 | const FIELD_CACHE_EXPIRY = 'expiry';
4 | const FIELD_BUNDLE_CODE = 'code';
5 | const FIELD_BUNDLE_LAST_MODIFIED = 'last-modified';
6 | const FIELD_BUNDLE_ETAG = 'etag';
7 |
8 | const REQUIRED_FIELDS = [FIELD_BUNDLE_CODE, FIELD_CACHE_EXPIRY];
9 |
10 | const DEFAULT_EXPIRY_MS = 60 * 1000;
11 |
12 | function cacheKeyForUrl(url: string): string {
13 | return `callobj_bundle_${url}`;
14 | }
15 |
16 | type CacheResponse = {
17 | code?: string;
18 | refetchHeaders?: Headers;
19 | };
20 |
21 | /**
22 | * A workaround for the fact that the iOS HTTP cache won't cache the call
23 | * object bundle due to size.
24 | */
25 | export default class iOSCallObjectBundleCache {
26 | static async get(
27 | url: string,
28 | ignoreExpiry = false
29 | ): Promise {
30 | // console.log("[iOSCallObjectBundleCache] get", url);
31 |
32 | const now = Date.now();
33 |
34 | try {
35 | // Get bundle code + metadata from cache
36 | const cacheItemString = await AsyncStorage.getItem(cacheKeyForUrl(url));
37 |
38 | // If cache miss, return null
39 | if (!cacheItemString) {
40 | return null;
41 | }
42 |
43 | const cacheItem: { [key: string]: any } = JSON.parse(cacheItemString);
44 |
45 | // Check cache item for missing required fields
46 | for (const field of REQUIRED_FIELDS) {
47 | if (!cacheItem[field]) {
48 | throw new Error(
49 | `Missing call object bundle cache item field: ${field}`
50 | );
51 | }
52 | }
53 |
54 | // If cache expired, return headers needed to conditionally fetch the
55 | // bundle again
56 | if (!ignoreExpiry && now > cacheItem[FIELD_CACHE_EXPIRY]) {
57 | // console.log("[iOSCallObjectBundleCache] cache item expired");
58 | let headers: { [key: string]: string } = {};
59 | // Only set headers for validators (ETag, Last-Modified) that exist in
60 | // the cache item
61 | cacheItem[FIELD_BUNDLE_ETAG] &&
62 | (headers['if-none-match'] = cacheItem[FIELD_BUNDLE_ETAG]);
63 | cacheItem[FIELD_BUNDLE_LAST_MODIFIED] &&
64 | (headers['if-modified-since'] =
65 | cacheItem[FIELD_BUNDLE_LAST_MODIFIED]);
66 | return { refetchHeaders: new Headers(headers) };
67 | }
68 |
69 | // Return cached code
70 | return { code: cacheItem[FIELD_BUNDLE_CODE] };
71 | } catch (e) {
72 | // console.log("[iOSCallObjectBundleCache] error in get", url, e);
73 |
74 | // Clear potentially problematic cache entry and return null
75 | AsyncStorage.removeItem(cacheKeyForUrl(url));
76 | return null;
77 | }
78 | }
79 |
80 | static async renew(url: string, headers: Headers): Promise {
81 | // console.log("[iOSCallObjectBundleCache] renew", url);
82 |
83 | const cacheResponse = await this.get(url, true);
84 |
85 | if (!(cacheResponse && cacheResponse.code)) {
86 | throw new Error(
87 | 'Attempting to renew a call object bundle cache item that is missing'
88 | );
89 | }
90 |
91 | this.set(url, cacheResponse.code, headers);
92 |
93 | return cacheResponse;
94 | }
95 |
96 | static async set(url: string, code: string, headers: Headers): Promise {
97 | // console.log("[iOSCallObjectBundleCache] set", url, headers);
98 |
99 | if (!code) {
100 | return;
101 | }
102 |
103 | // Get cache expiry from cache-control header (or use a default value)
104 | let expiry = DEFAULT_EXPIRY_MS;
105 | const cacheControlHeader = headers.get('cache-control');
106 | if (cacheControlHeader) {
107 | const expiryMatch = cacheControlHeader.match(/max-age=([0-9]+)/i);
108 | if (expiryMatch && expiryMatch[1] && !isNaN(Number(expiryMatch[1]))) {
109 | expiry = Date.now() + Number(expiryMatch[1]) * 1000;
110 | }
111 | }
112 |
113 | // Get validators for conditional requests
114 | const etag = headers.get('etag');
115 | const lastModified = headers.get('last-modified');
116 |
117 | let cacheItem: { [key: string]: any } = {};
118 | cacheItem[FIELD_BUNDLE_CODE] = code;
119 | cacheItem[FIELD_CACHE_EXPIRY] = expiry;
120 | cacheItem[FIELD_BUNDLE_ETAG] = etag;
121 | cacheItem[FIELD_BUNDLE_LAST_MODIFIED] = lastModified;
122 |
123 | return AsyncStorage.setItem(cacheKeyForUrl(url), JSON.stringify(cacheItem));
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/android/src/main/java/com/daily/reactlibrary/DailyOngoingMeetingForegroundService.java:
--------------------------------------------------------------------------------
1 | package com.daily.reactlibrary;
2 |
3 | import android.app.Activity;
4 | import android.app.Notification;
5 | import android.app.NotificationChannel;
6 | import android.app.NotificationManager;
7 | import android.app.PendingIntent;
8 | import android.app.Service;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.os.Build;
12 | import android.os.IBinder;
13 |
14 | import androidx.annotation.Nullable;
15 | import androidx.annotation.RequiresApi;
16 | import androidx.core.app.NotificationCompat;
17 | import androidx.core.content.ContextCompat;
18 |
19 | // In addition to showing a notification, this helps keep the app alive in the background when
20 | // there's an ongoing meeting.
21 | public class DailyOngoingMeetingForegroundService extends Service {
22 |
23 | private static final String TAG = DailyOngoingMeetingForegroundService.class.getName();
24 | private static final String NOTIFICATION_CHANNEL_ID = "dailyOngoingMeetingNotificationChannel";
25 | private static final int NOTIFICATION_ID = 1;
26 | private static final String EXTRA_TITLE = "title";
27 | private static final String EXTRA_SUBTITLE = "subtitle";
28 | private static final String EXTRA_ICON_NAME = "icon_name";
29 |
30 | public static Class extends Activity> activityClassToOpenFromNotification;
31 |
32 | public static void start(Class extends Activity> activityClass, String title, String subtitle, String iconName, Context context) {
33 | activityClassToOpenFromNotification = activityClass;
34 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
35 | createNotificationChannel(context);
36 | }
37 | Intent intent = new Intent(context, DailyOngoingMeetingForegroundService.class);
38 | intent.putExtra(EXTRA_TITLE, title);
39 | intent.putExtra(EXTRA_SUBTITLE, subtitle);
40 | intent.putExtra(EXTRA_ICON_NAME, iconName);
41 | ContextCompat.startForegroundService(context, intent);
42 | }
43 |
44 | public static void stop(Context context) {
45 | Intent intent = new Intent(context, DailyOngoingMeetingForegroundService.class);
46 | context.stopService(intent);
47 | }
48 |
49 | @Nullable
50 | @Override
51 | public IBinder onBind(Intent intent) {
52 | return null;
53 | }
54 |
55 | @Override
56 | public int onStartCommand(Intent intent, int flags, int startId) {
57 | String title = intent.getStringExtra(EXTRA_TITLE);
58 | if (title == null) {
59 | title = "In a call";
60 | }
61 | String subtitle = intent.getStringExtra(EXTRA_SUBTITLE);
62 | if (subtitle == null) {
63 | subtitle = "You're in a call. Tap to open it.";
64 | }
65 | String iconName = intent.getStringExtra(EXTRA_ICON_NAME);
66 | if (iconName == null) {
67 | iconName = "ic_daily_videocam_24dp";
68 | }
69 | int icon = getResources().getIdentifier(
70 | iconName,
71 | "drawable",
72 | getPackageName());
73 |
74 | Intent notificationIntent = new Intent(this, activityClassToOpenFromNotification);
75 | PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
76 |
77 | Notification notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
78 | .setContentTitle(title)
79 | .setContentText(subtitle)
80 | .setSmallIcon(icon)
81 | .setContentIntent(pendingIntent)
82 | .setAutoCancel(false)
83 | .setCategory(NotificationCompat.CATEGORY_CALL)
84 | .setOngoing(true)
85 | .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
86 | .setOnlyAlertOnce(true)
87 | .build();
88 |
89 | startForeground(NOTIFICATION_ID, notification);
90 |
91 | return START_NOT_STICKY;
92 | }
93 |
94 | // This is safe to call multiple times. It will no-op if the channel already exists.
95 | @RequiresApi(Build.VERSION_CODES.O)
96 | private static void createNotificationChannel(Context context) {
97 | NotificationChannel serviceChannel = new NotificationChannel(
98 | NOTIFICATION_CHANNEL_ID,
99 | "Daily Ongoing Meeting Notification Channel",
100 | NotificationManager.IMPORTANCE_DEFAULT);
101 | serviceChannel.setShowBadge(false);
102 | serviceChannel.enableLights(false);
103 | serviceChannel.setVibrationPattern(new long[]{0});
104 | NotificationManager manager = context.getSystemService(NotificationManager.class);
105 | manager.createNotificationChannel(serviceChannel);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Daily from '@daily-co/daily-js';
2 | import { registerGlobals } from '@daily-co/react-native-webrtc';
3 | import DailyMediaView from './DailyMediaView';
4 | import iOSCallObjectBundleCache from './iOSCallObjectBundleCache';
5 | import 'react-native-url-polyfill/auto'; // Applies global URL polyfill
6 | import BackgroundTimer from 'react-native-background-timer';
7 | import { encode as btoa, decode as atob } from 'base-64';
8 | import {
9 | Platform,
10 | NativeModules,
11 | NativeEventEmitter,
12 | AppState,
13 | AppStateStatus,
14 | } from 'react-native';
15 | // Preventing RN issue getrandomvalues not supported
16 | // https://github.com/uuidjs/uuid#getrandomvalues-not-supported
17 | import 'react-native-get-random-values';
18 | const { DailyNativeUtils, WebRTCModule } = NativeModules;
19 |
20 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
21 | declare const global: any;
22 |
23 | const webRTCEventEmitter = new NativeEventEmitter(WebRTCModule);
24 | const dailyNativeUtilsEventEmitter = new NativeEventEmitter(DailyNativeUtils);
25 |
26 | let hasAudioFocus: boolean;
27 | let appState: AppStateStatus;
28 | const audioFocusChangeListeners: Set<(hasFocus: boolean) => void> = new Set();
29 |
30 | export type DailyAppStateEvent = 'active' | 'inactive' | 'destroyed';
31 | const appStateChangeListeners: Set<(appState: DailyAppStateEvent) => void> =
32 | new Set();
33 |
34 | const systemScreenCaptureStopListeners: Set<() => void> = new Set();
35 | let systemScreenCaptureStartCallback: { (): void } | null;
36 |
37 | function setupEventListeners() {
38 | dailyNativeUtilsEventEmitter.addListener('EventOnHostDestroy', () => {
39 | appStateChangeListeners.forEach((listener) => listener('destroyed'));
40 | });
41 |
42 | // audio focus: used by daily-js to auto-mute mic, for instance
43 | if (Platform.OS === 'android') {
44 | hasAudioFocus = true; // safe assumption, hopefully
45 | webRTCEventEmitter.addListener('EventAudioFocusChange', (event) => {
46 | if (!event || typeof event.hasFocus !== 'boolean') {
47 | console.error('invalid EventAudioFocusChange event');
48 | }
49 | const hadAudioFocus = hasAudioFocus;
50 | hasAudioFocus = event.hasFocus;
51 | if (hadAudioFocus !== hasAudioFocus) {
52 | audioFocusChangeListeners.forEach((listener) =>
53 | listener(hasAudioFocus)
54 | );
55 | }
56 | });
57 | }
58 |
59 | // app active state: used by daily-js to auto-mute cam, for instance
60 | appState = AppState.currentState;
61 | AppState.addEventListener('change', (nextState) => {
62 | const previousState = appState;
63 | appState = nextState;
64 | const wasActive = previousState === 'active';
65 | const isActive = appState === 'active';
66 | if (wasActive !== isActive) {
67 | appStateChangeListeners.forEach((listener) =>
68 | listener(isActive ? 'active' : 'inactive')
69 | );
70 | }
71 | });
72 |
73 | if (Platform.OS === 'ios') {
74 | // screen capture stop: used to synchronize JS screen sharing state with iOS
75 | // system screen capture state, which can be controlled outside the app via
76 | // the control center or by tapping the notification in the corner.
77 | dailyNativeUtilsEventEmitter.addListener(
78 | 'EventSystemScreenCaptureStop',
79 | () => {
80 | systemScreenCaptureStopListeners.forEach((listener) => listener());
81 | }
82 | );
83 | // when we invoke to start the screen share, we first invoke to start the screen capture
84 | // and add the listener, so we are only going to start the screen share if the capture has started
85 | // that is why we just need a single callback
86 | dailyNativeUtilsEventEmitter.addListener(
87 | 'EventSystemScreenCaptureStart',
88 | () => {
89 | if (systemScreenCaptureStartCallback) {
90 | systemScreenCaptureStartCallback();
91 | }
92 | }
93 | );
94 | }
95 | }
96 |
97 | function setupGlobals(): void {
98 | // WebRTC APIs + global `window` object
99 | registerGlobals();
100 |
101 | // A shim to prevent errors in call machine bundle (not ideal)
102 | global.window.addEventListener = () => {};
103 | global.window.removeEventListener = () => {};
104 | global.btoa = btoa;
105 | global.atob = atob;
106 |
107 | // A workaround for iOS HTTP cache not caching call object bundle due to size
108 | if (Platform.OS === 'ios') {
109 | global.iOSCallObjectBundleCache = iOSCallObjectBundleCache;
110 | }
111 |
112 | // Let timers run while Android app is in the background.
113 | // See https://github.com/jitsi/jitsi-meet/blob/caabdadf19ae5def3f8173acec6c49111f50a04e/react/features/mobile/polyfills/browser.js#L409,
114 | // where this technique was borrowed from.
115 | // For now we don't need this for iOS since we're recommending that apps use
116 | // the "voip" background mode capability, which keeps the app running normally
117 | // during a call.
118 | if (Platform.OS === 'android') {
119 | global.clearTimeout = BackgroundTimer.clearTimeout.bind(BackgroundTimer);
120 | global.clearInterval = BackgroundTimer.clearInterval.bind(BackgroundTimer);
121 | global.setInterval = BackgroundTimer.setInterval.bind(BackgroundTimer);
122 | global.setTimeout = (fn: () => void, ms = 0) =>
123 | BackgroundTimer.setTimeout(fn, ms);
124 | }
125 |
126 | global.DailyNativeUtils = {
127 | //With React Native new architecture the Native modules are lazily initialized
128 | //As a result ...DailyNativeUtils won't work as expected because the actual properties aren't there yet.
129 | //New architecture known limitations: https://github.com/reactwg/react-native-new-architecture/discussions/237
130 | //With this approach mentioning each function, everything works fine with the compatibility layer.
131 | //More details about the compatibility layer here: https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here#gradual-migration
132 | setKeepDeviceAwake: DailyNativeUtils.setKeepDeviceAwake,
133 | setShowOngoingMeetingNotification: DailyNativeUtils.setShowOngoingMeetingNotification,
134 | presentSystemScreenCapturePrompt: DailyNativeUtils.presentSystemScreenCapturePrompt,
135 | requestStopSystemScreenCapture: DailyNativeUtils.requestStopSystemScreenCapture,
136 | isScreenBeingCaptured: DailyNativeUtils.isScreenBeingCaptured,
137 | isIOS: Platform.OS === 'ios',
138 | isAndroid: Platform.OS === 'android',
139 | setAudioMode: WebRTCModule.setDailyAudioMode,
140 | setAudioDevice: WebRTCModule.setAudioDevice,
141 | getAudioDevice: WebRTCModule.getAudioDevice,
142 | enableNoOpRecordingEnsuringBackgroundContinuity:
143 | WebRTCModule.enableNoOpRecordingEnsuringBackgroundContinuity,
144 | addAudioFocusChangeListener: (listener: (hasFocus: boolean) => void) => {
145 | audioFocusChangeListeners.add(listener);
146 | },
147 | removeAudioFocusChangeListener: (listener: (hasFocus: boolean) => void) => {
148 | audioFocusChangeListeners.delete(listener);
149 | },
150 | addAppStateChangeListener: (
151 | listener: (appState: DailyAppStateEvent) => void
152 | ) => {
153 | appStateChangeListeners.add(listener);
154 | },
155 | removeAppStateChangeListener: (
156 | listener: (appState: DailyAppStateEvent) => void
157 | ) => {
158 | appStateChangeListeners.delete(listener);
159 | },
160 | addSystemScreenCaptureStopListener: (listener: () => void) => {
161 | systemScreenCaptureStopListeners.add(listener);
162 | },
163 | removeSystemScreenCaptureStopListener: (listener: () => void) => {
164 | systemScreenCaptureStopListeners.delete(listener);
165 | },
166 | setSystemScreenCaptureStartCallback: (listener: () => void) => {
167 | systemScreenCaptureStartCallback = listener;
168 | },
169 | platform: Platform,
170 | };
171 | }
172 |
173 | setupEventListeners();
174 | setupGlobals();
175 |
176 | export default Daily;
177 | export * from '@daily-co/daily-js';
178 | export { DailyMediaView };
179 | export * from '@daily-co/react-native-webrtc';
180 |
--------------------------------------------------------------------------------
/android/src/main/java/com/daily/reactlibrary/DailyNativeUtils.java:
--------------------------------------------------------------------------------
1 | package com.daily.reactlibrary;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.pm.PackageInfo;
8 | import android.content.pm.PackageManager;
9 | import android.os.Build;
10 | import android.os.Process;
11 | import android.util.Log;
12 | import android.view.Window;
13 | import android.view.WindowManager;
14 |
15 | import androidx.core.app.ActivityCompat;
16 | import androidx.core.content.ContextCompat;
17 | import androidx.fragment.app.FragmentActivity;
18 |
19 | import com.facebook.react.bridge.Arguments;
20 | import com.facebook.react.bridge.BaseActivityEventListener;
21 | import com.facebook.react.bridge.LifecycleEventListener;
22 | import com.facebook.react.bridge.Promise;
23 | import com.facebook.react.bridge.ReactApplicationContext;
24 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
25 | import com.facebook.react.bridge.ReactMethod;
26 | import com.facebook.react.modules.core.DeviceEventManagerModule;
27 | import com.facebook.react.modules.core.PermissionAwareActivity;
28 | import com.facebook.react.modules.core.PermissionListener;
29 |
30 | import java.util.ArrayList;
31 | import java.util.Arrays;
32 | import java.util.HashSet;
33 | import java.util.List;
34 | import java.util.Set;
35 |
36 | public class DailyNativeUtils extends ReactContextBaseJavaModule implements PermissionListener {
37 |
38 | private static final String TAG = DailyNativeUtils.class.getName();
39 | private static int PERMISSION_REQUEST_CODE = 666;
40 |
41 | private final ReactApplicationContext reactContext;
42 | private DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter;
43 | private Set requestersKeepingDeviceAwake = new HashSet<>();
44 | private Set requestersShowingOngoingMeetingNotification = new HashSet<>();
45 |
46 | // Info's received to crete the notification
47 | private String title;
48 | private String subtitle;
49 | private String iconName;
50 |
51 | public DailyNativeUtils(ReactApplicationContext reactContext) {
52 | super(reactContext);
53 | this.reactContext = reactContext;
54 | reactContext.addLifecycleEventListener(new LifecycleEventListener() {
55 | @Override
56 | public void onHostResume() {
57 | }
58 |
59 | @Override
60 | public void onHostPause() {
61 | }
62 |
63 | @Override
64 | public void onHostDestroy() {
65 | if (eventEmitter != null) {
66 | eventEmitter.emit("EventOnHostDestroy", Arguments.createMap());
67 | }
68 | DailyOngoingMeetingForegroundService.stop(reactContext);
69 | }
70 | });
71 | }
72 |
73 | @Override
74 | public void initialize() {
75 | this.eventEmitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
76 | }
77 |
78 | @Override
79 | public String getName() {
80 | return "DailyNativeUtils";
81 | }
82 |
83 | @ReactMethod
84 | public void setKeepDeviceAwake(boolean keepDeviceAwake, String requesterId) {
85 | Activity activity = getCurrentActivity();
86 | if (activity != null) {
87 | activity.runOnUiThread(() -> {
88 | if (keepDeviceAwake) {
89 | requestersKeepingDeviceAwake.add(requesterId);
90 | } else {
91 | requestersKeepingDeviceAwake.remove(requesterId);
92 | }
93 | updateKeepScreenOnFlag(activity);
94 | });
95 | }
96 | }
97 |
98 | // Note: notification properties (e.g. title) can't be changed while it is ongoing.
99 | @ReactMethod
100 | public void setShowOngoingMeetingNotification(boolean showOngoingMeetingNotification, String title, String subtitle, String iconName, String requesterId) {
101 | Activity activity = getCurrentActivity();
102 | if (activity != null) {
103 | activity.runOnUiThread(() -> {
104 | if (showOngoingMeetingNotification) {
105 | requestersShowingOngoingMeetingNotification.add(requesterId);
106 | } else {
107 | requestersShowingOngoingMeetingNotification.remove(requesterId);
108 | }
109 | this.title = title;
110 | this.subtitle = subtitle;
111 | this.iconName = iconName;
112 | updateOngoingMeetingForegroundService();
113 | });
114 | }
115 | }
116 |
117 | private void updateKeepScreenOnFlag(Activity activity) {
118 | Window window = activity.getWindow();
119 | if (requestersKeepingDeviceAwake.size() > 0) {
120 | window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
121 | } else {
122 | window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
123 | }
124 | }
125 |
126 | @Override
127 | public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
128 | if (requestCode == PERMISSION_REQUEST_CODE &&
129 | Arrays.stream(grantResults).allMatch(result -> result == PackageManager.PERMISSION_GRANTED)) {
130 | this.initializeOngoingMeetingForegroundService();
131 | } else {
132 | System.err.println("Failed to grant permissions.");
133 | }
134 | return true;
135 | }
136 |
137 | private List getDeclaredPermissions(Context context) {
138 | List declaredPermissions = new ArrayList<>();
139 | try {
140 | PackageInfo packageInfo = context.getPackageManager().getPackageInfo(
141 | context.getPackageName(),
142 | PackageManager.GET_PERMISSIONS
143 | );
144 | // Check if permissions are requested
145 | if (packageInfo.requestedPermissions != null) {
146 | declaredPermissions = Arrays.asList(packageInfo.requestedPermissions);
147 | }
148 | } catch (PackageManager.NameNotFoundException e) {
149 | e.printStackTrace();
150 | }
151 | return declaredPermissions;
152 | }
153 |
154 | private void checkPermissions() {
155 | Context applicationContext = this.reactContext.getApplicationContext();
156 | List declaredPermissions = this.getDeclaredPermissions(applicationContext);
157 | List permissionList = new ArrayList();
158 | if (declaredPermissions.contains(Manifest.permission.FOREGROUND_SERVICE_CAMERA)) {
159 | permissionList.add(Manifest.permission.CAMERA);
160 | }
161 | if (declaredPermissions.contains(Manifest.permission.FOREGROUND_SERVICE_MICROPHONE)) {
162 | permissionList.add(Manifest.permission.RECORD_AUDIO);
163 | }
164 | if (Build.VERSION.SDK_INT >= 33) {
165 | permissionList.add(Manifest.permission.POST_NOTIFICATIONS);
166 | }
167 |
168 | boolean allPermissionsGranted = true;
169 | for (String permission : permissionList) {
170 | if (ContextCompat.checkSelfPermission(this.reactContext.getApplicationContext(), permission) != PackageManager.PERMISSION_GRANTED) {
171 | allPermissionsGranted = false;
172 | break;
173 | }
174 | }
175 |
176 | if (!allPermissionsGranted) {
177 | Activity currentActivity = this.reactContext.getCurrentActivity();
178 | if (currentActivity instanceof PermissionAwareActivity) {
179 | PermissionAwareActivity permissionAware = (PermissionAwareActivity) currentActivity;
180 | permissionAware.requestPermissions(permissionList.toArray(new String[0]), PERMISSION_REQUEST_CODE, this);
181 | }
182 | } else {
183 | // Permissions are already granted, we can initialize
184 | initializeOngoingMeetingForegroundService();
185 | }
186 | }
187 |
188 | private void initializeOngoingMeetingForegroundService(){
189 | Activity activity = getCurrentActivity();
190 | if (activity != null) {
191 | activity.runOnUiThread(() -> {
192 | DailyOngoingMeetingForegroundService.start(activity.getClass(), title, subtitle, iconName, reactContext);
193 | });
194 | }
195 | }
196 |
197 | private void updateOngoingMeetingForegroundService() {
198 | if (!requestersShowingOngoingMeetingNotification.isEmpty()) {
199 | this.checkPermissions();
200 | } else {
201 | DailyOngoingMeetingForegroundService.stop(reactContext);
202 | }
203 | }
204 |
205 | @ReactMethod
206 | public void presentSystemScreenCapturePrompt() {
207 | Log.d(TAG, "presentSystemScreenCapturePrompt is not available on Android");
208 | }
209 |
210 | @ReactMethod
211 | public void requestStopSystemScreenCapture() {
212 | Log.d(TAG, "requestStopSystemScreenCapture is not available on Android");
213 | }
214 |
215 | @ReactMethod
216 | public void isScreenBeingCaptured(Promise promise) {
217 | Log.d(TAG, "isScreenBeingCaptured is not available on Android");
218 | promise.resolve(false);
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-daily-js
2 |
3 | The Daily JavaScript library for React Native.
4 |
5 | ## Minimum OS/SDK versions
6 |
7 | This package introduces some constraints on what OS/SDK versions your project can support:
8 |
9 | - **iOS**: Deployment target >= 12.0
10 | - **Android**: `minSdkVersion` >= 24
11 |
12 | ## Installation
13 |
14 | Install `react-native-daily-js` along with its peer dependencies:
15 |
16 | ```bash
17 | npm i @daily-co/react-native-daily-js @react-native-async-storage/async-storage@^1.15.7 react-native-background-timer@^2.3.1 react-native-get-random-values@^1.9.0
18 | npm i --save-exact @daily-co/react-native-webrtc@124.0.6-daily.1
19 | ```
20 |
21 | Then, follow the below steps to set up your native project on each platform. **Note that these steps assume you're using a version of React Native that supports autolinking (>= 60).**
22 |
23 | > If your project uses [Expo](https://expo.dev/), use the [`config-plugin-rn-daily-js` configuration package](https://www.npmjs.com/package/@daily-co/config-plugin-rn-daily-js) instead of the following steps.
24 |
25 | ### iOS
26 |
27 | Update the `platform` in your `Podfile`, since `@daily-co/react-native-webrtc` only works on iOS 12 and above:
28 |
29 | ```ruby
30 | platform :ios, '12.0'
31 | ```
32 |
33 | > If you wish to **send screen share** from iOS, it only works on **iOS 14** and above. Therefore, in this case, please switch to using iOS 14.0 instead of iOS 12.0.
34 |
35 | Then run:
36 |
37 | ```bash
38 | npx pod-install
39 | ```
40 |
41 | Next, you will need to update your project's `Info.plist` to add three new rows with the following keys:
42 |
43 | - `NSCameraUsageDescription`
44 | - `NSMicrophoneUsageDescription`
45 | - `UIBackgroundModes`
46 |
47 | For the first two key's values, provide user-facing strings explaining why your app is asking for camera and microphone access. **Note that the app will simply crash silently without these.**
48 |
49 | `UIBackgroundModes` is handled slightly differently and will resolve to an array. For its first item, specify the value `voip`. This ensures that audio will continue uninterrupted when your app is sent to the background.
50 |
51 | To add the new rows through Xcode, open the `Info.plist` and add the following three rows:
52 |
53 | | Key | Type | Value |
54 | | -------------------------------------- | ------ | -------------------------------------------------- |
55 | | Privacy - Camera Usage Description | String | "Daily Playground needs camera access to work" |
56 | | Privacy - Microphone Usage Description | String | "Daily Playground needs microphone access to work" |
57 | | Required background modes | Array | 1 item |
58 | | ---> Item 0 | String | "App provides Voice over IP services" |
59 |
60 | If you view the raw file contents of `Info.plist`, it should look like this:
61 |
62 | ```
63 |
64 | ...
65 | NSCameraUsageDescription
66 | Daily Playground needs camera access to work
67 | NSMicrophoneUsageDescription
68 | Daily Playground needs microphone access to work
69 | UIBackgroundModes
70 |
71 | voip
72 |
73 | ...
74 |
75 | ```
76 |
77 | #### Screen sharing on iOS
78 |
79 | To use the screen sharing functionality on iOS, you'll need to do a few manual steps to set up Daily's [React Native Screen Share Extension framework](https://github.com/daily-co/rn-screen-share-extension/) (already included in `react-native-daily-js`).
80 | Please refer to its README for a detailed walkthrough.
81 |
82 | ### Android
83 |
84 | Add the following to `AndroidManifest.xml`:
85 |
86 | ```xml
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | // ...
105 |
106 |
107 | ```
108 |
109 | > Note: above, `android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION` is only needed if you wish to do screen sharing.
110 |
111 | Update your `minSdkVersion` in your top-level `build.gradle` file:
112 |
113 | ```groovy
114 | minSdkVersion = 24
115 | ```
116 |
117 | (If you run into any issues, refer to [Github issues](https://github.com/react-native-webrtc/react-native-webrtc/issues/720) like [these](https://github.com/jitsi/jitsi-meet/issues/4778), or the `react-native-webrtc` [installation docs](https://github.com/react-native-webrtc/react-native-webrtc/blob/master/Documentation/AndroidInstallation.md), which walk you through a more complicated process. The simpler process laid out above seems to work in a vanilla modern React Native CLI-based setup).
118 |
119 | ## Usage
120 |
121 | `react-native-daily-js` is the React Native counterpart to `daily-js`, and can be used in pretty much the same way to add video calls to your apps. [Complete documentation for `react-native-daily-js` (as well as `daily-js`) can be found here](https://docs.daily.co/reference#using-the-react-native-daily-js-library).
122 |
123 | ```ts
124 | import Daily from '@daily-co/react-native-daily-js';
125 |
126 | // ...
127 |
128 | // Start joining a call
129 | const call = Daily.createCallObject();
130 | call.join({ url: 'https://your-team.daily.co/allhands' });
131 |
132 | // Listen for events signaling changes to participants or their audio or video.
133 | // - 'participant-joined' and 'participant-left' are for remote participants only
134 | // - 'participant-updated' is for the local participant as well as remote participants
135 | const events: DailyEvent[] = [
136 | 'participant-joined',
137 | 'participant-updated',
138 | 'participant-left',
139 | ];
140 | for (const event of events) {
141 | call.on(event, () => {
142 | for (const participant of Object.values(call.participants())) {
143 | console.log('---');
144 | console.log(`participant ${participant.user_id}:`);
145 | if (participant.local) {
146 | console.log('is local');
147 | }
148 | if (participant.audio) {
149 | console.log('audio enabled', participant.audioTrack);
150 | }
151 | if (participant.video) {
152 | console.log('video enabled', participant.videoTrack);
153 | }
154 | }
155 | });
156 | }
157 | ```
158 |
159 | ```tsx
160 | import { DailyMediaView } from '@daily-co/react-native-daily-js';
161 |
162 | // ...
163 |
164 | ;
171 | ```
172 |
173 | [See this blog post for a more thorough walkthrough of structuring a React video-chat app powered by Daily](https://www.daily.co/blog/building-a-custom-video-chat-app-with-react). It's focused on React web, but most of it should also apply to your React Native app.
174 |
175 | ## Assorted considerations for app implementers
176 |
177 | ### Participant count
178 |
179 | Keep in mind that simultaneously displaying videos for numerous participants can use up a lot of your phone's resources. This can make the app run more slowly and use up more of your battery, especially if you're using an older device. For larger meetings, consider only displaying a few videos at a time. In a future release, `daily-js`'s [track subscription APIs](https://www.daily.co/blog/create-dynamic-meetings-using-track-subscriptions/) will be brought over to `react-native-daily-js`, letting you avoid the network bandwidth cost of receiving video for off-screen participants.
180 |
181 | ### Device orientation
182 |
183 | If you'd like to support rotating your phone while in a call, it's recommended that you do not restrict your app's supported orientations (i.e. through `Info.plist` or `AndroidManifest.xml`).
184 |
185 | If you _are_ restricting your app's supported orientations (to portrait, for example), note that by default you'll end up with somewhat divergent video behavior on iOS and Android whenever you _do_ rotate your phone (to landscape, for example).
186 |
187 | On iOS, your captured video will rotate to compensate for the phone's rotation, meaning that:
188 |
189 | - Other participants will continue to see you upright
190 | - Your local video will be oriented the same way as other participant's videos on your phone (so you'd now need to cock your head to see it upright)
191 |
192 | On Android, your captured video will _not_ rotate to compensate for the phone's rotation, meaning that:
193 |
194 | - Other participants will now see you sideways
195 | - Your local video will _not_ be oriented the same way as other participant's videos on your phone (it will appear upright to you)
196 |
--------------------------------------------------------------------------------
/ios/DailyNativeUtils.m:
--------------------------------------------------------------------------------
1 | #import "DailyNativeUtils.h"
2 | #import
3 | #import
4 | #import
5 | #import
6 | #import
7 |
8 | // Notification from extension
9 | static NSString *const NotificationScreenCaptureStoppedBySystemUIOrError = @"ScreenCaptureStoppedBySystemUIOrError";
10 | static NSString *const NotificationScreenCaptureExtensionStarted = @"ScreenCaptureExtensionStarted";
11 | // Event to JS layer
12 | static NSString *const EventSystemScreenCaptureStop = @"EventSystemScreenCaptureStop";
13 | static NSString *const EventSystemScreenCaptureStart = @"EventSystemScreenCaptureStart";
14 | static NSString *const EventOnHostDestroy = @"EventOnHostDestroy";
15 |
16 | @interface DailyNativeUtils()
17 |
18 | // Expects to only be accessed on main thread
19 | @property (nonatomic, strong) NSMutableSet *requestersKeepingDeviceAwake;
20 |
21 | @end
22 |
23 | @implementation DailyNativeUtils
24 |
25 | RCT_EXPORT_MODULE()
26 |
27 | - (instancetype)init
28 | {
29 | if (self = [super init]) {
30 | _requestersKeepingDeviceAwake = [NSMutableSet set];
31 |
32 | // The “Darwin” notification center distributes notifications across the whole system!
33 | // This means you can send and receive notifications between different apps, and/or an app and its extensions.
34 | // Register for screen capture stopped notification.
35 | CFNotificationCenterRef notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
36 | CFNotificationCenterAddObserver(notificationCenter,
37 | (__bridge const void *)(self),
38 | handleDarwinNotification,
39 | (__bridge CFStringRef)(NotificationScreenCaptureStoppedBySystemUIOrError),
40 | NULL,
41 | CFNotificationSuspensionBehaviorDeliverImmediately);
42 | [[NSNotificationCenter defaultCenter] addObserver:self
43 | selector:@selector(handleNotificationScreenCaptureStoppedBySystemUIOrError)
44 | name:NotificationScreenCaptureStoppedBySystemUIOrError
45 | object:nil];
46 | // Adding observer so we can be aware when the screen capture has started
47 | CFNotificationCenterAddObserver(notificationCenter,
48 | (__bridge const void *)(self),
49 | handleDarwinNotification,
50 | (__bridge CFStringRef)(NotificationScreenCaptureExtensionStarted),
51 | NULL,
52 | CFNotificationSuspensionBehaviorDeliverImmediately);
53 | [[NSNotificationCenter defaultCenter] addObserver:self
54 | selector:@selector(handleNotificationScreenCaptureExtensionStarted)
55 | name:NotificationScreenCaptureExtensionStarted
56 | object:nil];
57 |
58 | [[NSNotificationCenter defaultCenter] addObserver:self
59 | selector:@selector(applicationWillTerminate)
60 | name:UIApplicationWillTerminateNotification
61 | object:nil];
62 | }
63 | return self;
64 | }
65 |
66 | - (void)applicationWillTerminate {
67 | NSLog(@"App is about to terminate");
68 | BOOL isStillOnMeeting = _requestersKeepingDeviceAwake.count > 0;
69 | if (isStillOnMeeting) {
70 | [self sendEventWithName:EventOnHostDestroy body:nil];
71 | NSLog(@"Triggered the event EventOnHostDestroy");
72 | }
73 | }
74 |
75 | + (BOOL)requiresMainQueueSetup
76 | {
77 | return NO;
78 | }
79 |
80 |
81 | - (NSArray *)supportedEvents
82 | {
83 | return @[EventSystemScreenCaptureStop, EventSystemScreenCaptureStart, EventOnHostDestroy];
84 | }
85 |
86 | #pragma mark Public
87 |
88 | RCT_EXPORT_METHOD(setKeepDeviceAwake:(BOOL)keepDeviceAwake onBehalfOfRequester:(nonnull NSString *)requesterId)
89 | {
90 | [DailyNativeUtils dispatchSyncOnMain:^{
91 | if (keepDeviceAwake) {
92 | [self.requestersKeepingDeviceAwake addObject:requesterId];
93 | }
94 | else {
95 | [self.requestersKeepingDeviceAwake removeObject:requesterId];
96 | }
97 | [self updateIdleTimer];
98 | }];
99 | }
100 |
101 | RCT_REMAP_METHOD(getDeviceCode,
102 | getDeviceCodewithResolver:(RCTPromiseResolveBlock)resolve
103 | rejecter:(RCTPromiseRejectBlock)reject) {
104 | struct utsname systemInfo;
105 | uname(&systemInfo);
106 | resolve([NSString stringWithCString:systemInfo.machine
107 | encoding:NSUTF8StringEncoding]);
108 | }
109 |
110 | //We are implementing here basically the same approach that react-native-webrtc did:
111 | //https://github.com/daily-co/react-native-webrtc/blob/main/ios/RCTWebRTC/ScreenCapturePickerViewManager.m
112 | //The difference is, with this approach, we don't need the user to create a screen and add in the app somewhere
113 | //we are only invoking this method and creating the screen whe the user invokes to startScreenShare.
114 | RCT_EXPORT_METHOD(presentSystemScreenCapturePrompt)
115 | {
116 | dispatch_async(dispatch_get_main_queue(), ^{
117 | NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
118 | NSString *screenCaptureExtensionBundleIdentifier = infoDictionary[@"DailyScreenCaptureExtensionBundleIdentifier"];
119 |
120 | // Simulate a click on a RPSystemBroadcastPickerView. This is ugly, but it's
121 | // the only way to programmatically trigger the system screen capture
122 | // prompt (RPBroadcastActivityViewController doesn't do what I expected it
123 | // to: https://stackoverflow.com/questions/65658319/replaykit-broadcast-upload-extension-service-not-found)
124 |
125 | RPSystemBroadcastPickerView *screenCaptureButton = [[RPSystemBroadcastPickerView alloc] init];
126 | screenCaptureButton.preferredExtension = screenCaptureExtensionBundleIdentifier;
127 | screenCaptureButton.showsMicrophoneButton = false;
128 | screenCaptureButton.userInteractionEnabled = false;
129 |
130 | // Code taken from ScreenCapturePickerViewManager
131 | // Simulate a click
132 | UIButton *btn = nil;
133 |
134 | for (UIView *subview in ((RPSystemBroadcastPickerView *)screenCaptureButton).subviews) {
135 | if ([subview isKindOfClass:[UIButton class]]) {
136 | btn = (UIButton *)subview;
137 | }
138 | }
139 | if (btn != nil) {
140 | [btn sendActionsForControlEvents:UIControlEventTouchUpInside];
141 | }
142 | else {
143 | RCTLogError(@"RPSystemBroadcastPickerView button not found");
144 | }
145 | });
146 | }
147 |
148 | RCT_EXPORT_METHOD(requestStopSystemScreenCapture)
149 | {
150 | dispatch_async(dispatch_get_main_queue(), ^{
151 | // The “Darwin” notification center distributes notifications across the whole system!
152 | // This means you can send and receive notifications between different apps, and/or an app and its extensions.
153 | // Register for screen capture stopped notification.
154 | CFNotificationCenterRef notification = CFNotificationCenterGetDarwinNotifyCenter ();
155 | CFNotificationCenterPostNotification(notification, (__bridge CFStringRef)(@"MustStopScreenCapture"), NULL, NULL, YES);
156 | });
157 | }
158 |
159 | RCT_REMAP_METHOD(isScreenBeingCaptured,
160 | withResolver:(RCTPromiseResolveBlock)resolve
161 | withRejecter:(RCTPromiseRejectBlock)reject)
162 | {
163 | if (@available(iOS 11.0, *)) {
164 | resolve(@(UIScreen.mainScreen.isCaptured));
165 | } else {
166 | resolve(false);
167 | }
168 | }
169 |
170 | RCT_EXPORT_METHOD(setShowOngoingMeetingNotification:(BOOL)show
171 | title:(NSString *)title
172 | subtitle:(NSString *)subtitle
173 | iconName:(NSString *)iconName
174 | requesterId:(NSString *)requesterId)
175 | {
176 | RCTLogInfo(@"setShowOngoingMeetingNotification is not available on iOS");
177 | }
178 |
179 | #pragma mark Private
180 |
181 | + (void)dispatchSyncOnMain:(void (^)(void))block
182 | {
183 | if ([NSThread isMainThread]) {
184 | block();
185 | } else {
186 | dispatch_sync(dispatch_get_main_queue(), block);
187 | }
188 | }
189 |
190 | // Expects to be invoked from main thread
191 | - (void)updateIdleTimer
192 | {
193 | BOOL disableIdleTimer = _requestersKeepingDeviceAwake.count > 0;
194 | [[UIApplication sharedApplication] setIdleTimerDisabled:disableIdleTimer];
195 | }
196 |
197 | // Translate Darwin notification to NSNotification so it can be used in
198 | // Objective-C class. Based on approach taken by MMWormhole and others.
199 | void handleDarwinNotification(CFNotificationCenterRef center,
200 | void * observer,
201 | CFStringRef name,
202 | void const * object,
203 | CFDictionaryRef userInfo) {
204 | NSString *identifier = (__bridge NSString *)name;
205 | [[NSNotificationCenter defaultCenter] postNotificationName:identifier
206 | object:nil
207 | userInfo:nil];
208 | }
209 |
210 | // Technically this notification tells us that the screen capture stopped
211 | // *either* via the system UI or due to an error, but here let's collapse these
212 | // cases into one event, so that the JS client can "sync" to the fact that the
213 | // screen capture occurred outside of its control.
214 | - (void)handleNotificationScreenCaptureStoppedBySystemUIOrError {
215 | [self sendEventWithName:EventSystemScreenCaptureStop body:nil];
216 | }
217 |
218 | - (void)handleNotificationScreenCaptureExtensionStarted {
219 | [self sendEventWithName:EventSystemScreenCaptureStart body:nil];
220 | }
221 |
222 | @end
223 |
--------------------------------------------------------------------------------
/ios/DailyJs.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXCopyFilesBuildPhase section */
10 | 58B511D91A9E6C8500147676 /* CopyFiles */ = {
11 | isa = PBXCopyFilesBuildPhase;
12 | buildActionMask = 2147483647;
13 | dstPath = "include/$(PRODUCT_NAME)";
14 | dstSubfolderSpec = 16;
15 | files = (
16 | );
17 | runOnlyForDeploymentPostprocessing = 0;
18 | };
19 | /* End PBXCopyFilesBuildPhase section */
20 |
21 | /* Begin PBXFileReference section */
22 | 134814201AA4EA6300B7C361 /* libDailyJs.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libDailyJs.a; sourceTree = BUILT_PRODUCTS_DIR; };
23 | /* End PBXFileReference section */
24 |
25 | /* Begin PBXFrameworksBuildPhase section */
26 | 58B511D81A9E6C8500147676 /* Frameworks */ = {
27 | isa = PBXFrameworksBuildPhase;
28 | buildActionMask = 2147483647;
29 | files = (
30 | );
31 | runOnlyForDeploymentPostprocessing = 0;
32 | };
33 | /* End PBXFrameworksBuildPhase section */
34 |
35 | /* Begin PBXGroup section */
36 | 134814211AA4EA7D00B7C361 /* Products */ = {
37 | isa = PBXGroup;
38 | children = (
39 | 134814201AA4EA6300B7C361 /* libDailyJs.a */,
40 | );
41 | name = Products;
42 | sourceTree = "";
43 | };
44 | 58B511D21A9E6C8500147676 = {
45 | isa = PBXGroup;
46 | children = (
47 | 134814211AA4EA7D00B7C361 /* Products */,
48 | );
49 | sourceTree = "";
50 | };
51 | /* End PBXGroup section */
52 |
53 | /* Begin PBXNativeTarget section */
54 | 58B511DA1A9E6C8500147676 /* DailyJs */ = {
55 | isa = PBXNativeTarget;
56 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "DailyJs" */;
57 | buildPhases = (
58 | 58B511D71A9E6C8500147676 /* Sources */,
59 | 58B511D81A9E6C8500147676 /* Frameworks */,
60 | 58B511D91A9E6C8500147676 /* CopyFiles */,
61 | );
62 | buildRules = (
63 | );
64 | dependencies = (
65 | );
66 | name = DailyJs;
67 | productName = RCTDataManager;
68 | productReference = 134814201AA4EA6300B7C361 /* libDailyJs.a */;
69 | productType = "com.apple.product-type.library.static";
70 | };
71 | /* End PBXNativeTarget section */
72 |
73 | /* Begin PBXProject section */
74 | 58B511D31A9E6C8500147676 /* Project object */ = {
75 | isa = PBXProject;
76 | attributes = {
77 | LastUpgradeCheck = 0920;
78 | ORGANIZATIONNAME = Facebook;
79 | TargetAttributes = {
80 | 58B511DA1A9E6C8500147676 = {
81 | CreatedOnToolsVersion = 6.1.1;
82 | };
83 | };
84 | };
85 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "DailyJs" */;
86 | compatibilityVersion = "Xcode 3.2";
87 | developmentRegion = en;
88 | hasScannedForEncodings = 0;
89 | knownRegions = (
90 | en,
91 | Base,
92 | );
93 | mainGroup = 58B511D21A9E6C8500147676;
94 | productRefGroup = 58B511D21A9E6C8500147676;
95 | projectDirPath = "";
96 | projectRoot = "";
97 | targets = (
98 | 58B511DA1A9E6C8500147676 /* DailyJs */,
99 | );
100 | };
101 | /* End PBXProject section */
102 |
103 | /* Begin PBXSourcesBuildPhase section */
104 | 58B511D71A9E6C8500147676 /* Sources */ = {
105 | isa = PBXSourcesBuildPhase;
106 | buildActionMask = 2147483647;
107 | files = (
108 | );
109 | runOnlyForDeploymentPostprocessing = 0;
110 | };
111 | /* End PBXSourcesBuildPhase section */
112 |
113 | /* Begin XCBuildConfiguration section */
114 | 58B511ED1A9E6C8500147676 /* Debug */ = {
115 | isa = XCBuildConfiguration;
116 | buildSettings = {
117 | ALWAYS_SEARCH_USER_PATHS = NO;
118 | CLANG_ANALYZER_NONNULL = YES;
119 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
120 | CLANG_CXX_LIBRARY = "libc++";
121 | CLANG_ENABLE_MODULES = YES;
122 | CLANG_ENABLE_OBJC_ARC = YES;
123 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
124 | CLANG_WARN_BOOL_CONVERSION = YES;
125 | CLANG_WARN_COMMA = YES;
126 | CLANG_WARN_CONSTANT_CONVERSION = YES;
127 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
128 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
129 | CLANG_WARN_EMPTY_BODY = YES;
130 | CLANG_WARN_ENUM_CONVERSION = YES;
131 | CLANG_WARN_INFINITE_RECURSION = YES;
132 | CLANG_WARN_INT_CONVERSION = YES;
133 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
134 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
135 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
136 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
137 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
138 | CLANG_WARN_STRICT_PROTOTYPES = YES;
139 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
140 | CLANG_WARN_UNREACHABLE_CODE = YES;
141 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
142 | COPY_PHASE_STRIP = NO;
143 | ENABLE_STRICT_OBJC_MSGSEND = YES;
144 | ENABLE_TESTABILITY = YES;
145 | GCC_C_LANGUAGE_STANDARD = gnu99;
146 | GCC_DYNAMIC_NO_PIC = NO;
147 | GCC_NO_COMMON_BLOCKS = YES;
148 | GCC_OPTIMIZATION_LEVEL = 0;
149 | GCC_PREPROCESSOR_DEFINITIONS = (
150 | "DEBUG=1",
151 | "$(inherited)",
152 | );
153 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
154 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
155 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
156 | GCC_WARN_UNDECLARED_SELECTOR = YES;
157 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
158 | GCC_WARN_UNUSED_FUNCTION = YES;
159 | GCC_WARN_UNUSED_VARIABLE = YES;
160 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
161 | LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
162 | LIBRARY_SEARCH_PATHS = (
163 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
164 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
165 | "\"$(inherited)\"",
166 | );
167 | MTL_ENABLE_DEBUG_INFO = YES;
168 | ONLY_ACTIVE_ARCH = YES;
169 | SDKROOT = iphoneos;
170 | };
171 | name = Debug;
172 | };
173 | 58B511EE1A9E6C8500147676 /* Release */ = {
174 | isa = XCBuildConfiguration;
175 | buildSettings = {
176 | ALWAYS_SEARCH_USER_PATHS = NO;
177 | CLANG_ANALYZER_NONNULL = YES;
178 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
179 | CLANG_CXX_LIBRARY = "libc++";
180 | CLANG_ENABLE_MODULES = YES;
181 | CLANG_ENABLE_OBJC_ARC = YES;
182 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
183 | CLANG_WARN_BOOL_CONVERSION = YES;
184 | CLANG_WARN_COMMA = YES;
185 | CLANG_WARN_CONSTANT_CONVERSION = YES;
186 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
187 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
188 | CLANG_WARN_EMPTY_BODY = YES;
189 | CLANG_WARN_ENUM_CONVERSION = YES;
190 | CLANG_WARN_INFINITE_RECURSION = YES;
191 | CLANG_WARN_INT_CONVERSION = YES;
192 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
193 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
194 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
195 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
196 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
197 | CLANG_WARN_STRICT_PROTOTYPES = YES;
198 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
199 | CLANG_WARN_UNREACHABLE_CODE = YES;
200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
201 | COPY_PHASE_STRIP = YES;
202 | ENABLE_NS_ASSERTIONS = NO;
203 | ENABLE_STRICT_OBJC_MSGSEND = YES;
204 | GCC_C_LANGUAGE_STANDARD = gnu99;
205 | GCC_NO_COMMON_BLOCKS = YES;
206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
208 | GCC_WARN_UNDECLARED_SELECTOR = YES;
209 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
210 | GCC_WARN_UNUSED_FUNCTION = YES;
211 | GCC_WARN_UNUSED_VARIABLE = YES;
212 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
213 | LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
214 | LIBRARY_SEARCH_PATHS = (
215 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
216 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"",
217 | "\"$(inherited)\"",
218 | );
219 | MTL_ENABLE_DEBUG_INFO = NO;
220 | SDKROOT = iphoneos;
221 | VALIDATE_PRODUCT = YES;
222 | };
223 | name = Release;
224 | };
225 | 58B511F01A9E6C8500147676 /* Debug */ = {
226 | isa = XCBuildConfiguration;
227 | buildSettings = {
228 | HEADER_SEARCH_PATHS = (
229 | "$(inherited)",
230 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
231 | "$(SRCROOT)/../../../React/**",
232 | "$(SRCROOT)/../../react-native/React/**",
233 | );
234 | LIBRARY_SEARCH_PATHS = "$(inherited)";
235 | OTHER_LDFLAGS = "-ObjC";
236 | PRODUCT_NAME = DailyJs;
237 | SKIP_INSTALL = YES;
238 | };
239 | name = Debug;
240 | };
241 | 58B511F11A9E6C8500147676 /* Release */ = {
242 | isa = XCBuildConfiguration;
243 | buildSettings = {
244 | HEADER_SEARCH_PATHS = (
245 | "$(inherited)",
246 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
247 | "$(SRCROOT)/../../../React/**",
248 | "$(SRCROOT)/../../react-native/React/**",
249 | );
250 | LIBRARY_SEARCH_PATHS = "$(inherited)";
251 | OTHER_LDFLAGS = "-ObjC";
252 | PRODUCT_NAME = DailyJs;
253 | SKIP_INSTALL = YES;
254 | };
255 | name = Release;
256 | };
257 | /* End XCBuildConfiguration section */
258 |
259 | /* Begin XCConfigurationList section */
260 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "DailyJs" */ = {
261 | isa = XCConfigurationList;
262 | buildConfigurations = (
263 | 58B511ED1A9E6C8500147676 /* Debug */,
264 | 58B511EE1A9E6C8500147676 /* Release */,
265 | );
266 | defaultConfigurationIsVisible = 0;
267 | defaultConfigurationName = Release;
268 | };
269 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "DailyJs" */ = {
270 | isa = XCConfigurationList;
271 | buildConfigurations = (
272 | 58B511F01A9E6C8500147676 /* Debug */,
273 | 58B511F11A9E6C8500147676 /* Release */,
274 | );
275 | defaultConfigurationIsVisible = 0;
276 | defaultConfigurationName = Release;
277 | };
278 | /* End XCConfigurationList section */
279 | };
280 | rootObject = 58B511D31A9E6C8500147676 /* Project object */;
281 | }
282 |
--------------------------------------------------------------------------------
/type-overrides/@daily-co/daily-js/index.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for react-native-daily-js
2 | // Project: https://github.com/daily-co/react-native-daily-js
3 | // Definitions by: Paul Kompfner
4 |
5 | /**
6 | * --- REACT-NATIVE-SPECIFIC TYPES ---
7 | */
8 |
9 | import {
10 | MediaStreamTrack,
11 | MediaDeviceInfo,
12 | MediaTrackConstraints,
13 | } from '@daily-co/react-native-webrtc';
14 |
15 | import RTCRtpEncodingParameters from '@daily-co/react-native-webrtc/lib/typescript/RTCRtpEncodingParameters';
16 |
17 | /**
18 | * --- DAILY-JS API EXPOSED VIA REACT-NATIVE-DAILY-JS ---
19 | */
20 |
21 | export type DailyLanguage =
22 | | 'da'
23 | | 'de'
24 | | 'en'
25 | | 'fi'
26 | | 'fr'
27 | | 'nl'
28 | | 'no'
29 | | 'pt'
30 | | 'pl'
31 | | 'ru'
32 | | 'sv'
33 | | 'es'
34 | | 'tr'
35 | | 'it'
36 | | 'ka'
37 | | 'jp';
38 |
39 | export type DailyLanguageSetting = DailyLanguage | 'user';
40 |
41 | export type DailyEvent =
42 | | 'loading'
43 | | 'load-attempt-failed'
44 | | 'loaded'
45 | | 'started-camera'
46 | | 'camera-error'
47 | | 'joining-meeting'
48 | | 'joined-meeting'
49 | | 'left-meeting'
50 | | 'call-instance-destroyed'
51 | | 'participant-joined'
52 | | 'participant-updated'
53 | | 'participant-left'
54 | | 'participant-counts-updated'
55 | | 'track-started'
56 | | 'track-stopped'
57 | | 'recording-started'
58 | | 'recording-stopped'
59 | | 'recording-stats'
60 | | 'recording-error'
61 | | 'transcription-started'
62 | | 'transcription-stopped'
63 | | 'transcription-error'
64 | | 'app-message'
65 | | 'transcription-message'
66 | | 'local-screen-share-started'
67 | | 'local-screen-share-stopped'
68 | | 'local-screen-share-canceled'
69 | | 'active-speaker-change'
70 | | 'network-quality-change'
71 | | 'network-connection'
72 | | 'test-completed'
73 | | 'cpu-load-change'
74 | | 'error'
75 | | 'nonfatal-error'
76 | | 'live-streaming-started'
77 | | 'live-streaming-updated'
78 | | 'live-streaming-stopped'
79 | | 'live-streaming-error'
80 | | 'remote-media-player-started'
81 | | 'remote-media-player-stopped'
82 | | 'remote-media-player-updated'
83 | | 'access-state-updated'
84 | | 'meeting-session-summary-updated'
85 | | 'meeting-session-state-updated'
86 | | 'waiting-participant-added'
87 | | 'waiting-participant-updated'
88 | | 'waiting-participant-removed'
89 | | 'available-devices-updated'
90 | | 'receive-settings-updated'
91 | | 'local-audio-level'
92 | | 'remote-participants-audio-level'
93 | | 'dialin-connected'
94 | | 'dialin-ready'
95 | | 'dialin-error'
96 | | 'dialin-stopped'
97 | | 'dialin-warning'
98 | | 'dialout-connected'
99 | | 'dialout-error'
100 | | 'dialout-stopped'
101 | | 'dialout-warning'
102 | | 'send-settings-updated';
103 |
104 | export type DailyMeetingState =
105 | | 'new'
106 | | 'loading'
107 | | 'loaded'
108 | | 'joining-meeting'
109 | | 'joined-meeting'
110 | | 'left-meeting'
111 | | 'error';
112 |
113 | export type DailyCameraErrorType =
114 | | 'cam-in-use'
115 | | 'mic-in-use'
116 | | 'cam-mic-in-use'
117 | | 'permissions'
118 | | 'undefined-mediadevices'
119 | | 'not-found'
120 | | 'constraints'
121 | | 'unknown';
122 |
123 | export type DailyFatalErrorType =
124 | | 'ejected'
125 | | 'nbf-room'
126 | | 'nbf-token'
127 | | 'exp-room'
128 | | 'exp-token'
129 | | 'no-room'
130 | | 'meeting-full'
131 | | 'end-of-life'
132 | | 'not-allowed'
133 | | 'connection-error';
134 |
135 | export type DailyNonFatalErrorType =
136 | | 'screen-share-error'
137 | | 'local-audio-level-observer-error'
138 | | 'remote-media-player-error'
139 | | 'live-streaming-warning'
140 | | 'meeting-session-data-error';
141 |
142 | export interface DailyParticipantsObject {
143 | local: DailyParticipant;
144 | [id: string]: DailyParticipant;
145 | }
146 |
147 | export interface DailyBrowserInfo {
148 | supported: boolean;
149 | mobile: boolean;
150 | name: string;
151 | version: string;
152 | supportsScreenShare: boolean;
153 | supportsSfu: boolean;
154 | supportsVideoProcessing: boolean;
155 | supportsAudioProcessing: boolean;
156 | }
157 |
158 | export interface DailyCallOptions {
159 | url?: string;
160 | token?: string;
161 | dailyConfig?: DailyAdvancedConfig;
162 | subscribeToTracksAutomatically?: boolean;
163 | reactNativeConfig?: DailyReactNativeConfig;
164 | videoSource?: string | MediaStreamTrack | boolean;
165 | audioSource?: string | MediaStreamTrack | boolean;
166 | receiveSettings?: DailyReceiveSettings;
167 | sendSettings?: DailySendSettings;
168 | userName?: string;
169 | userData?: unknown;
170 | startVideoOff?: boolean;
171 | startAudioOff?: boolean;
172 | }
173 |
174 | export interface DailyLoadOptions extends DailyCallOptions {
175 | baseUrl?: string;
176 | }
177 |
178 | export interface DailyFactoryOptions extends DailyCallOptions {
179 | // only available at constructor time
180 | allowMultipleCallInstances?: boolean;
181 | strictMode?: boolean;
182 | }
183 |
184 | export interface CamSimulcastEncoding {
185 | maxBitrate: number;
186 | maxFramerate?: number;
187 | scaleResolutionDownBy?: number;
188 | }
189 |
190 | /* These types are not defined inside react-native-webrtc, so we need to define them here in order to use */
191 | type RTCIceTransportPolicy = 'all' | 'relay';
192 | type RTCIceCredentialType = 'password';
193 | interface RTCIceServer {
194 | credential?: string;
195 | credentialType?: RTCIceCredentialType;
196 | urls: string | string[];
197 | username?: string;
198 | }
199 |
200 | export interface DailyIceConfig {
201 | iceServers?: RTCIceServer[];
202 | placement?: 'front' | 'back' | 'replace';
203 | iceTransportPolicy?: RTCIceTransportPolicy;
204 | }
205 |
206 | export interface DailyAdvancedConfig {
207 | /**
208 | * @deprecated This property will be removed. Instead, use sendSettings, which is found in DailyCallOptions.
209 | */
210 | camSimulcastEncodings?: CamSimulcastEncoding[];
211 | /**
212 | * @deprecated This property will be removed. All calls use v2CamAndMic.
213 | */
214 | v2CamAndMic?: boolean;
215 | micAudioMode?: 'music' | 'speech';
216 | userMediaAudioConstraints?: MediaTrackConstraints;
217 | userMediaVideoConstraints?: MediaTrackConstraints;
218 | preferH264ForCam?: boolean;
219 | h264Profile?: string;
220 | proxyUrl?: string;
221 | iceConfig?: DailyIceConfig;
222 | }
223 |
224 | export interface DailyReactNativeConfig {
225 | androidInCallNotification?: {
226 | title?: string;
227 | subtitle?: string;
228 | iconName?: string;
229 | disableForCustomOverride?: boolean;
230 | };
231 | disableAutoDeviceManagement?: {
232 | audio?: boolean;
233 | video?: boolean;
234 | };
235 | }
236 |
237 | export interface DailyTrackState {
238 | subscribed: DailyTrackSubscriptionState;
239 | state:
240 | | 'blocked'
241 | | 'off'
242 | | 'sendable'
243 | | 'loading'
244 | | 'interrupted'
245 | | 'playable';
246 | blocked?: {
247 | byDeviceMissing?: boolean;
248 | byDeviceInUse?: boolean;
249 | byPermissions?: boolean;
250 | };
251 | off?: {
252 | byUser?: boolean;
253 | byRemoteRequest?: boolean;
254 | byBandwidth?: boolean;
255 | byCanSendPermission?: boolean;
256 | byServerLimit?: boolean;
257 | };
258 | // guaranteed-playable reference to the track
259 | // (it's only present when state === 'playable')
260 | track?: MediaStreamTrack;
261 | // not-guaranteed-playable reference to the track
262 | // (it may be present when state !== 'playable')
263 | persistentTrack?: MediaStreamTrack;
264 | }
265 |
266 | export type DailyParticipantPermissionsCanSendValues =
267 | | 'video'
268 | | 'audio'
269 | | 'screenVideo'
270 | | 'screenAudio'
271 | | 'customVideo'
272 | | 'customAudio';
273 |
274 | export type DailyParticipantPermissionsCanAdminValues =
275 | | 'participants'
276 | | 'streaming'
277 | | 'transcription';
278 |
279 | export type DailyParticipantTypeValues =
280 | | 'remote-media-player'
281 | | 'sip-dial-in'
282 | | 'sip-dial-out'
283 | | 'pstn-dial-in'
284 | | 'pstn-dial-out'
285 | | 'unknown';
286 |
287 | export interface DailyParticipantCanReceiveMediaPermissionFull {
288 | video: boolean;
289 | audio: boolean;
290 | screenVideo: boolean;
291 | screenAudio: boolean;
292 | customVideo: { '*': boolean; [key: string]: boolean };
293 | customAudio: { '*': boolean; [key: string]: boolean };
294 | }
295 |
296 | export interface DailyParticipantCanReceiveMediaPermissionPartial {
297 | video?: boolean;
298 | audio?: boolean;
299 | screenVideo?: boolean;
300 | screenAudio?: boolean;
301 | customVideo?: { [key: string]: boolean };
302 | customAudio?: { [key: string]: boolean };
303 | }
304 |
305 | export interface DailyParticipantCanReceivePermission {
306 | base: DailyParticipantCanReceiveMediaPermissionFull | boolean;
307 | byUserId?: {
308 | [key: string]: DailyParticipantCanReceiveMediaPermissionPartial | boolean;
309 | };
310 | byParticipantId?: {
311 | [key: string]: DailyParticipantCanReceiveMediaPermissionPartial | boolean;
312 | };
313 | }
314 |
315 | export interface DailyParticipantPermissions {
316 | hasPresence: boolean;
317 | canSend: Set | boolean;
318 | canReceive: DailyParticipantCanReceivePermission;
319 | canAdmin: Set | boolean;
320 | }
321 |
322 | export type DailyParticipantPermissionsUpdate = {
323 | hasPresence?: boolean;
324 | canSend?:
325 | | Array
326 | | Set
327 | | boolean;
328 | canReceive?: Partial;
329 | canAdmin?:
330 | | Array
331 | | Set
332 | | boolean;
333 | };
334 |
335 | export interface DailyParticipantTracks {
336 | audio: DailyTrackState;
337 | video: DailyTrackState;
338 | screenAudio: DailyTrackState;
339 | screenVideo: DailyTrackState;
340 | [customTrackKey: string]: DailyTrackState | undefined;
341 | }
342 |
343 | export interface DailyParticipant {
344 | /**
345 | * @deprecated This property will be removed. Use tracks.audio.persistentTrack instead.
346 | */
347 | audioTrack?: MediaStreamTrack | false;
348 | /**
349 | * @deprecated This property will be removed. Use tracks.video.persistentTrack instead.
350 | */
351 | videoTrack?: MediaStreamTrack | false;
352 | /**
353 | * @deprecated This property will be removed. Use tracks.screenVideo.persistentTrack instead.
354 | */
355 | screenVideoTrack?: MediaStreamTrack | false;
356 | /**
357 | * @deprecated This property will be removed. Use tracks.screenAudio.persistentTrack instead.
358 | */
359 | screenAudioTrack?: MediaStreamTrack | false;
360 |
361 | /**
362 | * @deprecated This property will be removed. Use tracks.audio.state instead.
363 | */
364 | audio: boolean;
365 | /**
366 | * @deprecated This property will be removed. Use tracks.video.state instead.
367 | */
368 | video: boolean;
369 | /**
370 | * @deprecated This property will be removed. Use tracks.screenVideo.state instead.
371 | */
372 | screen: boolean;
373 |
374 | // track state
375 | tracks: DailyParticipantTracks;
376 |
377 | // user/session info
378 | user_id: string;
379 | user_name: string;
380 | userData?: unknown;
381 | session_id: string;
382 | joined_at?: Date;
383 | networkQualityState?: 'good' | 'warning' | 'bad' | 'unknown';
384 | /**
385 | * @deprecated This property is being replaced by networkState.
386 | */
387 | networkThreshold?: 'good' | 'low' | 'very-low';
388 | will_eject_at: Date;
389 | local: boolean;
390 | owner: boolean;
391 | permissions: DailyParticipantPermissions;
392 | record: boolean;
393 | participantType?: DailyParticipantTypeValues;
394 |
395 | // video element info (iframe-based calls using standard UI only)
396 | /**
397 | * @deprecated This property will be removed. Refer to tracks.video instead.
398 | */
399 | cam_info: {} | DailyVideoElementInfo;
400 | /**
401 | * @deprecated This property will be removed. Refer to tracks.screenVideo instead.
402 | */
403 | screen_info: {} | DailyVideoElementInfo;
404 | }
405 |
406 | export interface DailyParticipantCounts {
407 | present: number;
408 | hidden: number;
409 | }
410 |
411 | export interface DailyWaitingParticipant {
412 | id: string;
413 | name: string;
414 | awaitingAccess: SpecifiedDailyAccess;
415 | }
416 |
417 | export type DailyTrackSubscriptionState = 'staged' | boolean;
418 |
419 | export type DailyCustomTrackSubscriptionState =
420 | | DailyTrackSubscriptionState
421 | | { [name: string]: DailyTrackSubscriptionState };
422 |
423 | export type DailyTrackSubscriptionOptions =
424 | | DailyTrackSubscriptionState
425 | | {
426 | audio?: DailyTrackSubscriptionState;
427 | video?: DailyTrackSubscriptionState;
428 | screenVideo?: DailyTrackSubscriptionState;
429 | screenAudio?: DailyTrackSubscriptionState;
430 | custom?: DailyCustomTrackSubscriptionState;
431 | };
432 |
433 | export interface DailyParticipantUpdateOptions {
434 | setAudio?: boolean;
435 | setVideo?: boolean;
436 | setScreenShare?: false;
437 | setSubscribedTracks?: DailyTrackSubscriptionOptions;
438 | eject?: true;
439 | updatePermissions?: DailyParticipantPermissionsUpdate;
440 | }
441 |
442 | export interface DailyWaitingParticipantUpdateOptions {
443 | grantRequestedAccess?: boolean;
444 | }
445 |
446 | /**
447 | * @deprecated
448 | * All properties will be removed as cam_info and screen_info are also deprecated.
449 | * Use the participants() object's tracks property to retrieve track information instead.
450 | * e.g. call.participants()['participant-id'].tracks.video.persistentTrack.getSettings()
451 | */
452 | export interface DailyVideoElementInfo {
453 | width: number;
454 | height: number;
455 | left: number;
456 | top: number;
457 | video_width: number;
458 | video_height: number;
459 | }
460 |
461 | /**
462 | * DailyDeviceInfos reports the device information for the camera, mic, and
463 | * speaker currently in use or expected. If the given device has not been
464 | * specified and has not yet been acquired, the object will be empty ({}).
465 | * The object will also be empty if a custom track has been provided.
466 | */
467 | export interface DailyDeviceInfos {
468 | camera: {} | MediaDeviceInfo;
469 | mic: {} | MediaDeviceInfo;
470 | speaker: {} | MediaDeviceInfo;
471 | }
472 |
473 | export interface DailyStartScreenShare {
474 | //RN does not support any displayMediaOptions, that is why we have not added the definition here
475 | screenVideoSendSettings?:
476 | | DailyVideoSendSettings
477 | | DailyScreenVideoSendSettingsPreset;
478 | }
479 |
480 | export type DailyQualityTestResult =
481 | | 'good'
482 | | 'bad'
483 | | 'warning'
484 | | 'aborted'
485 | | 'failed';
486 |
487 | export type DailyP2PCallQualityTestResults =
488 | | DailyP2PCallQualityTestStats
489 | | DailyCallQualityTestAborted
490 | | DailyCallQualityTestFailure;
491 |
492 | export interface DailyP2PCallQualityTestStats {
493 | result: Extract;
494 | data: DailyP2PCallQualityTestData;
495 | secondsElapsed: number;
496 | }
497 |
498 | export interface DailyP2PCallQualityTestData {
499 | maxRoundTripTime: number | null;
500 | avgRoundTripTime: number | null;
501 | avgRecvPacketLoss: number | null;
502 | avgAvailableOutgoingBitrate: number | null;
503 | avgSendBitsPerSecond: number | null;
504 | avgRecvBitsPerSecond: number | null;
505 | }
506 |
507 | export interface DailyCallQualityTestAborted {
508 | result: Extract;
509 | secondsElapsed: number;
510 | }
511 |
512 | export interface DailyCallQualityTestFailure {
513 | result: Extract;
514 | errorMsg: string;
515 | error?: DailyFatalErrorObject;
516 | secondsElapsed: number;
517 | }
518 |
519 | export interface DailyWebsocketConnectivityTestResults {
520 | result: 'passed' | 'failed' | 'warning' | 'aborted';
521 | abortedRegions: string[];
522 | failedRegions: string[];
523 | passedRegions: string[];
524 | }
525 |
526 | export interface DailyNetworkConnectivityTestStats {
527 | result: 'passed' | 'failed' | 'aborted';
528 | }
529 |
530 | export type networkStateReasons =
531 | | 'sendPacketLoss'
532 | | 'recvPacketLoss'
533 | | 'roundTripTime'
534 | | 'availableOutgoingBitrate';
535 |
536 | export interface DailyNetworkStatsData {
537 | latest: {
538 | timestamp: number;
539 | recvBitsPerSecond: number | null;
540 | sendBitsPerSecond: number | null;
541 | availableOutgoingBitrate: number | null;
542 | networkRoundTripTime: number | null;
543 | videoRecvBitsPerSecond: number | null;
544 | videoSendBitsPerSecond: number | null;
545 | audioRecvBitsPerSecond: number | null;
546 | audioSendBitsPerSecond: number | null;
547 | videoRecvPacketLoss: number | null;
548 | videoSendPacketLoss: number | null;
549 | audioRecvPacketLoss: number | null;
550 | audioSendPacketLoss: number | null;
551 | totalSendPacketLoss: number | null;
552 | totalRecvPacketLoss: number | null;
553 | videoRecvJitter: number | null;
554 | videoSendJitter: number | null;
555 | audioRecvJitter: number | null;
556 | audioSendJitter: number | null;
557 | };
558 | worstVideoRecvPacketLoss: number;
559 | worstVideoSendPacketLoss: number;
560 | worstAudioRecvPacketLoss: number;
561 | worstAudioSendPacketLoss: number;
562 | worstVideoRecvJitter: number;
563 | worstVideoSendJitter: number;
564 | worstAudioRecvJitter: number;
565 | worstAudioSendJitter: number;
566 | averageNetworkRoundTripTime: number | null;
567 | }
568 | export interface DailyNetworkStats {
569 | networkState: 'good' | 'warning' | 'bad' | 'unknown';
570 | networkStateReasons: networkStateReasons[];
571 | stats: Record | DailyNetworkStatsData;
572 | /**
573 | * @deprecated This property is being replaced by networkState.
574 | */
575 | quality: number;
576 | /**
577 | * @deprecated This property is being replaced by networkState.
578 | */
579 | threshold: 'good' | 'low' | 'very-low';
580 | }
581 |
582 | export interface DailyCpuLoadStats {
583 | cpuLoadState: 'low' | 'high';
584 | cpuLoadStateReason: 'encode' | 'decode' | 'scheduleDuration' | 'none'; // We are currently not using the Inter frame Delay to change the cpu load state
585 | stats: {
586 | latest: {
587 | timestamp: number;
588 | scheduleDuration: number;
589 | frameEncodeTimeSec: number;
590 | targetEncodeFrameRate: number;
591 | targetDecodeFrameRate: number;
592 | targetScheduleDuration: number;
593 | cpuUsageBasedOnTargetEncode: number;
594 | cpuUsageBasedOnGlobalDecode: number;
595 | avgFrameDecodeTimeSec: number;
596 | avgInterFrameDelayStandardDeviation: number;
597 | totalReceivedVideoTracks: number;
598 | cpuInboundVideoStats: {
599 | trackId: string;
600 | ssrc: number;
601 | frameWidth: number;
602 | frameHeight: number;
603 | fps: number;
604 | frameDecodeTimeSec: number;
605 | interFrameDelayStandardDeviation: number;
606 | cpuUsageBasedOnTargetDecode: number;
607 | }[];
608 | };
609 | };
610 | }
611 |
612 | interface DailySendSettings {
613 | video?: DailyVideoSendSettings | DailyVideoSendSettingsPreset;
614 | customVideoDefaults?: DailyVideoSendSettings | DailyVideoSendSettingsPreset;
615 | [customKey: string]:
616 | | DailyVideoSendSettings
617 | | DailyVideoSendSettingsPreset
618 | | undefined;
619 | }
620 |
621 | export interface DailyParticipantsAudioLevel {
622 | [participantId: string]: number;
623 | }
624 |
625 | export type DailyVideoSendSettingsPreset =
626 | | 'default'
627 | | 'bandwidth-optimized'
628 | | 'quality-optimized';
629 |
630 | // Media Track Send Settings
631 | interface DailyVideoSendSettings {
632 | maxQuality?: 'low' | 'medium' | 'high';
633 | encodings?: {
634 | low: RTCRtpEncodingParameters;
635 | medium: RTCRtpEncodingParameters;
636 | high: RTCRtpEncodingParameters;
637 | };
638 | }
639 |
640 | export type DailyScreenVideoSendSettingsPreset =
641 | | 'default-screen-video'
642 | | 'detail-optimized'
643 | | 'motion-optimized'
644 | | 'motion-and-detail-balanced';
645 |
646 | export interface DailyPendingRoomInfo {
647 | roomUrlPendingJoin: string;
648 | }
649 |
650 | export interface DailyRoomInfo {
651 | id: string;
652 | name: string;
653 | config: {
654 | nbf?: number;
655 | exp?: number;
656 | max_participants?: number;
657 | enable_screenshare?: boolean;
658 | enable_breakout_rooms?: boolean;
659 | enable_emoji_reactions?: boolean;
660 | enable_chat?: boolean;
661 | enable_shared_chat_history?: boolean;
662 | enable_knocking?: boolean;
663 | enable_network_ui?: boolean;
664 | enable_noise_cancellation_ui?: boolean;
665 | enable_people_ui?: boolean;
666 | enable_pip_ui?: boolean;
667 | enable_hand_raising?: boolean;
668 | enable_prejoin_ui?: boolean;
669 | enable_video_processing_ui?: boolean;
670 | start_video_off?: boolean;
671 | start_audio_off?: boolean;
672 | owner_only_broadcast?: boolean;
673 | audio_only?: boolean;
674 | enable_recording?: string;
675 | enable_dialin?: boolean;
676 | enable_dialout?: boolean;
677 | /**
678 | * @deprecated This property will be removed.
679 | * All calls are treated as autojoin.
680 | */
681 | autojoin?: boolean;
682 | eject_at_room_exp?: boolean;
683 | eject_after_elapsed?: number;
684 | lang?: '' | DailyLanguageSetting;
685 | sfu_switchover?: number;
686 | /**
687 | * @deprecated This property will be removed.
688 | * All calls use websocket signaling ('ws').
689 | */
690 | signaling_impl?: string;
691 | geo?: string;
692 | };
693 | domainConfig: {
694 | hide_daily_branding?: boolean;
695 | redirect_on_meeting_exit?: string;
696 | hipaa?: boolean;
697 | sfu_impl?: string;
698 | signaling_impl?: string;
699 | sfu_switchover?: number;
700 | lang?: '' | DailyLanguageSetting;
701 | max_api_rooms?: number;
702 | webhook_meeting_end?: any;
703 | max_live_streams?: number;
704 | max_streaming_instances_per_room?: number;
705 | enable_breakout_rooms?: boolean;
706 | enable_emoji_reactions?: boolean;
707 | enable_network_ui?: boolean;
708 | enable_noise_cancellation_ui?: boolean;
709 | enable_people_ui?: boolean;
710 | enable_pip_ui?: boolean;
711 | enable_hand_raising?: boolean;
712 | enable_prejoin_ui?: boolean;
713 | enable_video_processing_ui?: boolean;
714 | };
715 | tokenConfig: {
716 | eject_at_token_exp?: boolean;
717 | eject_after_elapsed?: number;
718 | nbf?: number;
719 | exp?: number;
720 | is_owner?: boolean;
721 | user_name?: string;
722 | user_id?: string;
723 | enable_screenshare?: boolean;
724 | start_video_off?: boolean;
725 | start_audio_off?: boolean;
726 | enable_recording?: string;
727 | start_cloud_recording?: boolean;
728 | close_tab_on_exit?: boolean;
729 | redirect_on_meeting_exit?: string;
730 | lang?: '' | DailyLanguageSetting;
731 | };
732 | dialInPIN?: string;
733 | }
734 |
735 | export interface DailyMeetingSessionSummary {
736 | id: string;
737 | }
738 |
739 | export interface DailyMeetingSessionState {
740 | data: unknown;
741 | }
742 |
743 | export type DailySessionDataMergeStrategy = 'replace' | 'shallow-merge';
744 |
745 | export interface DailyVideoReceiveSettings {
746 | layer?: number;
747 | }
748 | export interface DailySingleParticipantReceiveSettings {
749 | video?: DailyVideoReceiveSettings;
750 | screenVideo?: DailyVideoReceiveSettings;
751 | }
752 |
753 | export interface DailyReceiveSettings {
754 | [participantIdOrBase: string]: DailySingleParticipantReceiveSettings;
755 | }
756 |
757 | export interface DailyVideoReceiveSettingsUpdates {
758 | layer?: number | 'inherit';
759 | }
760 |
761 | export interface DailySingleParticipantReceiveSettingsUpdates {
762 | video?: DailyVideoReceiveSettingsUpdates | 'inherit';
763 | screenVideo?: DailyVideoReceiveSettingsUpdates | 'inherit';
764 | }
765 |
766 | export interface DailyReceiveSettingsUpdates {
767 | [participantIdOrBaseOrStar: string]:
768 | | DailySingleParticipantReceiveSettingsUpdates
769 | | 'inherit';
770 | }
771 |
772 | export interface DailyInputSettings {
773 | audio?: DailyInputAudioSettings;
774 | video?: DailyInputVideoSettings;
775 | }
776 |
777 | export interface DailyCustomTrackSettings {
778 | customTrack: MediaStreamTrack;
779 | }
780 |
781 | export interface DailyInputAudioSettings {
782 | settings?: MediaTrackConstraints | DailyCustomTrackSettings;
783 | }
784 |
785 | export interface DailyInputVideoSettings {
786 | settings?: MediaTrackConstraints | DailyCustomTrackSettings;
787 | }
788 |
789 | export type DailyEventObjectBase = {
790 | action: DailyEvent;
791 | callClientId: string;
792 | };
793 |
794 | export interface DailyEventObjectNoPayload extends DailyEventObjectBase {
795 | action: Extract<
796 | DailyEvent,
797 | | 'loading'
798 | | 'loaded'
799 | | 'joining-meeting'
800 | | 'left-meeting'
801 | | 'call-instance-destroyed'
802 | | 'recording-stats'
803 | >;
804 | }
805 |
806 | export type DailyCameraError = {
807 | msg: string;
808 | };
809 |
810 | export interface DailyCamPermissionsError extends DailyCameraError {
811 | type: Extract;
812 | blockedBy: 'user' | 'browser';
813 | blockedMedia: Array<'video' | 'audio'>;
814 | }
815 |
816 | export interface DailyCamDeviceNotFoundError extends DailyCameraError {
817 | type: Extract;
818 | missingMedia: Array<'video' | 'audio'>;
819 | }
820 |
821 | export interface DailyCamConstraintsError extends DailyCameraError {
822 | type: Extract;
823 | reason: 'invalid' | 'none-specified';
824 | failedMedia: Array<'video' | 'audio'>;
825 | }
826 |
827 | export interface DailyCamInUseError extends DailyCameraError {
828 | type: Extract<
829 | DailyCameraErrorType,
830 | 'cam-in-use' | 'mic-in-use' | 'cam-mic-in-use'
831 | >;
832 | }
833 |
834 | export interface DailyCamTypeError extends DailyCameraError {
835 | type: Extract;
836 | }
837 |
838 | export interface DailyCamUnknownError extends DailyCameraError {
839 | type: Extract;
840 | }
841 |
842 | export type DailyCameraErrorObject =
843 | T extends DailyCamPermissionsError['type']
844 | ? DailyCamPermissionsError
845 | : T extends DailyCamDeviceNotFoundError['type']
846 | ? DailyCamDeviceNotFoundError
847 | : T extends DailyCamConstraintsError['type']
848 | ? DailyCamConstraintsError
849 | : T extends DailyCamInUseError['type']
850 | ? DailyCamInUseError
851 | : T extends DailyCamTypeError['type']
852 | ? DailyCamTypeError
853 | : T extends DailyCamUnknownError['type']
854 | ? DailyCamUnknownError
855 | : any;
856 |
857 | export interface DailyEventObjectCameraError extends DailyEventObjectBase {
858 | action: Extract;
859 | errorMsg: {
860 | errorMsg: string;
861 | audioOk?: boolean;
862 | videoOk?: boolean;
863 | };
864 | error: DailyCameraErrorObject;
865 | }
866 |
867 | export type DailyFatalError = {
868 | type: DailyFatalErrorType;
869 | msg: string;
870 | };
871 |
872 | export interface DailyFatalConnectionError extends DailyFatalError {
873 | type: Extract;
874 | details: {
875 | on:
876 | | 'load'
877 | | 'join'
878 | | 'reconnect'
879 | | 'move'
880 | | 'rtc-connection'
881 | | 'room-lookup';
882 | sourceError: Record;
883 | uri?: string;
884 | workerGroup?: string;
885 | geoGroup?: string;
886 | bundleUrl?: string;
887 | };
888 | }
889 |
890 | export type DailyFatalErrorObject =
891 | T extends DailyFatalConnectionError['type'] ? DailyFatalConnectionError : any;
892 |
893 | export interface DailyEventObjectFatalError extends DailyEventObjectBase {
894 | action: Extract;
895 | errorMsg: string;
896 | error?: DailyFatalErrorObject;
897 | }
898 |
899 | export interface DailyEventObjectNonFatalError extends DailyEventObjectBase {
900 | action: Extract;
901 | type: DailyNonFatalErrorType;
902 | errorMsg: string;
903 | details?: any;
904 | }
905 |
906 | export interface DailyEventObjectGenericError extends DailyEventObjectBase {
907 | action: Extract;
908 | errorMsg: string;
909 | }
910 |
911 | export interface DailyEventObjectLiveStreamingError
912 | extends DailyEventObjectBase {
913 | action: Extract;
914 | errorMsg: string;
915 | instanceId?: string;
916 | actionTraceId?: string;
917 | }
918 |
919 | export interface DailyEventObjectParticipants extends DailyEventObjectBase {
920 | action: Extract;
921 | participants: DailyParticipantsObject;
922 | }
923 |
924 | export interface DailyEventObjectParticipant extends DailyEventObjectBase {
925 | action: Extract;
926 | participant: DailyParticipant;
927 | }
928 |
929 | // only 1 reason reported for now. more to come.
930 | export type DailyParticipantLeftReason = 'hidden';
931 |
932 | export interface DailyEventObjectParticipantLeft extends DailyEventObjectBase {
933 | action: Extract;
934 | participant: DailyParticipant;
935 | // reason undefined if participant left for any reason other than those listed
936 | // in DailyParticipantLeftReason
937 | reason?: DailyParticipantLeftReason;
938 | }
939 |
940 | export interface DailyEventObjectParticipantCounts
941 | extends DailyEventObjectBase {
942 | action: Extract;
943 | participantCounts: DailyParticipantCounts;
944 | }
945 |
946 | export interface DailyEventObjectWaitingParticipant
947 | extends DailyEventObjectBase {
948 | action: Extract<
949 | DailyEvent,
950 | | 'waiting-participant-added'
951 | | 'waiting-participant-updated'
952 | | 'waiting-participant-removed'
953 | >;
954 | participant: DailyWaitingParticipant;
955 | }
956 |
957 | export interface DailyEventObjectAccessState
958 | extends DailyAccessState,
959 | DailyEventObjectBase {
960 | action: Extract;
961 | }
962 |
963 | export interface DailyEventObjectMeetingSessionSummaryUpdated
964 | extends DailyEventObjectBase {
965 | action: Extract;
966 | meetingSession: DailyMeetingSessionSummary;
967 | }
968 |
969 | export interface DailyEventObjectMeetingSessionStateUpdated
970 | extends DailyEventObjectBase {
971 | action: Extract;
972 | meetingSessionState: DailyMeetingSessionState;
973 | }
974 |
975 | export interface DailyEventObjectTrack extends DailyEventObjectBase {
976 | action: Extract;
977 | participant: DailyParticipant | null; // null if participant left meeting
978 | track: MediaStreamTrack;
979 | type:
980 | | 'video'
981 | | 'audio'
982 | | 'screenVideo'
983 | | 'screenAudio'
984 | | 'rmpVideo'
985 | | 'rmpAudio'
986 | | string; // string - for custom tracks
987 | }
988 |
989 | export interface DailyEventObjectRecordingStarted extends DailyEventObjectBase {
990 | action: Extract;
991 | local?: boolean;
992 | recordingId?: string;
993 | startedBy?: string;
994 | type?: string;
995 | layout?: DailyStreamingLayoutConfig<'start'>;
996 | instanceId?: string;
997 | }
998 |
999 | export interface DailyEventObjectRecordingStopped extends DailyEventObjectBase {
1000 | action: Extract;
1001 | instanceId?: string;
1002 | }
1003 |
1004 | export interface DailyEventObjectRecordingError extends DailyEventObjectBase {
1005 | action: Extract;
1006 | errorMsg: string;
1007 | instanceId?: string;
1008 | actionTraceId?: string;
1009 | }
1010 |
1011 | export interface DailyEventObjectNetworkQualityEvent
1012 | extends DailyEventObjectBase,
1013 | DailyNetworkStats {
1014 | action: Extract;
1015 | }
1016 |
1017 | export interface DailyEventObjectCpuLoadEvent extends DailyEventObjectBase {
1018 | action: Extract;
1019 | cpuLoadState: 'low' | 'high';
1020 | cpuLoadStateReason: 'encode' | 'decode' | 'scheduleDuration' | 'none'; // We are currently not using the Inter frame Delay to change the cpu load state
1021 | }
1022 |
1023 | export type DailyNetworkConnectionType = 'signaling' | 'peer-to-peer' | 'sfu';
1024 |
1025 | export interface DailyEventObjectNetworkConnectionEvent
1026 | extends DailyEventObjectBase {
1027 | action: Extract;
1028 | type: DailyNetworkConnectionType;
1029 | event: string;
1030 | session_id?: string;
1031 | sfu_id?: string;
1032 | }
1033 |
1034 | export interface DailyEventObjectTestCompleted extends DailyEventObjectBase {
1035 | action: Extract;
1036 | test: 'p2p-call-quality' | 'network-connectivity' | 'websocket-connectivity';
1037 | results:
1038 | | DailyP2PCallQualityTestResults
1039 | | DailyNetworkConnectivityTestStats
1040 | | DailyWebsocketConnectivityTestResults;
1041 | }
1042 |
1043 | export interface DailyEventObjectActiveSpeakerChange
1044 | extends DailyEventObjectBase {
1045 | action: Extract;
1046 | activeSpeaker: {
1047 | peerId: string;
1048 | };
1049 | }
1050 |
1051 | export interface DailyEventObjectAppMessage extends DailyEventObjectBase {
1052 | action: Extract;
1053 | data: any;
1054 | fromId: string;
1055 | }
1056 |
1057 | export interface DailyEventObjectTranscriptionMessage
1058 | extends DailyEventObjectBase {
1059 | action: Extract;
1060 | instanceId?: string;
1061 | trackType?: 'cam-audio' | 'screen-audio' | 'rmpAudio' | string;
1062 | participantId: string;
1063 | text: string;
1064 | timestamp: Date;
1065 | rawResponse: Record;
1066 | }
1067 |
1068 | export interface DailyEventObjectReceiveSettingsUpdated
1069 | extends DailyEventObjectBase {
1070 | action: Extract;
1071 | receiveSettings: DailyReceiveSettings;
1072 | }
1073 |
1074 | export interface DailyEventObjectAvailableDevicesUpdated
1075 | extends DailyEventObjectBase {
1076 | action: Extract;
1077 | availableDevices: MediaDeviceInfo[];
1078 | }
1079 |
1080 | export interface DailyEventObjectSendSettingsUpdated
1081 | extends DailyEventObjectBase {
1082 | action: Extract;
1083 | sendSettings: DailySendSettings;
1084 | }
1085 |
1086 | export interface DailyEventObjectLocalAudioLevel extends DailyEventObjectBase {
1087 | action: Extract;
1088 | audioLevel: number;
1089 | }
1090 |
1091 | export interface DailyEventObjectRemoteParticipantsAudioLevel
1092 | extends DailyEventObjectBase {
1093 | action: Extract;
1094 | participantsAudioLevel: DailyParticipantsAudioLevel;
1095 | }
1096 |
1097 | export interface DailyEventObjectLiveStreamingStarted
1098 | extends DailyEventObjectBase {
1099 | action: Extract;
1100 | layout?: DailyLiveStreamingLayoutConfig<'start'>;
1101 | instanceId?: string;
1102 | }
1103 | export interface DailyEventObjectLiveStreamingUpdated
1104 | extends DailyEventObjectBase {
1105 | action: Extract;
1106 | endpoint?: DailyStreamingEndpoint;
1107 | state: DailyStreamingState;
1108 | instanceId?: string;
1109 | }
1110 |
1111 | export interface DailyEventObjectLiveStreamingStopped
1112 | extends DailyEventObjectBase {
1113 | action: Extract;
1114 | instanceId?: string;
1115 | }
1116 |
1117 | export interface DailyEventObjectTranscriptionStarted
1118 | extends DailyEventObjectBase {
1119 | action: Extract;
1120 | instanceId: string;
1121 | transcriptId?: string;
1122 | language: string;
1123 | model: string;
1124 | tier?: string;
1125 | profanity_filter?: boolean;
1126 | redact?: Array | Array | boolean;
1127 | endpointing?: number | boolean;
1128 | punctuate?: boolean;
1129 | extra?: Record;
1130 | includeRawResponse?: boolean;
1131 | startedBy: string;
1132 | }
1133 |
1134 | export interface DailyEventObjectTranscriptionStopped
1135 | extends DailyEventObjectBase {
1136 | action: Extract;
1137 | instanceId: string;
1138 | updatedBy: string;
1139 | }
1140 |
1141 | export interface DailyEventObjectTranscriptionError
1142 | extends DailyEventObjectBase {
1143 | action: Extract;
1144 | instanceId: string;
1145 | errorMsg?: string;
1146 | }
1147 | export interface DailyEventObjectRemoteMediaPlayerUpdate
1148 | extends DailyEventObjectBase {
1149 | action: Extract<
1150 | DailyEvent,
1151 | 'remote-media-player-started' | 'remote-media-player-updated'
1152 | >;
1153 | updatedBy: string;
1154 | session_id: string;
1155 | remoteMediaPlayerState: DailyRemoteMediaPlayerState;
1156 | }
1157 |
1158 | export interface DailyEventObjectRemoteMediaPlayerStopped
1159 | extends DailyEventObjectBase {
1160 | action: Extract;
1161 | session_id: string;
1162 | updatedBy: string;
1163 | reason: DailyRemoteMediaPlayerStopReason;
1164 | }
1165 |
1166 | export interface DailyEventObjectDialinReady extends DailyEventObjectBase {
1167 | action: Extract;
1168 | sipEndpoint: string;
1169 | }
1170 |
1171 | export interface DailyEventObjectDialinConnected extends DailyEventObjectBase {
1172 | action: Extract;
1173 | sipHeaders?: Record;
1174 | sipFrom?: string;
1175 | actionTraceId?: string;
1176 | }
1177 |
1178 | export interface DailyEventObjectDialinError extends DailyEventObjectBase {
1179 | action: Extract;
1180 | errorMsg: string;
1181 | sessionId: string;
1182 | type?: 'start-failed' | null;
1183 | details?: { sipEndpoint?: string };
1184 | actionTraceId?: string;
1185 | }
1186 |
1187 | export interface DailyEventObjectDialinStopped extends DailyEventObjectBase {
1188 | action: Extract;
1189 | sipHeaders?: Record;
1190 | sipFrom?: string;
1191 | actionTraceId?: string;
1192 | }
1193 |
1194 | export interface DailyEventObjectDialinWarning extends DailyEventObjectBase {
1195 | action: Extract;
1196 | errorMsg: string;
1197 | actionTraceId?: string;
1198 | }
1199 |
1200 | export interface DailyEventObjectDialOutConnected extends DailyEventObjectBase {
1201 | action: Extract;
1202 | sessionId?: string;
1203 | userId?: string;
1204 | actionTraceId?: string;
1205 | }
1206 |
1207 | export interface DailyEventObjectDialOutError extends DailyEventObjectBase {
1208 | action: Extract;
1209 | errorMsg: string;
1210 | sessionId?: string;
1211 | userId?: string;
1212 | actionTraceId?: string;
1213 | }
1214 |
1215 | export interface DailyEventObjectDialOutStopped extends DailyEventObjectBase {
1216 | action: Extract;
1217 | sessionId?: string;
1218 | userId?: string;
1219 | actionTraceId?: string;
1220 | }
1221 |
1222 | export interface DailyEventObjectDialOutWarning extends DailyEventObjectBase {
1223 | action: Extract;
1224 | errorMsg: string;
1225 | sessionId?: string;
1226 | userId?: string;
1227 | actionTraceId?: string;
1228 | }
1229 |
1230 | export type DailyEventObject =
1231 | T extends DailyEventObjectAppMessage['action']
1232 | ? DailyEventObjectAppMessage
1233 | : T extends DailyEventObjectNoPayload['action']
1234 | ? DailyEventObjectNoPayload
1235 | : T extends DailyEventObjectCameraError['action']
1236 | ? DailyEventObjectCameraError
1237 | : T extends DailyEventObjectFatalError['action']
1238 | ? DailyEventObjectFatalError
1239 | : T extends DailyEventObjectNonFatalError['action']
1240 | ? DailyEventObjectNonFatalError
1241 | : T extends DailyEventObjectGenericError['action']
1242 | ? DailyEventObjectGenericError
1243 | : T extends DailyEventObjectParticipants['action']
1244 | ? DailyEventObjectParticipants
1245 | : T extends DailyEventObjectLiveStreamingStarted['action']
1246 | ? DailyEventObjectLiveStreamingStarted
1247 | : T extends DailyEventObjectLiveStreamingUpdated['action']
1248 | ? DailyEventObjectLiveStreamingUpdated
1249 | : T extends DailyEventObjectLiveStreamingStopped['action']
1250 | ? DailyEventObjectLiveStreamingStopped
1251 | : T extends DailyEventObjectLiveStreamingError['action']
1252 | ? DailyEventObjectLiveStreamingError
1253 | : T extends DailyEventObjectTranscriptionStarted['action']
1254 | ? DailyEventObjectTranscriptionStarted
1255 | : T extends DailyEventObjectTranscriptionMessage['action']
1256 | ? DailyEventObjectTranscriptionMessage
1257 | : T extends DailyEventObjectTranscriptionStopped['action']
1258 | ? DailyEventObjectTranscriptionStopped
1259 | : T extends DailyEventObjectTranscriptionError['action']
1260 | ? DailyEventObjectTranscriptionError
1261 | : T extends DailyEventObjectParticipant['action']
1262 | ? DailyEventObjectParticipant
1263 | : T extends DailyEventObjectParticipantLeft['action']
1264 | ? DailyEventObjectParticipantLeft
1265 | : T extends DailyEventObjectParticipantCounts['action']
1266 | ? DailyEventObjectParticipantCounts
1267 | : T extends DailyEventObjectWaitingParticipant['action']
1268 | ? DailyEventObjectWaitingParticipant
1269 | : T extends DailyEventObjectAccessState['action']
1270 | ? DailyEventObjectAccessState
1271 | : T extends DailyEventObjectMeetingSessionStateUpdated['action']
1272 | ? DailyEventObjectMeetingSessionStateUpdated
1273 | : T extends DailyEventObjectTrack['action']
1274 | ? DailyEventObjectTrack
1275 | : T extends DailyEventObjectRecordingStarted['action']
1276 | ? DailyEventObjectRecordingStarted
1277 | : T extends DailyEventObjectRecordingStopped['action']
1278 | ? DailyEventObjectRecordingStopped
1279 | : T extends DailyEventObjectRecordingError['action']
1280 | ? DailyEventObjectRecordingError
1281 | : T extends DailyEventObjectRemoteMediaPlayerUpdate['action']
1282 | ? DailyEventObjectRemoteMediaPlayerUpdate
1283 | : T extends DailyEventObjectRemoteMediaPlayerStopped['action']
1284 | ? DailyEventObjectRemoteMediaPlayerStopped
1285 | : T extends DailyEventObjectNetworkQualityEvent['action']
1286 | ? DailyEventObjectNetworkQualityEvent
1287 | : T extends DailyEventObjectCpuLoadEvent['action']
1288 | ? DailyEventObjectCpuLoadEvent
1289 | : T extends DailyEventObjectNetworkConnectionEvent['action']
1290 | ? DailyEventObjectNetworkConnectionEvent
1291 | : T extends DailyEventObjectTestCompleted['action']
1292 | ? DailyEventObjectTestCompleted
1293 | : T extends DailyEventObjectActiveSpeakerChange['action']
1294 | ? DailyEventObjectActiveSpeakerChange
1295 | : T extends DailyEventObjectReceiveSettingsUpdated['action']
1296 | ? DailyEventObjectReceiveSettingsUpdated
1297 | : T extends DailyEventObjectAvailableDevicesUpdated['action']
1298 | ? DailyEventObjectAvailableDevicesUpdated
1299 | : T extends DailyEventObjectSendSettingsUpdated['action']
1300 | ? DailyEventObjectSendSettingsUpdated
1301 | : T extends DailyEventObjectDialinReady['action']
1302 | ? DailyEventObjectDialinReady
1303 | : T extends DailyEventObjectDialinConnected['action']
1304 | ? DailyEventObjectDialinConnected
1305 | : T extends DailyEventObjectDialinError['action']
1306 | ? DailyEventObjectDialinError
1307 | : T extends DailyEventObjectDialinStopped['action']
1308 | ? DailyEventObjectDialinStopped
1309 | : T extends DailyEventObjectDialinWarning['action']
1310 | ? DailyEventObjectDialinWarning
1311 | : T extends DailyEventObjectDialOutConnected['action']
1312 | ? DailyEventObjectDialOutConnected
1313 | : T extends DailyEventObjectDialOutError['action']
1314 | ? DailyEventObjectDialOutError
1315 | : T extends DailyEventObjectDialOutStopped['action']
1316 | ? DailyEventObjectDialOutStopped
1317 | : T extends DailyEventObjectDialOutWarning['action']
1318 | ? DailyEventObjectDialOutWarning
1319 | : T extends DailyEventObjectLocalAudioLevel['action']
1320 | ? DailyEventObjectLocalAudioLevel
1321 | : T extends DailyEventObjectRemoteParticipantsAudioLevel['action']
1322 | ? DailyEventObjectRemoteParticipantsAudioLevel
1323 | : T extends DailyEvent
1324 | ? DailyEventObjectBase
1325 | : any;
1326 |
1327 | export type DailyNativeInCallAudioMode = 'video' | 'voice';
1328 |
1329 | export interface DailyCallFactory {
1330 | createCallObject(properties?: DailyFactoryOptions): DailyCall;
1331 | getCallInstance(): DailyCall | undefined;
1332 | }
1333 |
1334 | export interface DailyCallStaticUtils {
1335 | supportedBrowser(): DailyBrowserInfo;
1336 | }
1337 |
1338 | export interface DailyScreenShareUpdateOptions {
1339 | screenVideo: {
1340 | enabled: boolean;
1341 | };
1342 | screenAudio: {
1343 | enabled: boolean;
1344 | };
1345 | }
1346 |
1347 | export type DailyCameraFacingMode = 'user' | 'environment';
1348 |
1349 | type DailyStreamingParticipantsSortMethod = 'active';
1350 |
1351 | export interface DailyStreamingParticipantsConfig {
1352 | video?: string[];
1353 | audio?: string[];
1354 | sort?: DailyStreamingParticipantsSortMethod;
1355 | }
1356 |
1357 | export interface DailyStreamingDefaultLayoutConfig {
1358 | preset: 'default';
1359 | max_cam_streams?: number;
1360 | participants?: DailyStreamingParticipantsConfig;
1361 | }
1362 |
1363 | export interface DailyStreamingSingleParticipantLayoutConfig {
1364 | preset: 'single-participant';
1365 | session_id: string;
1366 | }
1367 |
1368 | export interface DailyStreamingActiveParticipantLayoutConfig {
1369 | preset: 'active-participant';
1370 | participants?: DailyStreamingParticipantsConfig;
1371 | }
1372 |
1373 | export interface DailyStreamingAudioOnlyLayoutConfig {
1374 | preset: 'audio-only';
1375 | participants?: DailyStreamingParticipantsConfig;
1376 | }
1377 |
1378 | export type DailyStreamingPortraitLayoutVariant = 'vertical' | 'inset';
1379 |
1380 | export interface DailyStreamingPortraitLayoutConfig {
1381 | preset: 'portrait';
1382 | variant?: DailyStreamingPortraitLayoutVariant;
1383 | max_cam_streams?: number;
1384 | participants?: DailyStreamingParticipantsConfig;
1385 | }
1386 |
1387 | export interface DailyUpdateStreamingCustomLayoutConfig {
1388 | preset: 'custom';
1389 | participants?: DailyStreamingParticipantsConfig;
1390 | composition_params?: {
1391 | [key: string]: boolean | number | string;
1392 | };
1393 | }
1394 |
1395 | export interface DailyStartStreamingCustomLayoutConfig
1396 | extends DailyUpdateStreamingCustomLayoutConfig {
1397 | composition_id?: string;
1398 | session_assets?: {
1399 | [key: string]: string;
1400 | };
1401 | }
1402 |
1403 | type DailyStreamingLayoutConfigType = 'start' | 'update';
1404 | type DailyStartStreamingMethod = 'liveStreaming' | 'recording';
1405 |
1406 | export type DailyStreamingLayoutConfig<
1407 | Type extends DailyStreamingLayoutConfigType = 'start'
1408 | > =
1409 | | DailyStreamingDefaultLayoutConfig
1410 | | DailyStreamingSingleParticipantLayoutConfig
1411 | | DailyStreamingActiveParticipantLayoutConfig
1412 | | DailyStreamingPortraitLayoutConfig
1413 | | DailyStreamingAudioOnlyLayoutConfig
1414 | | (Type extends 'start'
1415 | ? DailyStartStreamingCustomLayoutConfig
1416 | : DailyUpdateStreamingCustomLayoutConfig);
1417 |
1418 | export type DailyLiveStreamingLayoutConfig<
1419 | Type extends DailyStreamingLayoutConfigType = 'start'
1420 | > = Exclude<
1421 | DailyStreamingLayoutConfig,
1422 | DailyStreamingAudioOnlyLayoutConfig
1423 | >;
1424 |
1425 | export type DailyStreamingState = 'connected' | 'interrupted';
1426 |
1427 | export type DailyRemoteMediaPlayerSettingPlay = 'play';
1428 | export type DailyRemoteMediaPlayerSettingPause = 'pause';
1429 |
1430 | export type DailyRemoteMediaPlayerStatePlaying = 'playing';
1431 | export type DailyRemoteMediaPlayerStatePaused = 'paused';
1432 | export type DailyRemoteMediaPlayerStateBuffering = 'buffering';
1433 |
1434 | export type DailyRemoteMediaPlayerEOS = 'EOS';
1435 | export type DailyRemoteMediaPlayerPeerStopped = 'stopped-by-peer';
1436 |
1437 | export type DailyRemoteMediaPlayerStopReason =
1438 | | DailyRemoteMediaPlayerEOS
1439 | | DailyRemoteMediaPlayerPeerStopped;
1440 |
1441 | export type DailyAccess = 'unknown' | SpecifiedDailyAccess;
1442 |
1443 | export type SpecifiedDailyAccess = { level: 'none' | 'lobby' | 'full' };
1444 |
1445 | export type DailyAccessState = {
1446 | access: DailyAccess;
1447 | awaitingAccess?: SpecifiedDailyAccess;
1448 | };
1449 |
1450 | export type DailyAccessRequest = {
1451 | access?: { level: 'full' };
1452 | name: string;
1453 | };
1454 |
1455 | export interface DailyStreamingOptions<
1456 | Method extends DailyStartStreamingMethod,
1457 | Type extends DailyStreamingLayoutConfigType = 'start'
1458 | > {
1459 | width?: number;
1460 | height?: number;
1461 | fps?: number;
1462 | videoBitrate?: number;
1463 | audioBitrate?: number;
1464 | minIdleTimeOut?: number;
1465 | maxDuration?: number;
1466 | backgroundColor?: string;
1467 | instanceId?: string;
1468 | layout?: Method extends 'recording'
1469 | ? DailyStreamingLayoutConfig
1470 | : DailyLiveStreamingLayoutConfig;
1471 | }
1472 |
1473 | export interface DailyStreamingEndpoint {
1474 | endpoint: string;
1475 | }
1476 |
1477 | export interface DailyLiveStreamingOptions<
1478 | Type extends DailyStreamingLayoutConfigType = 'start'
1479 | > extends DailyStreamingOptions<'liveStreaming', Type> {
1480 | rtmpUrl?: string | string[];
1481 | endpoints?: DailyStreamingEndpoint[];
1482 | }
1483 |
1484 | export interface RemoteMediaPlayerSimulcastEncoding {
1485 | maxBitrate: number;
1486 | maxFramerate?: number;
1487 | scaleResolutionDownBy?: number;
1488 | }
1489 |
1490 | export interface DailyRemoteMediaPlayerSettings {
1491 | state: DailyRemoteMediaPlayerSettingPlay | DailyRemoteMediaPlayerSettingPause;
1492 | simulcastEncodings?: RemoteMediaPlayerSimulcastEncoding[];
1493 | }
1494 |
1495 | export interface DailyRemoteMediaPlayerStartOptions {
1496 | url: string;
1497 | settings?: DailyRemoteMediaPlayerSettings;
1498 | }
1499 |
1500 | export interface DailyRemoteMediaPlayerUpdateOptions {
1501 | session_id: string;
1502 | settings: DailyRemoteMediaPlayerSettings;
1503 | }
1504 |
1505 | export interface DailyRemoteMediaPlayerState {
1506 | state:
1507 | | DailyRemoteMediaPlayerStatePlaying
1508 | | DailyRemoteMediaPlayerStatePaused
1509 | | DailyRemoteMediaPlayerStateBuffering;
1510 | settings: DailyRemoteMediaPlayerSettings;
1511 | }
1512 |
1513 | export interface DailyRemoteMediaPlayerInfo {
1514 | session_id: string;
1515 | remoteMediaPlayerState: DailyRemoteMediaPlayerState;
1516 | }
1517 |
1518 | export interface DailyTranscriptionDeepgramOptions {
1519 | language?: string;
1520 | model?: string;
1521 | tier?: string;
1522 | profanity_filter?: boolean;
1523 | redact?: Array | Array | boolean;
1524 | endpointing?: number | boolean;
1525 | punctuate?: boolean;
1526 | extra?: Record;
1527 | includeRawResponse?: boolean;
1528 | instanceId?: string;
1529 | participants?: Array;
1530 | }
1531 |
1532 | export interface DailyTranscriptionUpdateOptions {
1533 | instanceId?: string;
1534 | participants: Array;
1535 | }
1536 |
1537 | export interface DailyTranscriptionStopOptions {
1538 | instanceId?: string;
1539 | }
1540 |
1541 | export type DailyDialOutAudioCodecs = 'PCMU' | 'OPUS' | 'G722' | 'PCMA';
1542 |
1543 | export type DailyDialOutVideoCodecs = 'H264' | 'VP8';
1544 |
1545 | export interface DailyDialOutCodecs {
1546 | audio?: Array;
1547 | video?: Array;
1548 | }
1549 |
1550 | export interface DailyDialOutSession {
1551 | sessionId: string;
1552 | }
1553 |
1554 | export interface DailySipPstnParticipantPermissions {
1555 | canReceive: Partial;
1556 | }
1557 |
1558 | export interface DailyStartDialoutSipOptions {
1559 | sipUri?: string;
1560 | displayName?: string;
1561 | userId?: string;
1562 | video?: boolean;
1563 | codecs?: DailyDialOutCodecs;
1564 | permissions?: DailySipPstnParticipantPermissions;
1565 | }
1566 |
1567 | export interface DailyStartDialoutPhoneOptions {
1568 | phoneNumber?: string;
1569 | displayName?: string;
1570 | userId?: string;
1571 | codecs?: DailyDialOutCodecs;
1572 | callerId?: string;
1573 | permissions?: DailySipPstnParticipantPermissions;
1574 | extension?: string;
1575 | waitBeforeExtensionDialSec?: number;
1576 | }
1577 |
1578 | export type DailyStartDialoutOptions =
1579 | | DailyStartDialoutSipOptions
1580 | | DailyStartDialoutPhoneOptions;
1581 |
1582 | export interface DailySipCallTransferOptions {
1583 | sessionId: string;
1584 | toEndPoint: string;
1585 | callerId?: string;
1586 | extension?: string;
1587 | waitBeforeExtensionDialSec?: number;
1588 | }
1589 |
1590 | export interface DailySipReferOptions {
1591 | sessionId: string;
1592 | toEndPoint: string;
1593 | }
1594 |
1595 | export interface DailyCall {
1596 | callClientId: string;
1597 | join(properties?: DailyCallOptions): Promise;
1598 | leave(): Promise;
1599 | destroy(): Promise;
1600 | isDestroyed(): boolean;
1601 | meetingState(): DailyMeetingState;
1602 | accessState(): DailyAccessState;
1603 | participants(): DailyParticipantsObject;
1604 | participantCounts(): DailyParticipantCounts;
1605 | updateParticipant(
1606 | sessionId: string,
1607 | updates: DailyParticipantUpdateOptions
1608 | ): DailyCall;
1609 | updateParticipants(updates: {
1610 | [sessionId: string]: DailyParticipantUpdateOptions;
1611 | }): DailyCall;
1612 | waitingParticipants(): { [id: string]: DailyWaitingParticipant };
1613 | updateWaitingParticipant(
1614 | id: string,
1615 | updates: DailyWaitingParticipantUpdateOptions
1616 | ): Promise<{ id: string }>;
1617 | updateWaitingParticipants(updates: {
1618 | [id: string]: DailyWaitingParticipantUpdateOptions;
1619 | }): Promise<{ ids: string[] }>;
1620 | requestAccess(
1621 | access: DailyAccessRequest
1622 | ): Promise<{ access: DailyAccess; granted: boolean }>;
1623 | localAudio(): boolean;
1624 | localVideo(): boolean;
1625 | setLocalAudio(enabled: boolean): DailyCall;
1626 | setLocalVideo(enabled: boolean): DailyCall;
1627 | localScreenAudio(): boolean;
1628 | localScreenVideo(): boolean;
1629 | updateScreenShare(options?: DailyScreenShareUpdateOptions): void;
1630 | getReceiveSettings(
1631 | id: string,
1632 | options?: { showInheritedValues: boolean }
1633 | ): Promise;
1634 | getReceiveSettings(): Promise;
1635 | updateReceiveSettings(
1636 | receiveSettings: DailyReceiveSettingsUpdates
1637 | ): Promise;
1638 | updateInputSettings(
1639 | inputSettings: DailyInputSettings
1640 | ): Promise<{ inputSettings: DailyInputSettings }>;
1641 | startCamera(properties?: DailyCallOptions): Promise;
1642 | startLocalAudioLevelObserver(interval?: number): Promise;
1643 | isLocalAudioLevelObserverRunning(): boolean;
1644 | stopLocalAudioLevelObserver(): void;
1645 | getLocalAudioLevel(): number;
1646 | startRemoteParticipantsAudioLevelObserver(interval?: number): Promise;
1647 | isRemoteParticipantsAudioLevelObserverRunning(): boolean;
1648 | stopRemoteParticipantsAudioLevelObserver(): void;
1649 | getRemoteParticipantsAudioLevel(): DailyParticipantsAudioLevel;
1650 | cycleCamera(): Promise<{
1651 | device: { facingMode: DailyCameraFacingMode } | null;
1652 | }>;
1653 | setCamera(cameraDeviceId: string | number): Promise<{
1654 | device: { facingMode: DailyCameraFacingMode } | null;
1655 | }>;
1656 | setAudioDevice(deviceId: string): Promise<{
1657 | deviceId: string;
1658 | }>;
1659 | getCameraFacingMode(): Promise;
1660 | nativeInCallAudioMode(): DailyNativeInCallAudioMode;
1661 | setNativeInCallAudioMode(
1662 | inCallAudioMode: DailyNativeInCallAudioMode
1663 | ): DailyCall;
1664 | getInputDevices(): Promise;
1665 | startRecording(options?: DailyStreamingOptions<'recording', 'start'>): void;
1666 | updateRecording(options: {
1667 | layout?: DailyStreamingLayoutConfig<'update'>;
1668 | instanceId?: string;
1669 | }): void;
1670 | stopRecording(options?: { instanceId: string }): void;
1671 | startLiveStreaming(options: DailyLiveStreamingOptions<'start'>): void;
1672 | updateLiveStreaming(options: {
1673 | layout?: DailyLiveStreamingLayoutConfig<'update'>;
1674 | instanceId?: string;
1675 | }): void;
1676 | addLiveStreamingEndpoints(options: {
1677 | endpoints: DailyStreamingEndpoint[];
1678 | instanceId?: string;
1679 | }): void;
1680 | removeLiveStreamingEndpoints(options: {
1681 | endpoints: DailyStreamingEndpoint[];
1682 | instanceId?: string;
1683 | }): void;
1684 | stopLiveStreaming(options?: { instanceId: string }): void;
1685 | startRemoteMediaPlayer(
1686 | options: DailyRemoteMediaPlayerStartOptions
1687 | ): Promise;
1688 | stopRemoteMediaPlayer(session_id: string): Promise;
1689 | updateRemoteMediaPlayer(
1690 | options: DailyRemoteMediaPlayerUpdateOptions
1691 | ): Promise;
1692 | startTranscription(options?: DailyTranscriptionDeepgramOptions): void;
1693 | updateTranscription(options: DailyTranscriptionUpdateOptions): void;
1694 | stopTranscription(options?: DailyTranscriptionStopOptions): void;
1695 | preAuth(properties?: DailyCallOptions): Promise<{ access: DailyAccess }>;
1696 | load(properties?: DailyLoadOptions): Promise;
1697 | startScreenShare(properties?: DailyStartScreenShare): void;
1698 | stopScreenShare(): void;
1699 | testPeerToPeerCallQuality(options: {
1700 | videoTrack: MediaStreamTrack;
1701 | duration?: number;
1702 | }): Promise;
1703 | stopTestPeerToPeerCallQuality(): void;
1704 | testWebsocketConnectivity(): Promise;
1705 | abortTestWebsocketConnectivity(): void;
1706 | testNetworkConnectivity(
1707 | videoTrack: MediaStreamTrack
1708 | ): Promise;
1709 | abortTestNetworkConnectivity(): void;
1710 | getNetworkStats(): Promise;
1711 | getCpuLoadStats(): Promise;
1712 | updateSendSettings(settings: DailySendSettings): Promise;
1713 | getSendSettings(): DailySendSettings;
1714 | subscribeToTracksAutomatically(): boolean;
1715 | setSubscribeToTracksAutomatically(enabled: boolean): DailyCall;
1716 | enumerateDevices(): Promise<{ devices: MediaDeviceInfo[] }>;
1717 | sendAppMessage(data: any, to?: string | string[]): DailyCall;
1718 | setProxyUrl(proxyUrl?: string): DailyCall;
1719 | setIceConfig(iceConfig?: DailyIceConfig): DailyCall;
1720 | meetingSessionSummary(): DailyMeetingSessionSummary;
1721 | meetingSessionState(): DailyMeetingSessionState;
1722 | setMeetingSessionData(
1723 | data: unknown,
1724 | mergeStrategy?: DailySessionDataMergeStrategy
1725 | ): void;
1726 | setUserName(name: string): Promise<{ userName: string }>;
1727 | setUserData(data: unknown): Promise<{ userData: unknown }>;
1728 | room(options?: {
1729 | includeRoomConfigDefaults: boolean;
1730 | }): Promise;
1731 | geo(): Promise<{ current: string }>;
1732 | on(
1733 | event: T,
1734 | handler: (event: DailyEventObject) => void
1735 | ): DailyCall;
1736 | once(
1737 | event: T,
1738 | handler: (event: DailyEventObject) => void
1739 | ): DailyCall;
1740 | off(
1741 | event: T,
1742 | handler: (event: DailyEventObject) => void
1743 | ): DailyCall;
1744 | properties: {
1745 | dailyConfig?: DailyAdvancedConfig;
1746 | };
1747 | startDialOut(
1748 | options: DailyStartDialoutOptions
1749 | ): Promise<{ session?: DailyDialOutSession }>;
1750 | stopDialOut(options: { sessionId: string }): Promise;
1751 | sendDTMF(options: { sessionId: string; tones: string }): Promise;
1752 | sipCallTransfer(options: DailySipCallTransferOptions): Promise;
1753 | sipRefer(options: DailySipReferOptions): Promise;
1754 | }
1755 |
1756 | declare const Daily: DailyCallFactory & DailyCallStaticUtils;
1757 |
1758 | export default Daily;
1759 |
--------------------------------------------------------------------------------