Skip to content

Commit

Permalink
Merge branch 'main' into fix/bg-filter-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
myandrienko committed Jun 28, 2024
2 parents 7fc1b9f + 55e3382 commit a84759e
Show file tree
Hide file tree
Showing 86 changed files with 3,018 additions and 4,904 deletions.
34 changes: 32 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,42 @@ coverage

/**/docusaurus/shared

lefthook.yml

# Test files
video-buddy-session.json
video-buddy-log.txt
**/fastlane/*.log
**/fastlane/*.mp4
**/fastlane/*.json


# Xcode
#
**/ios/build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace

# Android/IJ
#
**/android/build/
.classpath
.cxx
.gradle
.idea
.project
.settings
local.properties
android.iml
21 changes: 10 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,23 @@
"@commitlint/config-angular": "^17.0.0",
"@jscutlery/semver": "^2.30.1",
"@nrwl/devkit": "^16.0.1",
"@types/eslint": "^8.44.4",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"@typescript-eslint/typescript-estree": "^6.8.0",
"eslint": "^8.51.0",
"@types/eslint": "^8.56.10",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@typescript-eslint/typescript-estree": "^7.13.1",
"eslint": "^8.57.0",
"eslint-config-react-app": "^7.0.1",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"ngx-deploy-npm": "^5.2.0",
"nx": "16.0.1",
"prettier": "^3.3.0",
"typescript": "^5.4.3",
"vercel": "^34.1.2",
"prettier": "^3.3.2",
"typescript": "^5.5.2",
"vercel": "^34.2.7",
"vite": "^4.4.11"
},
"resolutions": {
"eslint-plugin-prettier": "^5.1.3",
"prettier": "^3.3.0"
"eslint-plugin-prettier": "^5.1.3"
}
}
4 changes: 2 additions & 2 deletions packages/audio-filters-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"devDependencies": {
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-typescript": "^11.1.6",
"rimraf": "^5.0.5",
"rimraf": "^5.0.7",
"rollup": "^3.29.4",
"typescript": "^5.4.3"
"typescript": "^5.5.2"
}
}
14 changes: 14 additions & 0 deletions packages/client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).

### [1.4.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.4.2...@stream-io/video-client-1.4.3) (2024-06-25)


### Bug Fixes

* improve browser permission handling ([#1394](https://github.com/GetStream/stream-video-js/issues/1394)) ([c8ccb21](https://github.com/GetStream/stream-video-js/commit/c8ccb219a43464d1215987d99fd01d8b4a407eb5))

### [1.4.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.4.1...@stream-io/video-client-1.4.2) (2024-06-24)


### Bug Fixes

* support for portrait mode recording ([#1418](https://github.com/GetStream/stream-video-js/issues/1418)) ([70a304d](https://github.com/GetStream/stream-video-js/commit/70a304d3f20d93ecfffc97794e8e4974acf88e9a))

### [1.4.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.4.0...@stream-io/video-client-1.4.1) (2024-06-19)


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
id: session-timers
title: Session Timers
---

A session timer allows you to limit the maximum duration of a call. The duration
[can be configured](https://getstream.io/video/docs/api/calls/#session-timers)
for all calls of a certain type, or on a per-call basis. When a session timer
reaches zero, the call automatically ends.

## Creating a call with a session timer

Let's see how to create a single call with a limited duration:

```ts
const callType = 'default';
const callId = 'test-call';

const call = client.call(callType, callId);
await call.getOrCreate({
data: {
settings_override: {
limits: {
max_duration_seconds: 3600,
},
},
},
});
```

This code creates a call with a duration of 3600 seconds (1 hour) from the time
the session is starts (a participant joins the call).

After joining the call with the specified `max_duration_seconds`, you can
examine a session's `timer_ends_at` field, which provides the timestamp when the
call will end. When a call ends, all participants are removed from the call.

```ts
await call.join();
console.log(call.state.session?.timer_ends_at);
```

## Extending a call

​You can also extend the duration of a call, both before or during the call. To
do that, you should use the `call.update` method:

```ts
await call.get();
// extend by 1 minute
const duration = call.state.settings?.limits.max_duration_seconds + 60;

await call.update({
settings_override: {
limits: {
max_duration_seconds: duration,
},
},
});
```

If the call duration is extended, the `timer_ends_at` is updated to reflect this
change. Call participants will receive the `call.updated` event to notify them
about this change.
8 changes: 4 additions & 4 deletions packages/client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stream-io/video-client",
"version": "1.4.1",
"version": "1.4.3",
"packageManager": "[email protected]",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
Expand Down Expand Up @@ -52,10 +52,10 @@
"@vitest/coverage-v8": "^0.34.4",
"dotenv": "^16.3.1",
"happy-dom": "^11.0.2",
"prettier": "^3.3.0",
"rimraf": "^5.0.5",
"prettier": "^3.3.2",
"rimraf": "^5.0.7",
"rollup": "^3.29.4",
"typescript": "^5.4.3",
"typescript": "^5.5.2",
"vite": "^4.4.11",
"vitest": "^0.34.4",
"vitest-mock-extended": "^1.2.1"
Expand Down
152 changes: 152 additions & 0 deletions packages/client/src/devices/BrowserPermission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { fromEventPattern, map } from 'rxjs';
import { isReactNative } from '../helpers/platforms';
import { getLogger } from '../logger';
import { disposeOfMediaStream } from './devices';
import { withoutConcurrency } from '../helpers/concurrency';

interface BrowserPermissionConfig {
constraints: DisplayMediaStreamOptions;
queryName: PermissionName;
}

export class BrowserPermission {
private ready: Promise<void>;
private disposeController = new AbortController();
private state: PermissionState | undefined;
private wasPrompted: boolean = false;
private listeners = new Set<(state: PermissionState) => void>();
private logger = getLogger(['permissions']);

constructor(private readonly permission: BrowserPermissionConfig) {
const signal = this.disposeController.signal;

this.ready = (async () => {
const assumeGranted = (error?: unknown) => {
this.logger('warn', "Can't query permissions, assuming granted", {
permission,
error,
});
this.setState('granted');
};

if (!canQueryPermissions()) {
return assumeGranted();
}

try {
const status = await navigator.permissions.query({
name: permission.queryName,
});

if (!signal.aborted) {
this.setState(status.state);
status.addEventListener('change', () => this.setState(status.state), {
signal,
});
}
} catch (err) {
assumeGranted(err);
}
})();
}

dispose() {
this.state = undefined;
this.disposeController.abort();
}

async getState() {
await this.ready;
if (!this.state) {
throw new Error('BrowserPermission instance possibly disposed');
}
return this.state;
}

async prompt({
forcePrompt = false,
throwOnNotAllowed = false,
}: { forcePrompt?: boolean; throwOnNotAllowed?: boolean } = {}) {
await withoutConcurrency(
`permission-prompt-${this.permission.queryName}`,
async () => {
if (
(await this.getState()) !== 'prompt' ||
(this.wasPrompted && !forcePrompt)
) {
const isGranted = this.state === 'granted';

if (!isGranted && throwOnNotAllowed) {
throw new DOMException(
'Permission was not granted previously, and prompting again is not allowed',
'NotAllowedError',
);
}

return isGranted;
}

try {
this.wasPrompted = true;
const stream = await navigator.mediaDevices.getUserMedia(
this.permission.constraints,
);
disposeOfMediaStream(stream);
return true;
} catch (e) {
if (e instanceof DOMException && e.name === 'NotAllowedError') {
this.logger('info', 'Browser permission was not granted', {
permission: this.permission,
});

if (throwOnNotAllowed) {
throw e;
}

return false;
}

this.logger('error', `Failed to getUserMedia`, {
error: e,
permission: this.permission,
});
throw e;
}
},
);
}

listen(cb: (state: PermissionState) => void) {
this.listeners.add(cb);
if (this.state) cb(this.state);
return () => this.listeners.delete(cb);
}

asObservable() {
return fromEventPattern<PermissionState>(
(handler) => this.listen(handler),
(handler, unlisten) => unlisten(),
).pipe(
// In some browsers, the 'change' event doesn't reliably emit and hence,
// permissionState stays in 'prompt' state forever.
// Typically, this happens when a user grants one-time permission.
// Instead of checking if a permission is granted, we check if it isn't denied
map((state) => state !== 'denied'),
);
}

private setState(state: PermissionState) {
if (this.state !== state) {
this.state = state;
this.listeners.forEach((listener) => listener(state));
}
}
}

function canQueryPermissions() {
return (
!isReactNative() &&
typeof navigator !== 'undefined' &&
!!navigator.permissions?.query
);
}
8 changes: 2 additions & 6 deletions packages/client/src/devices/CameraManagerState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BehaviorSubject, distinctUntilChanged, Observable } from 'rxjs';
import { InputMediaDeviceManagerState } from './InputMediaDeviceManagerState';
import { isReactNative } from '../helpers/platforms';
import { getVideoBrowserPermission } from './devices';

export type CameraDirection = 'front' | 'back' | undefined;

Expand All @@ -15,12 +16,7 @@ export class CameraManagerState extends InputMediaDeviceManagerState {
direction$: Observable<CameraDirection>;

constructor() {
super(
'stop-tracks',
// `camera` is not in the W3C standard yet,
// but it's supported by Chrome and Safari.
'camera' as PermissionName,
);
super('stop-tracks', getVideoBrowserPermission());
this.direction$ = this.directionSubject
.asObservable()
.pipe(distinctUntilChanged());
Expand Down
Loading

0 comments on commit a84759e

Please sign in to comment.