diff --git a/.env.localhost b/.env.localhost index 6386fdd7d21..4b8da96deee 100644 --- a/.env.localhost +++ b/.env.localhost @@ -5,7 +5,7 @@ APP_BASE="https://local.zinfra.io:8081" FORCED_CONFIG_URL="https://github.com/wireapp/wire-web-config-wire" -CSP_EXTRA_CONNECT_SRC="http://localhost:32123, ws://localhost:32123, https://*.zinfra.io, https://*.wire.link, https://*.wire.com, https://api.raygun.io, wss://*.zinfra.io, wss://*.wire.link, wss://prod-nginz-ssl.wire.com, https://wire.count.ly, https://balderdash.hogwash.work:9000, https://balderdash.hogwash.work:5556, https://accounts.google.com, https://oauth2.googleapis.com/token" +CSP_EXTRA_CONNECT_SRC="http://localhost:4570, http://localhost:32123, ws://localhost:32123, https://*.zinfra.io, https://*.wire.link, https://*.wire.com, https://api.raygun.io, wss://*.zinfra.io, wss://*.wire.link, wss://prod-nginz-ssl.wire.com, https://wire.count.ly, https://balderdash.hogwash.work:9000, https://balderdash.hogwash.work:5556, https://accounts.google.com, https://oauth2.googleapis.com/token" CSP_EXTRA_IMG_SRC="https://*.zinfra.io, https://*.wire.com, https://*.wire.link" CSP_EXTRA_SCRIPT_SRC="http://localhost:32123, https://*.zinfra.io, https://*.wire.com, https://*.wire.link, https://api.raygun.io" ENFORCE_HTTPS="false" @@ -30,3 +30,7 @@ BACKEND_WS="wss://staging-nginz-ssl.zinfra.io" # BACKEND_REST="https://prod-nginz-https.wire.com" # BACKEND_WS="wss://prod-nginz-ssl.wire.com" +# Use these vars if you are running a local backend. (Rare) +# BACKEND_REST="http://localhost:8080" +# BACKEND_WS="ws://localhost:8080" +# APP_BASE="http://localhost:8081" diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index cbc75624b91..bb29832c1ed 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,17 +1,20 @@ ## Description + + ## Checklist +- [ ] mentions the JIRA issue in the PR name (Ex. [WPB-423]) - [ ] PR has been self reviewed by the author; - [ ] Hard-to-understand areas of the code have been commented; - [ ] If it is a core feature, unit tests have been added; -### Important details for the reviewers - -(Delete this section if unnecessary) + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1f209ef7c5c..45cff760f78 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -35,6 +35,10 @@ updates: ignore: - dependency-name: '@wireapp/avs' + # Wait until we have next release with wasm folder + - dependency-name: '@mediapipe/tasks-vision' + versions: + - '0.10.16' # Server dependencies - package-ecosystem: npm diff --git a/.github/workflows/cherry-pick-release-to-dev.yml b/.github/workflows/cherry-pick-release-to-dev.yml index d97a158a8c9..534406809a1 100644 --- a/.github/workflows/cherry-pick-release-to-dev.yml +++ b/.github/workflows/cherry-pick-release-to-dev.yml @@ -1,10 +1,12 @@ -# This job will automatically create a cherry pick PR from any release/* branch to the dev branch +# This job will automatically create a cherry-pick PR from any release/* or master branch to the dev branch. +# This job will not run if the push/PR is from the dev branch itself. # It allows the dev branch to stay up-to-date with fixes made to specific release branches name: Cherry pick to dev on: push: branches: - - release/* + - 'release/*' + - 'master' permissions: pull-requests: write @@ -12,6 +14,7 @@ permissions: jobs: cherry_pick: + if: github.event.pull_request.head.ref != 'dev' runs-on: ubuntu-latest steps: - name: checkout diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9460fe24348..e15c1451aeb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: run: yarn test --coverage --coverage-reporters=lcov --detectOpenHandles=false - name: Monitor coverage - uses: codecov/codecov-action@v4.5.0 + uses: codecov/codecov-action@v4.6.0 with: fail_ci_if_error: false files: ./coverage/lcov.info diff --git a/.github/workflows/deploy-to-test-env.yml b/.github/workflows/deploy-to-test-env.yml index 6327ccdac35..1b41689e54a 100644 --- a/.github/workflows/deploy-to-test-env.yml +++ b/.github/workflows/deploy-to-test-env.yml @@ -8,7 +8,6 @@ on: required: true type: choice options: - - wire-webapp-main - wire-webapp-qa - wire-webapp-mls @@ -28,8 +27,10 @@ jobs: steps: - name: Print environment variables run: | - echo -e "branch = ${{github.ref_name}}" - echo -e "env = ${{inputs.env}}" + echo -e "branch = ${GITHUB_REF_NAME}" + echo -e "env = ${ENV}" + env: + ENV: ${{ inputs.env }} - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 738a3050266..060b7c2a490 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -9,6 +9,6 @@ jobs: name: Label PR based on title runs-on: ubuntu-latest steps: - - uses: srvaroa/labeler@v1.4 + - uses: srvaroa/labeler@v1.11 env: GITHUB_TOKEN: ${{secrets.OTTO_THE_BOT_GH_TOKEN}} diff --git a/.github/workflows/pull_translations.yml b/.github/workflows/pull_translations.yml index 411dbe76683..f9cb78e85f9 100644 --- a/.github/workflows/pull_translations.yml +++ b/.github/workflows/pull_translations.yml @@ -28,7 +28,7 @@ jobs: run: yarn --immutable - name: Sync translations - uses: crowdin/github-action@v2.1.0 + uses: crowdin/github-action@v2.3.0 env: GITHUB_TOKEN: ${{secrets.OTTO_THE_BOT_GH_TOKEN}} GITHUB_ACTOR: otto-the-bot diff --git a/.github/workflows/sync_translations.yml b/.github/workflows/sync_translations.yml index 0e21f352977..4b50711cf4a 100644 --- a/.github/workflows/sync_translations.yml +++ b/.github/workflows/sync_translations.yml @@ -37,7 +37,7 @@ jobs: run: yarn translate:merge - name: Download translations - uses: crowdin/github-action@v2.1.0 + uses: crowdin/github-action@v2.3.0 env: GITHUB_TOKEN: ${{secrets.OTTO_THE_BOT_GH_TOKEN}} CROWDIN_PROJECT_ID: 342359 diff --git a/.husky/pre-commit b/.husky/pre-commit index dc0378c3488..37236231717 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -yarn lint-staged \ No newline at end of file +yarn lint-staged diff --git a/Dockerfile b/Dockerfile index f5419e98c4b..a5467679df6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22-alpine +FROM node:23-alpine # For some extra dependencies... RUN apk add --no-cache dumb-init git bash diff --git a/app-config/package.json b/app-config/package.json index 12572c87f19..94ddb5d0f2b 100644 --- a/app-config/package.json +++ b/app-config/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "wire-web-config-default-master": "https://github.com/wireapp/wire-web-config-wire#v0.31.29-0", - "wire-web-config-default-staging": "https://github.com/wireapp/wire-web-config-default#v0.31.28" + "wire-web-config-default-master": "https://github.com/wireapp/wire-web-config-wire#v0.31.36", + "wire-web-config-default-staging": "https://github.com/wireapp/wire-web-config-default#v0.31.35" } } diff --git a/bin/release_tag.ts b/bin/release_tag.ts index c9035cbc7bc..d33f378d20e 100644 --- a/bin/release_tag.ts +++ b/bin/release_tag.ts @@ -36,10 +36,12 @@ const filename = path.basename(__filename); const stage = process.argv[2]; const usageText = `Usage: ${filename} [-h|--help] `; +const exec = (command: string): string => execSync(command, {stdio: 'pipe'}).toString().trim(); + let commitId = process.argv[3]; let target = ''; let commitMessage = ''; -let branch = ''; +let branch = exec('git rev-parse --abbrev-ref HEAD'); const isDryRun = process.argv.includes('--dry-run'); const logger = logdown(filename, { @@ -48,8 +50,6 @@ const logger = logdown(filename, { }); logger.state.isEnabled = true; -const exec = (command: string): string => execSync(command, {stdio: 'pipe'}).toString().trim(); - if (isDryRun) { logger.info('Note: Dry run enabled.'); } @@ -61,12 +61,10 @@ switch (stage) { process.exit(); } case DeploymentStage.PRODUCTION: { - branch = 'master'; target = stage; break; } case DeploymentStage.STAGING: { - branch = 'dev'; target = stage; break; } @@ -117,6 +115,15 @@ const ask = (questionToAsk: string): Promise => { }; (async () => { + if (branch !== 'master' && target === DeploymentStage.PRODUCTION) { + const answer = await ask( + `⚠️ You are about to release a commit from branch "${branch}" to production. ARE YOU SURE?? [yes/no] `, + ); + if (answer !== 'yes') { + logger.info('Aborting.'); + process.exit(); + } + } const answer = await ask( `ℹ️ The commit "${commitMessage}" will be released with tag "${tagName}". Continue? [yes/no] `, ); diff --git a/jest.config.ts b/jest.config.ts index 8e37ab95824..2dcf361ee47 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -26,6 +26,7 @@ const config: Config = { // Must be in sync with tsconfig.json >> paths moduleNameMapper: { 'Components/(.*)': '/src/script/components/$1', + 'Hooks/(.*)': '/src/script/hooks/$1', 'I18n/(.*)': '/src/i18n/$1', 'Resource/(.*)': '/resource/$1', 'Util/(.*)': '/src/script/util/$1', diff --git a/package.json b/package.json index ca43780ef55..9ffba12771a 100644 --- a/package.json +++ b/package.json @@ -1,50 +1,49 @@ { "dependencies": { - "@datadog/browser-logs": "5.23.0", - "@datadog/browser-rum": "5.23.0", + "@datadog/browser-logs": "5.29.1", + "@datadog/browser-rum": "5.29.1", "@emotion/react": "11.11.4", - "@lexical/history": "0.16.1", - "@lexical/react": "0.16.1", - "@mediapipe/tasks-vision": "0.10.14", - "@wireapp/avs": "9.8.14", - "@wireapp/commons": "5.2.10", - "@wireapp/core": "46.1.1", - "@wireapp/react-ui-kit": "9.21.1", - "@wireapp/store-engine-dexie": "2.1.11", - "@wireapp/webapp-events": "0.22.1", + "@lexical/history": "0.19.0", + "@lexical/react": "0.19.0", + "@mediapipe/tasks-vision": "0.10.17", + "@wireapp/avs": "9.10.16", + "@wireapp/commons": "5.2.13", + "@wireapp/core": "46.6.0", + "@wireapp/react-ui-kit": "9.26.0", + "@wireapp/store-engine-dexie": "2.1.15", + "@wireapp/webapp-events": "0.24.3", "amplify": "https://github.com/wireapp/amplify#head=master", "beautiful-react-hooks": "5.0.2", "classnames": "2.5.1", "copy-webpack-plugin": "12.0.2", - "core-js": "3.37.1", - "countly-sdk-web": "24.4.0", - "date-fns": "3.6.0", + "core-js": "3.39.0", + "countly-sdk-web": "24.4.1", + "date-fns": "4.1.0", "dexie-batch": "0.4.3", "dexie-encrypted": "2.0.0", - "emoji-picker-react": "4.11.1", + "emoji-picker-react": "4.12.0", "highlight.js": "11.10.0", "http-status-codes": "2.3.0", "jimp": "0.22.12", - "jquery": "3.7.1", "js-cookie": "3.0.5", "jszip": "3.10.1", "keyboardjs": "2.7.0", "knockout": "3.5.1", - "lexical": "0.16.1", - "libsodium-wrappers": "0.7.14", + "lexical": "0.19.0", + "libsodium-wrappers": "0.7.15", "linkify-it": "5.0.0", "long": "5.2.3", "markdown-it": "14.0.0", "murmurhash": "2.0.1", - "oidc-client-ts": "3.0.1", + "oidc-client-ts": "3.1.0", "platform": "1.3.6", "react": "18.3.1", "react-dom": "18.3.1", - "react-error-boundary": "4.0.13", - "react-intl": "6.6.8", + "react-error-boundary": "4.1.2", + "react-intl": "6.8.5", "react-redux": "9.1.2", - "react-router": "6.25.1", - "react-router-dom": "6.25.1", + "react-router": "6.27.0", + "react-router-dom": "6.27.0", "react-transition-group": "4.4.5", "redux": "5.0.1", "redux-logdown": "1.0.4", @@ -52,60 +51,60 @@ "speakingurl": "14.0.1", "switch-path": "1.2.0", "tsyringe": "4.8.0", - "underscore": "1.13.6", - "uuid": "10.0.0", + "underscore": "1.13.7", + "uuid": "11.0.2", "webgl-utils.js": "1.1.0", "webrtc-adapter": "9.0.1", - "zustand": "4.5.4" + "zustand": "4.5.5" }, "devDependencies": { - "@babel/core": "7.24.9", - "@babel/plugin-proposal-decorators": "7.24.7", - "@babel/preset-env": "7.24.8", - "@babel/preset-react": "7.24.7", - "@babel/preset-typescript": "7.24.7", + "@babel/core": "7.26.0", + "@babel/plugin-proposal-decorators": "7.25.9", + "@babel/preset-env": "7.26.0", + "@babel/preset-react": "7.25.9", + "@babel/preset-typescript": "7.26.0", "@emotion/eslint-plugin": "11.11.0", - "@faker-js/faker": "8.4.1", - "@formatjs/cli": "6.2.12", + "@faker-js/faker": "9.1.0", + "@formatjs/cli": "6.3.6", "@roamhq/wrtc": "0.8.0", - "@testing-library/dom": "^10.3.2", - "@testing-library/react": "16.0.0", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "16.0.1", "@types/dexie-batch": "0.4.7", "@types/eslint": "8.56.5", "@types/fs-extra": "11.0.4", "@types/generate-changelog": "1.8.3", - "@types/jest": "29.5.12", - "@types/jquery": "3.5.30", + "@types/jest": "29.5.14", "@types/js-cookie": "3.0.6", "@types/jsdom": "21.1.7", "@types/keyboardjs": "2.5.3", "@types/libsodium-wrappers": "0", "@types/linkify-it": "5.0.0", "@types/markdown-it": "14.1.1", - "@types/node": "20.14.11", + "@types/node": "22.8.7", "@types/open-graph": "0.2.5", "@types/platform": "1.3.6", "@types/react": "18.3.3", - "@types/react-dom": "18.3.0", - "@types/react-transition-group": "4.4.10", + "@types/react-dom": "18.3.1", + "@types/react-transition-group": "4.4.11", "@types/redux-mock-store": "1.0.6", "@types/seedrandom": "3.0.8", "@types/sinon": "17.0.3", "@types/speakingurl": "13.0.6", - "@types/underscore": "1.11.15", - "@types/uuid": "10.0.0", + "@types/underscore": "1.13.0", + "@types/uuid": "^10.0.0", "@types/webpack-env": "1.18.5", - "@wireapp/copy-config": "2.2.3", + "@types/wicg-file-system-access": "^2023.10.5", + "@wireapp/copy-config": "2.2.10", "@wireapp/eslint-config": "3.0.7", "@wireapp/prettier-config": "0.6.4", - "@wireapp/store-engine": "5.1.7", + "@wireapp/store-engine": "5.1.11", "archiver": "7.0.1", - "autoprefixer": "10.4.19", - "babel-loader": "9.1.3", + "autoprefixer": "10.4.20", + "babel-loader": "9.2.1", "babel-plugin-transform-import-meta": "2.2.1", "cross-env": "7.0.3", "css-loader": "7.1.2", - "cssnano": "7.0.4", + "cssnano": "7.0.6", "dexie": "4.0.7", "dotenv": "16.4.5", "dpdm": "3.14.0", @@ -113,9 +112,9 @@ "eslint-plugin-prettier": "5.1.3", "fake-indexeddb": "6.0.0", "generate-changelog": "1.8.0", - "html-webpack-plugin": "5.6.0", - "husky": "9.1.1", - "i18next-scanner": "4.5.0", + "html-webpack-plugin": "5.6.3", + "husky": "9.1.6", + "i18next-scanner": "4.6.0", "intersection-observer": "0.12.2", "jest": "29.7.0", "jest-canvas-mock": "2.5.2", @@ -125,34 +124,34 @@ "jsdom-worker": "0.3.0", "less": "4.2.0", "less-loader": "12.2.0", - "lint-staged": "15.2.7", + "lint-staged": "15.2.10", "os-browserify": "0.3.0", "path-browserify": "1.0.1", - "postcss": "8.4.39", + "postcss": "8.4.47", "postcss-import": "16.1.0", "postcss-less": "6.0.0", "postcss-loader": "8.1.1", - "postcss-preset-env": "9.6.0", + "postcss-preset-env": "10.0.9", "postcss-scss": "4.0.9", "prettier": "3.3.2", "raw-loader": "4.0.2", - "redux-mock-store": "1.5.4", + "redux-mock-store": "1.5.5", "seedrandom": "3.0.5", - "simple-git": "3.25.0", + "simple-git": "3.27.0", "sinon": "18.0.0", "style-loader": "4.0.0", - "stylelint": "16.7.0", + "stylelint": "16.10.0", "stylelint-config-idiomatic-order": "10.0.0", "svg-inline-loader": "0.8.2", "text-encoding": "0.7.0", "ts-node": "10.9.2", "tsc-watch": "6.2.0", "typescript": "5.5.2", - "webpack": "5.93.0", + "webpack": "5.96.1", "webpack-cli": "5.1.4", - "webpack-dev-middleware": "7.3.0", + "webpack-dev-middleware": "7.4.2", "webpack-hot-middleware": "2.26.1", - "workbox-webpack-plugin": "7.1.0" + "workbox-webpack-plugin": "7.3.0" }, "engines": { "yarn": ">= 4.1.1", @@ -189,10 +188,11 @@ "lint:other": "yarn prettier --list-different && yarn stylelint", "lint:code": "eslint --ext .js,.ts,.tsx .", "check:circular-dependencies": "dpdm --no-warning --no-tree -T ./src/script/auth/main.tsx ./src/script/main/app.ts", - "postinstall": "yarn configure && cd server && yarn && cd .. && husky install", + "postinstall": "yarn configure && cd server && yarn && cd .. && husky", "prettier": "prettier --ignore-path .prettierignore \"**/*.{json,md,yml}\"", "release:staging": "git checkout dev && git pull && ts-node ./bin/release_tag.ts staging", "release:production": "git checkout master && git pull && ts-node ./bin/release_tag.ts production", + "release:custom": "npx ts-node ./bin/release_tag.ts", "start": "yarn build:server && yarn watch:server", "watch:server": "tsc-watch -P server/tsconfig.json --onSuccess \"yarn start:server:dev\"", "start:server:dev": "cross-env NODE_ENV=development node ./server/dist/index.js", diff --git a/server/Server.ts b/server/Server.ts index 665349889e8..8c0ec2ed600 100644 --- a/server/Server.ts +++ b/server/Server.ts @@ -67,6 +67,7 @@ class Server { this.initStaticRoutes(); this.initWebpack(); this.initSiteMap(this.config); + this.app.use('/libs', express.static(path.join(__dirname, 'libs'))); this.app.use(Root()); this.app.use(HealthCheckRoute()); this.app.use(ConfigRoute(this.config, this.clientConfig)); @@ -212,6 +213,7 @@ class Server { APP_BASE: this.config.APP_BASE, OPEN_GRAPH: this.config.OPEN_GRAPH, VERSION: this.config.VERSION, + COUNTLY_NONCE: this.config.COUNTLY_NONCE, }; } @@ -236,7 +238,7 @@ class Server { if (this.server) { reject('Server is already running.'); } else if (this.config.PORT_HTTP) { - if (this.config.DEVELOPMENT) { + if (this.config.DEVELOPMENT && this.config.DEVELOPMENT_ENABLE_TLS) { const options = { cert: fs.readFileSync(this.config.SSL_CERTIFICATE_PATH), key: fs.readFileSync(this.config.SSL_CERTIFICATE_KEY_PATH), diff --git a/server/bin/copy_server_assets.js b/server/bin/copy_server_assets.js index 46aa12e499c..970e1666fde 100755 --- a/server/bin/copy_server_assets.js +++ b/server/bin/copy_server_assets.js @@ -24,9 +24,20 @@ const path = require('path'); const srcFolder = '../'; const distFolder = '../dist/'; +const npmModulesFolder = '../../node_modules/'; const assetFolders = ['.ebextensions/', 'robots/', 'templates/', 'certificate']; assetFolders.forEach(assetFolder => { fs.copySync(path.resolve(__dirname, srcFolder, assetFolder), path.resolve(__dirname, distFolder, assetFolder)); }); + +// copy countly.min.js and the official countly boomerang plugin +fs.copySync( + path.resolve(__dirname, npmModulesFolder, 'countly-sdk-web/lib/countly.min.js'), + path.resolve(__dirname, distFolder, 'libs/countly/countly.min.js'), +); +fs.copySync( + path.resolve(__dirname, npmModulesFolder, 'countly-sdk-web/plugin/boomerang'), + path.resolve(__dirname, distFolder, 'libs/countly'), +); diff --git a/server/config/client.config.ts b/server/config/client.config.ts index 26d3828bf5a..5966226fd9c 100644 --- a/server/config/client.config.ts +++ b/server/config/client.config.ts @@ -23,14 +23,17 @@ import {Env} from './env'; export function generateConfig(params: ConfigGeneratorParams, env: Env) { const {urls, version, env: nodeEnv} = params; return { - APP_BASE: urls.base, + APP_BASE: urls.base ?? '', ANALYTICS_API_KEY: env.ANALYTICS_API_KEY, APP_NAME: env.APP_NAME ?? 'Wire', BACKEND_NAME: env.BACKEND_NAME, - BACKEND_REST: urls.api, - BACKEND_WS: urls.ws, + BACKEND_REST: urls.api ?? '', + BACKEND_WS: urls.ws ?? '', BRAND_NAME: env.BRAND_NAME, COUNTLY_API_KEY: env.COUNTLY_API_KEY, + COUNTLY_ENABLE_LOGGING: env.COUNTLY_ENABLE_LOGGING == 'true', + COUNTLY_FORCE_REPORTING: env.COUNTLY_FORCE_REPORTING == 'true', + COUNTLY_ALLOWED_BACKEND: env.COUNTLY_ALLOWED_BACKEND ?? '', DATADOG_APPLICATION_ID: env.DATADOG_APPLICATION_ID, DATADOG_CLIENT_TOKEN: env.DATADOG_CLIENT_TOKEN, ENABLE_DEV_BACKEND_API: env.ENABLE_DEV_BACKEND_API == 'true', @@ -45,14 +48,15 @@ export function generateConfig(params: ConfigGeneratorParams, env: Env) { CHECK_CONSENT: env.FEATURE_CHECK_CONSENT != 'false', CONFERENCE_AUTO_MUTE: env.FEATURE_CONFERENCE_AUTO_MUTE == 'true', ENABLE_IN_CALL_REACTIONS: env.FEATURE_ENABLE_IN_CALL_REACTIONS == 'true', + ENABLE_REMOVE_GROUP_CONVERSATION: env.FEATURE_ENABLE_REMOVE_GROUP_CONVERSATION == 'true', + ENABLE_DETACHED_CALLING_WINDOW: env.FEATURE_ENABLE_DETACHED_CALLING_WINDOW == 'true', + ENABLE_TEAM_CREATION: env.FEATURE_ENABLE_TEAM_CREATION == 'true', DEFAULT_LOGIN_TEMPORARY_CLIENT: env.FEATURE_DEFAULT_LOGIN_TEMPORARY_CLIENT == 'true', ENABLE_ACCOUNT_REGISTRATION: env.FEATURE_ENABLE_ACCOUNT_REGISTRATION != 'false', ENABLE_ACCOUNT_REGISTRATION_ACCEPT_TERMS_AND_PRIVACY_POLICY: env.FEATURE_ENABLE_ACCOUNT_REGISTRATION_ACCEPT_TERMS_AND_PRIVACY_POLICY == 'true', ENABLE_DEBUG: env.FEATURE_ENABLE_DEBUG == 'true', ENABLE_PING_CONFIRMATION: env.FEATURE_ENABLE_PING_CONFIRMATION == 'true', - //TODO: remove this when all clients have enabled this feature - ENABLE_LINK_PASSWORDS: env.FEATURE_ENABLE_LINK_PASSWORDS == 'true', ENABLE_DOMAIN_DISCOVERY: env.FEATURE_ENABLE_DOMAIN_DISCOVERY != 'false', ENABLE_ENFORCE_DESKTOP_APPLICATION_ONLY: env.FEATURE_ENABLE_ENFORCE_DESKTOP_APPLICATION_ONLY == 'true', ENABLE_EXTRA_CLIENT_ENTROPY: env.FEATURE_ENABLE_EXTRA_CLIENT_ENTROPY == 'true', @@ -71,6 +75,7 @@ export function generateConfig(params: ConfigGeneratorParams, env: Env) { USE_CORE_CRYPTO: env.FEATURE_USE_CORE_CRYPTO == 'true', MAX_USERS_TO_PING_WITHOUT_ALERT: (env.FEATURE_MAX_USERS_TO_PING_WITHOUT_ALERT && Number(env.FEATURE_MAX_USERS_TO_PING_WITHOUT_ALERT)) || 4, + DATADOG_ENVIRONMENT: env.FEATURE_DATADOG_ENVIRONMENT, }, MAX_GROUP_PARTICIPANTS: (env.MAX_GROUP_PARTICIPANTS && Number(env.MAX_GROUP_PARTICIPANTS)) || 500, MAX_VIDEO_PARTICIPANTS: (env.MAX_VIDEO_PARTICIPANTS && Number(env.MAX_VIDEO_PARTICIPANTS)) || 4, @@ -100,6 +105,7 @@ export function generateConfig(params: ConfigGeneratorParams, env: Env) { HISTORY: env.URL_SUPPORT_HISTORY, INDEX: env.URL_SUPPORT_INDEX, FOLDERS: env.URL_SUPPORT_FOLDERS, + FAVORITES: env.URL_SUPPORT_FAVORITES, LEARN_MORE_ABOUT_GUEST_LINKS: env.URL_LEARN_MORE_ABOUT_GUEST_LINKS, LEGAL_HOLD_BLOCK: env.URL_SUPPORT_LEGAL_HOLD_BLOCK, MICROPHONE_ACCESS_DENIED: env.URL_SUPPORT_MICROPHONE_ACCESS_DENIED, @@ -112,8 +118,6 @@ export function generateConfig(params: ConfigGeneratorParams, env: Env) { PRIVACY_WHY: env.URL_SUPPORT_PRIVACY_WHY, SCREEN_ACCESS_DENIED: env.URL_SUPPORT_SCREEN_ACCESS_DENIED, SYSTEM_KEYCHAIN_ACCESS: env.URL_SUPPORT_SYSTEM_KEYCHAIN_ACCESS, - URL_SUPPORT_FAVORITES: env.URL_SUPPORT_FAVORITES, - URL_SUPPORT_FOLDERS: env.URL_SUPPORT_FOLDERS, }, TEAMS_BASE: env.URL_TEAMS_BASE, TEAMS_CREATE: env.URL_TEAMS_CREATE, diff --git a/server/config/env.ts b/server/config/env.ts index 6a8f142c516..b82aaf81294 100644 --- a/server/config/env.ts +++ b/server/config/env.ts @@ -146,10 +146,6 @@ export type Env = { /** Set a default federation domain in case no domain can be found */ FEATURE_FEDERATION_DOMAIN: string; - // TODO: remove this when all clients have enabled this feature - /** Enable guest link passwords if the feature is enabled on the backend */ - FEATURE_ENABLE_LINK_PASSWORDS: string; - /** Feature toggle for the log in with a username. Can be set to true or false */ FEATURE_ENABLE_USERNAME_LOGIN: string; @@ -168,6 +164,15 @@ export type Env = { /** Feature to enable in call reactions */ FEATURE_ENABLE_IN_CALL_REACTIONS: string; + /** Feature to enable remove conversation locally */ + FEATURE_ENABLE_REMOVE_GROUP_CONVERSATION: string; + + /** Feature to enable calling popout window */ + FEATURE_ENABLE_DETACHED_CALLING_WINDOW: string; + + /** Feature to enable team creation flow for individual users */ + FEATURE_ENABLE_TEAM_CREATION: string; + /** Sets the verification ID for Google webmasters */ GOOGLE_WEBMASTER_ID: string; @@ -183,6 +188,18 @@ export type Env = { /** Sets the Countly product reporting API key */ COUNTLY_API_KEY: string; + /** Enables logging for Countly */ + COUNTLY_ENABLE_LOGGING: string; + + /** Force Countly reporting (only to be used on internal environments) */ + COUNTLY_FORCE_REPORTING: string; + + /** Countly allow list for backend urls + * Multiple entries separated by comma, e.g. "https://nginz-https.anta.wire.link, https://nginz-https.diya.wire.link, https://prod-nginz-https.wire.com" + * Used to disable countly tracking on on-prem instances + */ + COUNTLY_ALLOWED_BACKEND: string; + /** Open graph header description */ OPEN_GRAPH_DESCRIPTION: string; @@ -324,4 +341,5 @@ export type Env = { DATADOG_APPLICATION_ID?: string; DATADOG_CLIENT_TOKEN?: string; + FEATURE_DATADOG_ENVIRONMENT?: string; }; diff --git a/server/config/server.config.ts b/server/config/server.config.ts index 1bda4a7bf7a..7d17381a9a3 100644 --- a/server/config/server.config.ts +++ b/server/config/server.config.ts @@ -68,7 +68,17 @@ function parseCommaSeparatedList(list: string = ''): string[] { return cleanedList.split(','); } -function mergedCSP({urls}: ConfigGeneratorParams, env: Record): Record> { +// This is a constant that is used to use as CSP nonce for the countly script +const COUNTLY_NONCE = { + value: '36f73136-20aa-4c87-aff4-667cdc814d98', + cspString: "'nonce-36f73136-20aa-4c87-aff4-667cdc814d98'", +}; + +function mergedCSP( + {urls}: ConfigGeneratorParams, + env: Record, + countlyCSPNonce: string, +): Record> { const objectSrc = parseCommaSeparatedList(env.CSP_EXTRA_OBJECT_SRC); const csp = { connectSrc: [...defaultCSP.connectSrc, urls.api, urls.ws, ...parseCommaSeparatedList(env.CSP_EXTRA_CONNECT_SRC)], @@ -79,7 +89,7 @@ function mergedCSP({urls}: ConfigGeneratorParams, env: Record): manifestSrc: [...defaultCSP.manifestSrc, ...parseCommaSeparatedList(env.CSP_EXTRA_MANIFEST_SRC)], mediaSrc: [...defaultCSP.mediaSrc, ...parseCommaSeparatedList(env.CSP_EXTRA_MEDIA_SRC)], objectSrc: objectSrc.length > 0 ? objectSrc : ["'none'"], - scriptSrc: [...defaultCSP.scriptSrc, ...parseCommaSeparatedList(env.CSP_EXTRA_SCRIPT_SRC)], + scriptSrc: [...defaultCSP.scriptSrc, ...parseCommaSeparatedList(env.CSP_EXTRA_SCRIPT_SRC), countlyCSPNonce], styleSrc: [...defaultCSP.styleSrc, ...parseCommaSeparatedList(env.CSP_EXTRA_STYLE_SRC)], workerSrc: [...defaultCSP.workerSrc, ...parseCommaSeparatedList(env.CSP_EXTRA_WORKER_SRC)], }; @@ -95,10 +105,11 @@ export function generateConfig(params: ConfigGeneratorParams, env: Env) { COMMIT: commit, VERSION: version, CACHE_DURATION_SECONDS: 300, - CSP: mergedCSP(params, env), + CSP: mergedCSP(params, env, COUNTLY_NONCE.cspString), BACKEND_REST: urls.api, BACKEND_WS: urls.ws, DEVELOPMENT: nodeEnv === 'development', + DEVELOPMENT_ENABLE_TLS: urls.base.startsWith('https://'), ENFORCE_HTTPS: env.ENFORCE_HTTPS != 'false', ENVIRONMENT: nodeEnv, GOOGLE_WEBMASTER_ID: env.GOOGLE_WEBMASTER_ID, @@ -114,6 +125,7 @@ export function generateConfig(params: ConfigGeneratorParams, env: Env) { ALLOWED_HOSTS: ['app.wire.com'], DISALLOW: readFile(ROBOTS_DISALLOW_FILE, 'User-agent: *\r\nDisallow: /'), }, + COUNTLY_NONCE: COUNTLY_NONCE.value, SSL_CERTIFICATE_KEY_PATH: env.SSL_CERTIFICATE_KEY_PATH || path.join(__dirname, '../certificate/development-key.pem'), SSL_CERTIFICATE_PATH: env.SSL_CERTIFICATE_PATH || path.join(__dirname, '../certificate/development-cert.pem'), diff --git a/server/package.json b/server/package.json index 5e54728be88..982cec481d9 100644 --- a/server/package.json +++ b/server/package.json @@ -4,17 +4,17 @@ "main": "dist/index.js", "license": "GPL-3.0", "dependencies": { - "@wireapp/commons": "5.2.10", + "@wireapp/commons": "5.2.13", "dotenv": "16.4.5", "dotenv-extended": "2.9.0", - "express": "4.19.2", + "express": "4.21.1", "express-sitemap-xml": "3.1.0", "express-useragent": "1.0.15", "fs-extra": "11.2.0", "geolite2": "1.3.0", "hbs": "4.2.0", - "helmet": "7.1.0", - "http-proxy-middleware": "3.0.0", + "helmet": "8.0.0", + "http-proxy-middleware": "3.0.3", "http-status-codes": "2.3.0", "logdown": "3.3.1", "maxmind": "4.3.10", @@ -29,11 +29,11 @@ "@types/fs-extra": "11.0.4", "@types/geolite2": "2.0.0", "@types/hbs": "4.0.4", - "@types/jest": "^29.5.12", - "@types/node": "18.11.18", + "@types/jest": "^29.5.14", + "@types/node": "22.5.5", "jest": "29.7.0", "rimraf": "6.0.1", - "typescript": "5.5.3" + "typescript": "5.6.3" }, "engines": { "node": ">= 20.14.0" diff --git a/server/yarn.lock b/server/yarn.lock index e8289037c59..9f0a23a1b17 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -984,12 +984,12 @@ __metadata: languageName: node linkType: hard -"@types/http-proxy@npm:^1.17.10": - version: 1.17.14 - resolution: "@types/http-proxy@npm:1.17.14" +"@types/http-proxy@npm:^1.17.15": + version: 1.17.15 + resolution: "@types/http-proxy@npm:1.17.15" dependencies: "@types/node": "npm:*" - checksum: 10/aa1a3e66cd43cbf06ea5901bf761d2031200a0ab42ba7e462a15c752e70f8669f21fb3be7c2f18fefcb83b95132dfa15740282e7421b856745598fbaea8e3a42 + checksum: 10/fa86d5397c021f6c824d1143a206009bfb64ff703da32fb30f6176c603daf6c24ce3a28daf26b3945c94dd10f9d76f07ea7a6a2c3e9b710e00ff42da32e08dea languageName: node linkType: hard @@ -1018,13 +1018,13 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:^29.5.12": - version: 29.5.12 - resolution: "@types/jest@npm:29.5.12" +"@types/jest@npm:^29.5.14": + version: 29.5.14 + resolution: "@types/jest@npm:29.5.14" dependencies: expect: "npm:^29.0.0" pretty-format: "npm:^29.0.0" - checksum: 10/312e8dcf92cdd5a5847d6426f0940829bca6fe6b5a917248f3d7f7ef5d85c9ce78ef05e47d2bbabc40d41a930e0e36db2d443d2610a9e3db9062da2d5c904211 + checksum: 10/59ec7a9c4688aae8ee529316c43853468b6034f453d08a2e1064b281af9c81234cec986be796288f1bbb29efe943bc950e70c8fa8faae1e460d50e3cf9760f9b languageName: node linkType: hard @@ -1060,10 +1060,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:18.11.18": - version: 18.11.18 - resolution: "@types/node@npm:18.11.18" - checksum: 10/da05cf3a0036ef05cd695ac4cb265948593acbe723ba818f0ca0ce466b13ba99e1aac3a363086d6b8c7ea8f30c9233478e0293ac878a6f4b1d5515b10c392257 +"@types/node@npm:22.5.5": + version: 22.5.5 + resolution: "@types/node@npm:22.5.5" + dependencies: + undici-types: "npm:~6.19.2" + checksum: 10/172d02c8e6d921699edcf559c28b3805616bd6481af1b3cb0299f89ad9a6f33b71050434c06ce7b503166054a26275344187c443f99f745d0b12601372452f19 languageName: node linkType: hard @@ -1125,15 +1127,15 @@ __metadata: languageName: node linkType: hard -"@wireapp/commons@npm:5.2.10": - version: 5.2.10 - resolution: "@wireapp/commons@npm:5.2.10" +"@wireapp/commons@npm:5.2.13": + version: 5.2.13 + resolution: "@wireapp/commons@npm:5.2.13" dependencies: ansi-regex: "npm:5.0.1" fs-extra: "npm:11.2.0" logdown: "npm:3.3.1" platform: "npm:1.3.6" - checksum: 10/d3fc853e50b461092b0153b19d831464242bb18ecb099e1510b50ef7347708b172a79c13d6af1bb4a394c45aaae5b34f5af7be92f3a9622a57de073d1b14948a + checksum: 10/ea356bf0d9d6ff401031fc746512463b5981bae4e94237de2f0fd1177eca759c0772bb111187b38863098d8eaea0dd6085bf7b4f2675667c591ab9e61fd412d7 languageName: node linkType: hard @@ -1466,9 +1468,9 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.20.2": - version: 1.20.2 - resolution: "body-parser@npm:1.20.2" +"body-parser@npm:1.20.3": + version: 1.20.3 + resolution: "body-parser@npm:1.20.3" dependencies: bytes: "npm:3.1.2" content-type: "npm:~1.0.5" @@ -1478,11 +1480,11 @@ __metadata: http-errors: "npm:2.0.0" iconv-lite: "npm:0.4.24" on-finished: "npm:2.4.1" - qs: "npm:6.11.0" + qs: "npm:6.13.0" raw-body: "npm:2.5.2" type-is: "npm:~1.6.18" unpipe: "npm:1.0.0" - checksum: 10/3cf171b82190cf91495c262b073e425fc0d9e25cc2bf4540d43f7e7bbca27d6a9eae65ca367b6ef3993eea261159d9d2ab37ce444e8979323952e12eb3df319a + checksum: 10/8723e3d7a672eb50854327453bed85ac48d045f4958e81e7d470c56bf111f835b97e5b73ae9f6393d0011cc9e252771f46fd281bbabc57d33d3986edf1e6aeca languageName: node linkType: hard @@ -1505,7 +1507,7 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.2, braces@npm:~3.0.2": +"braces@npm:^3.0.3, braces@npm:~3.0.2": version: 3.0.3 resolution: "braces@npm:3.0.3" dependencies: @@ -1571,13 +1573,16 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" +"call-bind@npm:^1.0.7": + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" dependencies: - function-bind: "npm:^1.1.1" - get-intrinsic: "npm:^1.0.2" - checksum: 10/ca787179c1cbe09e1697b56ad499fd05dc0ae6febe5081d728176ade699ea6b1589240cb1ff1fe11fcf9f61538c1af60ad37e8eb2ceb4ef21cd6085dfd3ccedd + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.1" + checksum: 10/cd6fe658e007af80985da5185bff7b55e12ef4c2b6f41829a26ed1eef254b1f1c12e3dfd5b2b068c6ba8b86aba62390842d81752e67dcbaec4f6f76e7113b6b7 languageName: node linkType: hard @@ -1827,10 +1832,10 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.6.0": - version: 0.6.0 - resolution: "cookie@npm:0.6.0" - checksum: 10/c1f8f2ea7d443b9331680598b0ae4e6af18a618c37606d1bbdc75bec8361cce09fe93e727059a673f2ba24467131a9fb5a4eec76bb1b149c1b3e1ccb268dc583 +"cookie@npm:0.7.1": + version: 0.7.1 + resolution: "cookie@npm:0.7.1" + checksum: 10/aec6a6aa0781761bf55d60447d6be08861d381136a0fe94aa084fddd4f0300faa2b064df490c6798adfa1ebaef9e0af9b08a189c823e0811b8b313b3d9a03380 languageName: node linkType: hard @@ -1927,6 +1932,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.3.6": + version: 4.3.6 + resolution: "debug@npm:4.3.6" + dependencies: + ms: "npm:2.1.2" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10/d3adb9af7d57a9e809a68f404490cf776122acca16e6359a2702c0f462e510e91f9765c07f707b8ab0d91e03bad57328f3256f5082631cefb5393d0394d50fb7 + languageName: node + linkType: hard + "dedent@npm:^1.0.0": version: 1.5.1 resolution: "dedent@npm:1.5.1" @@ -1946,6 +1963,17 @@ __metadata: languageName: node linkType: hard +"define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10/abdcb2505d80a53524ba871273e5da75e77e52af9e15b3aa65d8aad82b8a3a424dad7aee2cc0b71470ac7acf501e08defac362e8b6a73cdb4309f028061df4ae + languageName: node + linkType: hard + "degenerator@npm:^5.0.0": version: 5.0.1 resolution: "degenerator@npm:5.0.1" @@ -2069,6 +2097,13 @@ __metadata: languageName: node linkType: hard +"encodeurl@npm:~2.0.0": + version: 2.0.0 + resolution: "encodeurl@npm:2.0.0" + checksum: 10/abf5cd51b78082cf8af7be6785813c33b6df2068ce5191a40ca8b1afe6a86f9230af9a9ce694a5ce4665955e5c1120871826df9c128a642e09c58d592e2807fe + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -2110,6 +2145,22 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: "npm:^1.2.4" + checksum: 10/f66ece0a887b6dca71848fa71f70461357c0e4e7249696f81bad0a1f347eed7b31262af4a29f5d726dc026426f085483b6b90301855e647aa8e21936f07293c6 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5 + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -2283,42 +2334,42 @@ __metadata: languageName: node linkType: hard -"express@npm:4.19.2": - version: 4.19.2 - resolution: "express@npm:4.19.2" +"express@npm:4.21.1": + version: 4.21.1 + resolution: "express@npm:4.21.1" dependencies: accepts: "npm:~1.3.8" array-flatten: "npm:1.1.1" - body-parser: "npm:1.20.2" + body-parser: "npm:1.20.3" content-disposition: "npm:0.5.4" content-type: "npm:~1.0.4" - cookie: "npm:0.6.0" + cookie: "npm:0.7.1" cookie-signature: "npm:1.0.6" debug: "npm:2.6.9" depd: "npm:2.0.0" - encodeurl: "npm:~1.0.2" + encodeurl: "npm:~2.0.0" escape-html: "npm:~1.0.3" etag: "npm:~1.8.1" - finalhandler: "npm:1.2.0" + finalhandler: "npm:1.3.1" fresh: "npm:0.5.2" http-errors: "npm:2.0.0" - merge-descriptors: "npm:1.0.1" + merge-descriptors: "npm:1.0.3" methods: "npm:~1.1.2" on-finished: "npm:2.4.1" parseurl: "npm:~1.3.3" - path-to-regexp: "npm:0.1.7" + path-to-regexp: "npm:0.1.10" proxy-addr: "npm:~2.0.7" - qs: "npm:6.11.0" + qs: "npm:6.13.0" range-parser: "npm:~1.2.1" safe-buffer: "npm:5.2.1" - send: "npm:0.18.0" - serve-static: "npm:1.15.0" + send: "npm:0.19.0" + serve-static: "npm:1.16.2" setprototypeof: "npm:1.2.0" statuses: "npm:2.0.1" type-is: "npm:~1.6.18" utils-merge: "npm:1.0.1" vary: "npm:~1.1.2" - checksum: 10/3fcd792536f802c059789ef48db3851b87e78fba103423e524144d79af37da7952a2b8d4e1a007f423329c7377d686d9476ac42e7d9ea413b80345d495e30a3a + checksum: 10/5d4a36dd03c1d1cce93172e9b185b5cd13a978d29ee03adc51cd278be7b4a514ae2b63e2fdaec0c00fdc95c6cfb396d9dd1da147917ffd337d6cd0778e08c9bc languageName: node linkType: hard @@ -2370,18 +2421,18 @@ __metadata: languageName: node linkType: hard -"finalhandler@npm:1.2.0": - version: 1.2.0 - resolution: "finalhandler@npm:1.2.0" +"finalhandler@npm:1.3.1": + version: 1.3.1 + resolution: "finalhandler@npm:1.3.1" dependencies: debug: "npm:2.6.9" - encodeurl: "npm:~1.0.2" + encodeurl: "npm:~2.0.0" escape-html: "npm:~1.0.3" on-finished: "npm:2.4.1" parseurl: "npm:~1.3.3" statuses: "npm:2.0.1" unpipe: "npm:~1.0.0" - checksum: 10/635718cb203c6d18e6b48dfbb6c54ccb08ea470e4f474ddcef38c47edcf3227feec316f886dd701235997d8af35240cae49856721ce18f539ad038665ebbf163 + checksum: 10/4babe72969b7373b5842bc9f75c3a641a4d0f8eb53af6b89fa714d4460ce03fb92b28de751d12ba415e96e7e02870c436d67412120555e2b382640535697305b languageName: node linkType: hard @@ -2502,7 +2553,7 @@ __metadata: languageName: node linkType: hard -"function-bind@npm:^1.1.1": +"function-bind@npm:^1.1.2": version: 1.1.2 resolution: "function-bind@npm:1.1.2" checksum: 10/185e20d20f10c8d661d59aac0f3b63b31132d492e1b11fcc2a93cb2c47257ebaee7407c38513efd2b35cafdf972d9beb2ea4593c1e0f3bf8f2744836928d7454 @@ -2555,15 +2606,16 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2": - version: 1.2.1 - resolution: "get-intrinsic@npm:1.2.1" +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.4": + version: 1.2.4 + resolution: "get-intrinsic@npm:1.2.4" dependencies: - function-bind: "npm:^1.1.1" - has: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" has-proto: "npm:^1.0.1" has-symbols: "npm:^1.0.3" - checksum: 10/aee631852063f8ad0d4a374970694b5c17c2fb5c92bd1929476d7eb8798ce7aebafbf9a34022c05fd1adaa2ce846d5877a627ce1986f81fc65adf3b81824bd54 + hasown: "npm:^2.0.0" + checksum: 10/85bbf4b234c3940edf8a41f4ecbd4e25ce78e5e6ad4e24ca2f77037d983b9ef943fd72f00f3ee97a49ec622a506b67db49c36246150377efcda1c9eb03e5f06d languageName: node linkType: hard @@ -2668,6 +2720,15 @@ __metadata: languageName: node linkType: hard +"gopd@npm:^1.0.1": + version: 1.0.1 + resolution: "gopd@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.1.3" + checksum: 10/5fbc7ad57b368ae4cd2f41214bd947b045c1a4be2f194a7be1778d71f8af9dbf4004221f3b6f23e30820eb0d052b4f819fe6ebe8221e2a3c6f0ee4ef173421ca + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -2725,6 +2786,15 @@ __metadata: languageName: node linkType: hard +"has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10/2d8c9ab8cebb572e3362f7d06139a4592105983d4317e68f7adba320fe6ddfc8874581e0971e899e633fd5f72e262830edce36d5a0bc863dad17ad20572484b2 + languageName: node + linkType: hard + "has-proto@npm:^1.0.1": version: 1.0.1 resolution: "has-proto@npm:1.0.1" @@ -2753,6 +2823,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.0": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10/7898a9c1788b2862cf0f9c345a6bec77ba4a0c0983c7f19d610c382343d4f98fa260686b225dfb1f88393a66679d2ec58ee310c1d6868c081eda7918f32cc70a + languageName: node + linkType: hard + "hbs@npm:4.2.0": version: 4.2.0 resolution: "hbs@npm:4.2.0" @@ -2763,10 +2842,10 @@ __metadata: languageName: node linkType: hard -"helmet@npm:7.1.0": - version: 7.1.0 - resolution: "helmet@npm:7.1.0" - checksum: 10/aa13b0907d9d31b65e4ec73093b58d7e94e83c06818cc06869f5eb95d205574a14f01ed71cadfac4a68b736a1a6d7ef2290f3f6ef77ac664c7e75b8f9eddf88c +"helmet@npm:8.0.0": + version: 8.0.0 + resolution: "helmet@npm:8.0.0" + checksum: 10/cf30579d1dbd095e301458265fb6b3446d1ee0598c99b5e946afda8a72c035a6a7ebf2176168d5ca2541e6e522a88fb58d06f0eeec4ab378646960de9aea6584 languageName: node linkType: hard @@ -2818,17 +2897,17 @@ __metadata: languageName: node linkType: hard -"http-proxy-middleware@npm:3.0.0": - version: 3.0.0 - resolution: "http-proxy-middleware@npm:3.0.0" +"http-proxy-middleware@npm:3.0.3": + version: 3.0.3 + resolution: "http-proxy-middleware@npm:3.0.3" dependencies: - "@types/http-proxy": "npm:^1.17.10" - debug: "npm:^4.3.4" + "@types/http-proxy": "npm:^1.17.15" + debug: "npm:^4.3.6" http-proxy: "npm:^1.18.1" - is-glob: "npm:^4.0.1" - is-plain-obj: "npm:^3.0.0" - micromatch: "npm:^4.0.5" - checksum: 10/ea3e58c4665821aaf6060f59029bc8dbdbe7b13d6c74c7e80fb6c8ddc5a7c3f0fa970898f98dd5e006b138f64d23b3a7b9f30b8a525ed254b5aa88712a3b3010 + is-glob: "npm:^4.0.3" + is-plain-object: "npm:^5.0.0" + micromatch: "npm:^4.0.8" + checksum: 10/32f58c29288ca63e109909fb998bd0f6f50eb15a98dec9487eac07dfc4f09d8507dbfa00b44442d868bafa904bd633c8bbd55686bb13b4d4af4f5c5b3bbca430 languageName: node linkType: hard @@ -3021,7 +3100,7 @@ __metadata: languageName: node linkType: hard -"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1": +"is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": version: 4.0.3 resolution: "is-glob@npm:4.0.3" dependencies: @@ -3044,10 +3123,10 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^3.0.0": - version: 3.0.0 - resolution: "is-plain-obj@npm:3.0.0" - checksum: 10/a6ebdf8e12ab73f33530641972a72a4b8aed6df04f762070d823808303e4f76d87d5ea5bd76f96a7bbe83d93f04ac7764429c29413bd9049853a69cb630fb21c +"is-plain-object@npm:^5.0.0": + version: 5.0.0 + resolution: "is-plain-object@npm:5.0.0" + checksum: 10/e32d27061eef62c0847d303125440a38660517e586f2f3db7c9d179ae5b6674ab0f469d519b2e25c147a1a3bc87156d0d5f4d8821e0ce4a9ee7fe1fcf11ce45c languageName: node linkType: hard @@ -3860,10 +3939,10 @@ __metadata: languageName: node linkType: hard -"merge-descriptors@npm:1.0.1": - version: 1.0.1 - resolution: "merge-descriptors@npm:1.0.1" - checksum: 10/5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26 +"merge-descriptors@npm:1.0.3": + version: 1.0.3 + resolution: "merge-descriptors@npm:1.0.3" + checksum: 10/52117adbe0313d5defa771c9993fe081e2d2df9b840597e966aadafde04ae8d0e3da46bac7ca4efc37d4d2b839436582659cd49c6a43eacb3fe3050896a105d1 languageName: node linkType: hard @@ -3881,13 +3960,13 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": - version: 4.0.5 - resolution: "micromatch@npm:4.0.5" +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" dependencies: - braces: "npm:^3.0.2" + braces: "npm:^3.0.3" picomatch: "npm:^2.3.1" - checksum: 10/a749888789fc15cac0e03273844dbd749f9f8e8d64e70c564bcf06a033129554c789bb9e30d7566d7ff6596611a08e58ac12cf2a05f6e3c9c47c50c4c7e12fa2 + checksum: 10/6bf2a01672e7965eb9941d1f02044fad2bd12486b5553dc1116ff24c09a8723157601dc992e74c911d896175918448762df3b3fd0a6b61037dd1a9766ddfbf58 languageName: node linkType: hard @@ -4245,10 +4324,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.9.0": - version: 1.13.0 - resolution: "object-inspect@npm:1.13.0" - checksum: 10/d64609c3738a916d4c6d8306436427bfab87d5a68c017306aee9e8ebf39c9c08e6619f8397e520a64ff9b8545a2dcee9f4704157020700e324ccd179bd5ef931 +"object-inspect@npm:^1.13.1": + version: 1.13.2 + resolution: "object-inspect@npm:1.13.2" + checksum: 10/7ef65583b6397570a17c56f0c1841e0920e83900f2c94638927abb7b81ac08a19c7aae135bd9dcca96208cac0c7332b4650fb927f027b0cf92d71df2990d0561 languageName: node linkType: hard @@ -4474,10 +4553,10 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:0.1.7": - version: 0.1.7 - resolution: "path-to-regexp@npm:0.1.7" - checksum: 10/701c99e1f08e3400bea4d701cf6f03517474bb1b608da71c78b1eb261415b645c5670dfae49808c89e12cea2dccd113b069f040a80de012da0400191c6dbd1c8 +"path-to-regexp@npm:0.1.10": + version: 0.1.10 + resolution: "path-to-regexp@npm:0.1.10" + checksum: 10/894e31f1b20e592732a87db61fff5b95c892a3fe430f9ab18455ebe69ee88ef86f8eb49912e261f9926fc53da9f93b46521523e33aefd9cb0a7b0d85d7096006 languageName: node linkType: hard @@ -4715,12 +4794,12 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.11.0": - version: 6.11.0 - resolution: "qs@npm:6.11.0" +"qs@npm:6.13.0": + version: 6.13.0 + resolution: "qs@npm:6.13.0" dependencies: - side-channel: "npm:^1.0.4" - checksum: 10/5a3bfea3e2f359ede1bfa5d2f0dbe54001aa55e40e27dc3e60fab814362d83a9b30758db057c2011b6f53a2d4e4e5150194b5bac45372652aecb3e3c0d4b256e + side-channel: "npm:^1.0.6" + checksum: 10/f548b376e685553d12e461409f0d6e5c59ec7c7d76f308e2a888fd9db3e0c5e89902bedd0754db3a9038eda5f27da2331a6f019c8517dc5e0a16b3c9a6e9cef8 languageName: node linkType: hard @@ -4931,9 +5010,9 @@ __metadata: languageName: node linkType: hard -"send@npm:0.18.0": - version: 0.18.0 - resolution: "send@npm:0.18.0" +"send@npm:0.19.0": + version: 0.19.0 + resolution: "send@npm:0.19.0" dependencies: debug: "npm:2.6.9" depd: "npm:2.0.0" @@ -4948,19 +5027,19 @@ __metadata: on-finished: "npm:2.4.1" range-parser: "npm:~1.2.1" statuses: "npm:2.0.1" - checksum: 10/ec66c0ad109680ad8141d507677cfd8b4e40b9559de23191871803ed241718e99026faa46c398dcfb9250676076573bd6bfe5d0ec347f88f4b7b8533d1d391cb + checksum: 10/1f6064dea0ae4cbe4878437aedc9270c33f2a6650a77b56a16b62d057527f2766d96ee282997dd53ec0339082f2aad935bc7d989b46b48c82fc610800dc3a1d0 languageName: node linkType: hard -"serve-static@npm:1.15.0": - version: 1.15.0 - resolution: "serve-static@npm:1.15.0" +"serve-static@npm:1.16.2": + version: 1.16.2 + resolution: "serve-static@npm:1.16.2" dependencies: - encodeurl: "npm:~1.0.2" + encodeurl: "npm:~2.0.0" escape-html: "npm:~1.0.3" parseurl: "npm:~1.3.3" - send: "npm:0.18.0" - checksum: 10/699b2d4c29807a51d9b5e0f24955346911437aebb0178b3c4833ad30d3eca93385ff9927254f5c16da345903cad39d9cd4a532198c95a5129cc4ed43911b15a4 + send: "npm:0.19.0" + checksum: 10/7fa9d9c68090f6289976b34fc13c50ac8cd7f16ae6bce08d16459300f7fc61fbc2d7ebfa02884c073ec9d6ab9e7e704c89561882bbe338e99fcacb2912fde737 languageName: node linkType: hard @@ -4971,6 +5050,20 @@ __metadata: languageName: node linkType: hard +"set-function-length@npm:^1.2.1": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10/505d62b8e088468917ca4e3f8f39d0e29f9a563b97dbebf92f4bd2c3172ccfb3c5b8e4566d5fcd00784a00433900e7cb8fbc404e2dbd8c3818ba05bb9d4a8a6d + languageName: node + linkType: hard + "setprototypeof@npm:1.2.0": version: 1.2.0 resolution: "setprototypeof@npm:1.2.0" @@ -5001,14 +5094,15 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.0.4": - version: 1.0.4 - resolution: "side-channel@npm:1.0.4" +"side-channel@npm:^1.0.6": + version: 1.0.6 + resolution: "side-channel@npm:1.0.6" dependencies: - call-bind: "npm:^1.0.0" - get-intrinsic: "npm:^1.0.2" - object-inspect: "npm:^1.9.0" - checksum: 10/c4998d9fc530b0e75a7fd791ad868fdc42846f072734f9080ff55cc8dc7d3899abcda24fd896aa6648c3ab7021b4bb478073eb4f44dfd55bce9714bc1a7c5d45 + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + object-inspect: "npm:^1.13.1" + checksum: 10/eb10944f38cebad8ad643dd02657592fa41273ce15b8bfa928d3291aff2d30c20ff777cfe908f76ccc4551ace2d1245822fdc576657cce40e9066c638ca8fa4d languageName: node linkType: hard @@ -5384,23 +5478,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.5.3": - version: 5.5.3 - resolution: "typescript@npm:5.5.3" +"typescript@npm:5.6.3": + version: 5.6.3 + resolution: "typescript@npm:5.6.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/11a867312419ed497929aafd2f1d28b2cd41810a5eb6c6e9e169559112e9ea073d681c121a29102e67cd4478d0a4ae37a306a5800f3717f59c4337e6a9bd5e8d + checksum: 10/c328e418e124b500908781d9f7b9b93cf08b66bf5936d94332b463822eea2f4e62973bfb3b8a745fdc038785cb66cf59d1092bac3ec2ac6a3e5854687f7833f1 languageName: node linkType: hard -"typescript@patch:typescript@npm%3A5.5.3#optional!builtin": - version: 5.5.3 - resolution: "typescript@patch:typescript@npm%3A5.5.3#optional!builtin::version=5.5.3&hash=5adc0c" +"typescript@patch:typescript@npm%3A5.6.3#optional!builtin": + version: 5.6.3 + resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=5adc0c" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/b61b8bb4b4d6a8a00f9d5f931f8c67070eed6ad11feabf4c41744a326987080bfc806a621596c70fbf2e5974eca3ed65bafeeeb22a078071bdfb51d8abd7c013 + checksum: 10/dc4bec403cd33a204b655b1152a096a08e7bad2c931cb59ef8ff26b6f2aa541bf98f09fc157958a60c921b1983a8dde9a85b692f9de60fa8f574fd131e3ae4dd languageName: node linkType: hard @@ -5429,6 +5523,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~6.19.2": + version: 6.19.8 + resolution: "undici-types@npm:6.19.8" + checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -5574,19 +5675,19 @@ __metadata: "@types/fs-extra": "npm:11.0.4" "@types/geolite2": "npm:2.0.0" "@types/hbs": "npm:4.0.4" - "@types/jest": "npm:^29.5.12" - "@types/node": "npm:18.11.18" - "@wireapp/commons": "npm:5.2.10" + "@types/jest": "npm:^29.5.14" + "@types/node": "npm:22.5.5" + "@wireapp/commons": "npm:5.2.13" dotenv: "npm:16.4.5" dotenv-extended: "npm:2.9.0" - express: "npm:4.19.2" + express: "npm:4.21.1" express-sitemap-xml: "npm:3.1.0" express-useragent: "npm:1.0.15" fs-extra: "npm:11.2.0" geolite2: "npm:1.3.0" hbs: "npm:4.2.0" - helmet: "npm:7.1.0" - http-proxy-middleware: "npm:3.0.0" + helmet: "npm:8.0.0" + http-proxy-middleware: "npm:3.0.3" http-status-codes: "npm:2.3.0" jest: "npm:29.7.0" logdown: "npm:3.3.1" @@ -5595,7 +5696,7 @@ __metadata: opn: "npm:6.0.0" pm2: "npm:5.4.2" rimraf: "npm:6.0.1" - typescript: "npm:5.5.3" + typescript: "npm:5.6.3" languageName: unknown linkType: soft diff --git a/setupTests.js b/setupTests.js index e45cd0199d9..ef2b232baa3 100644 --- a/setupTests.js +++ b/setupTests.js @@ -33,6 +33,7 @@ import 'src/script/util/test/mock/navigatorPermissionsMock'; import 'src/script/util/test/mock/ResponseMock'; import 'src/script/util/test/mock/WebRTCMock'; import 'src/script/util/test/mock/resizeObserver.mock'; +import 'src/script/util/test/mock/wireEnvMock'; import encoding from 'text-encoding'; @@ -53,15 +54,6 @@ jest.mock('axios', () => { window.TextEncoder = encoding.TextEncoder; window.TextDecoder = encoding.TextDecoder; -window.wire = { - env: { - BACKEND_REST: 'https://test.wire.link', - FEATURE: {}, - URL: {SUPPORT: {}}, - NEW_PASSWORD_MINIMUM_LENGTH: 8, - }, -}; - window.z = {userPermission: {}}; window.URL.createObjectURL = jest.fn(); diff --git a/src/i18n/ar-SA.json b/src/i18n/ar-SA.json index 8eaad400aa4..d58530f0e14 100644 --- a/src/i18n/ar-SA.json +++ b/src/i18n/ar-SA.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} يتصل", "callStateOutgoing": "جاري الاتصال", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "ضيف", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "انضم إلى المحادثة", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": "، و ", "ephemeralRemaining": "متبقى", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "قبول", - "modalImproveWireHeadline": "ساعدنا لجعل Wire أفضل", - "modalImproveWireMessage": "يساعدنا إرسال تقارير الاستخدام و تقارير التعطيل للبرنامج المجهولة على تحسين منتجاتنا وخدماتنا. نحن لا نستخدم هذه المعلومات لأي شيء آخر.", - "modalImproveWireSecondary": "ليس الآن", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "إنشاء فريق", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "حذف الحساب", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "أضف خدمة", "searchServicePlaceholder": "البحث بحسب الاسم", "searchServices": "الخدمات", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "أعضاء الفريق", "searchTopPeople": "الأشخاص المفضلين", "searchTrySearch": "ابحث عن أشخاص بالاسم أو بالمُعرّف (اسم المستخدم)", @@ -1521,12 +1530,22 @@ "verify.resendCode": "أعد إرسال الرمز", "verify.subhead": "أدخِل رمز التحقق الذي أرسلناه إلى
{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "إنهاء الاتصال", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "مشاركه الشاشه", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "تم إيقاف الفيديو مؤقتاً", "videoCallScreenShareNotSupported": "مشاركه الشاشه غير مدعومه فى هذا المتصفح", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "نسخة واير هذه لا تستطيع المشاركة في المكالمة. من فضلك استخدم", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} يتصل. متصفحك لا يدعم المكالمات.", diff --git a/src/i18n/bn-BD.json b/src/i18n/bn-BD.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/bn-BD.json +++ b/src/i18n/bn-BD.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/ca-ES.json b/src/i18n/ca-ES.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/ca-ES.json +++ b/src/i18n/ca-ES.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/cs-CZ.json b/src/i18n/cs-CZ.json index 49633205b57..a96e35bf5f4 100644 --- a/src/i18n/cs-CZ.json +++ b/src/i18n/cs-CZ.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Zvoní…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Host", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", a ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Přijmout", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Ne", "modalIntegrationUnavailableHeadline": "Roboti jsou momentálně nedostupní", "modalIntegrationUnavailableMessage": "Děkujeme vám za váš zájem o roboty. Služba je pozastavena, zatímco pracujeme na další verzi.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Vytvořit tým", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Smazat účet", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Hledat podle jména", "searchServices": "Služby", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Členové týmu", "searchTopPeople": "Nejaktivnější kontakty", "searchTrySearch": "Vyhledávání osob podle\nvlastního nebo uživatelského jména", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Zavěsit", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Tato verze aplikace {{brandName}} se nemůže účastnit volání. Použijte prosím", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "Volá {{user}}. Tento prohlížeč nepodporuje volání.", diff --git a/src/i18n/da-DK.json b/src/i18n/da-DK.json index ef1f9a7d1a4..8610e73e1a4 100644 --- a/src/i18n/da-DK.json +++ b/src/i18n/da-DK.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringer op…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Gæst", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", og ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Besvar", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Nej", "modalIntegrationUnavailableHeadline": "Bots er ikke tilgængelig i øjeblikket", "modalIntegrationUnavailableMessage": "Tak for din interesse for bots. Tjenesten er i øjeblikket suspenderet, mens vi arbejder på den næste version. Bliv her.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Opret team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Slet konto", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Tilføj tjeneste", "searchServicePlaceholder": "Søg ved navn", "searchServices": "Tjenester", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team medlemmer", "searchTopPeople": "Top personer", "searchTrySearch": "Find personer ved navn eller Brugernavn", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Læg På", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Denne version af {{brandName}} kan ikke deltage i opkaldet. Brug venligst", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} ringer. Din browser understøtter ikke opkald.", diff --git a/src/i18n/de-DE.json b/src/i18n/de-DE.json index 82475bf41d7..c8717e5da1d 100644 --- a/src/i18n/de-DE.json +++ b/src/i18n/de-DE.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} ruft an", "callStateOutgoing": "Klingeln…", "callWasEndedBecause": "Ihr Anruf wurde beendet, weil", + "callingPopOutWindowTitle": "{{brandName}} Anruf", "callingRestrictedConferenceCallOwnerModalDescription": "Ihr Team nutzt derzeit das kostenlose Basis-Abo. Upgraden Sie auf Enterprise für den Zugriff auf weitere Funktionen wie das Starten von Telefonkonferenzen. [link]Erfahren Sie mehr über {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Auf Enterprise upgraden", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Jetzt upgraden", @@ -386,7 +387,7 @@ "conversationAllWelcomeMessage": "Willkommen bei Wire 👋", "conversationAssetDownloading": "Herunterladen…", "conversationAssetFailedDecryptDownloading": "DOWNLOAD FEHLGESCHLADEN (Datei konnte nicht entschlüsselt werden)", - "conversationAssetFailedHashDownloading": "DOWNLOAD FAILED (HASH DOES NOT MATCH)", + "conversationAssetFailedHashDownloading": "DOWNLOAD FEHLGESCHLADEN (HASH STIMMT NICHT ÜBEREIN)", "conversationAssetRestrictedAudioMessageHeadline": "Audionachricht", "conversationAssetUploadFailed": "Hochladen fehlgeschlagen", "conversationAssetUploading": "Hochladen…", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "Bislang sind Sie an keiner Gruppenunterhaltung beteiligt.", "conversationGuestIndicator": "Gast", "conversationImageAssetRestricted": "Empfang von Bildern ist verboten", + "conversationInternalEnvironmentDisclaimer": "Dies ist NICHT WIRE, sondern eine interne Testumgebung und ist nur für die Verwendung durch Mitarbeiter von Wire autorisiert. Jede öffentliche Nutzung ist UNTERSAGT. Die Daten der Benutzer dieser Testumgebung werden umfassend erfasst und analysiert. Um den sicheren Messenger Wire zu verwenden, besuchen Sie bitte [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Im Browser beitreten", "conversationJoin.existentAccountJoinWithoutLink": "an der Unterhaltung teilnehmen", "conversationJoin.existentAccountUserName": "Sie sind als {selfName} angemeldet", @@ -551,7 +553,7 @@ "conversationSendPastedFile": "Bild eingefügt am {{date}}", "conversationServicesWarning": "Dienste haben Zugriff auf den Inhalt dieser Unterhaltung", "conversationSomeone": "Jemand", - "conversationStartNewConversation": "Start a new conversation", + "conversationStartNewConversation": "Beginnen Sie eine neue Unterhaltung", "conversationTeamLeft": "[bold]{{name}}[/bold] wurde aus dem Team entfernt", "conversationToday": "Heute", "conversationTweetAuthor": " auf Twitter", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Geben Sie Ihre Zugangsdaten nur dann an, wenn Sie sicher sind, dass dies das Login Ihrer Organisation ist.", "customEnvRedirect.redirectHeadline": "Weiterleitung...", "customEnvRedirect.redirectTo": "Sie werden zu Ihrem speziellen Unternehmensservice weitergeleitet.", + "dataSharingModalAgree": "Zustimmen", + "dataSharingModalDecline": "Ablehnen", + "dataSharingModalDescription": "Helfen Sie mit, Wire zu verbessern, indem Sie Ihre Nutzungsdaten über eine pseudonyme ID mit uns teilen. Die Daten werden weder mit Ihren persönlichen Informationen verknüpft noch an Dritte außer der Wire Gruppe weitergegeben. Sie beinhalten zum Beispiel, wann Sie eine Funktion nutzen, Ihre App-Version, den Gerätetyp oder Ihr Betriebssystem. Diese Daten werden spätestens nach 365 Tagen gelöscht.
Weitere Einzelheiten finden Sie in unserer [link]Datenschutzerklärung[/link]. Sie können Ihre Zustimmung jederzeit widerrufen.", + "dataSharingModalTitle": "Einwilligung zum Teilen von Nutzungsdaten", "downloadLatestMLS": "Laden Sie die neueste MLS-Wire-Version herunter", "enumerationAnd": " und ", "ephemeralRemaining": "verbleibend", @@ -798,7 +804,7 @@ "index.createAccountForPersonalUse": "Privat", "index.createPersonalAccount": "Privat mit Freunden und der Familie chatten", "index.createTeam": "Sichere Zusammenarbeit für Unternehmen, Institutionen und Organisationen", - "index.disclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit {link}", + "index.disclaimer": "Dies ist NICHT WIRE, sondern eine interne Testumgebung und ist nur für die Verwendung durch Mitarbeiter von Wire autorisiert. Jede öffentliche Nutzung ist UNTERSAGT. Die Daten der Benutzer dieser Testumgebung werden umfassend erfasst und analysiert. Um den sicheren Messenger Wire zu verwenden, besuchen Sie bitte {link}", "index.enterprise": "Anmeldung für Unternehmen", "index.goBack": "Zurück", "index.login": "Anmelden", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Verwenden Sie mindestens {{minPasswordLength}} Zeichen, mit einem Kleinbuchstaben, einem Großbuchstaben, einer Zahl und einem Sonderzeichen.", "modalGuestLinkJoinLabel": "Passwort festlegen", "modalGuestLinkJoinPlaceholder": "Passwort eingeben", - "modalImproveWireAction": "Ich willige ein", - "modalImproveWireHeadline": "Wir brauchen immer Hilfe, um {{brandName}} zu verbessern", - "modalImproveWireMessage": "Ich willige ein, dass {{brandName}} anonymisierte Nutzungs- und Fehlerberichte erstellen und verwenden darf, um die {{brandName}}-App zu verbessern. Ich kann diese Einwilligung jederzeit widerrufen.", - "modalImproveWireSecondary": "Nein", "modalIntegrationUnavailableHeadline": "Bots momentan nicht verfügbar", "modalIntegrationUnavailableMessage": "Vielen Dank für Ihr Interesse an Bots. Der Dienst ist derzeit ausgesetzt, während wir an der nächsten Version arbeiten.", "modalLegalHoldConversationMissingConsentMessage": "Aufgrund der gesetzlichen Aufbewahrung können nur Team-Mitglieder zu dieser Unterhaltung hinzugefügt werden. [link]Mehr erfahren[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Profil-Link kopieren", "preferencesAccountCreateTeam": "Team erstellen", "preferencesAccountData": "Datennutzung", - "preferencesAccountDataCheckbox": "Anonyme Fehlerberichte senden", - "preferencesAccountDataDetail": "Senden Sie anonyme Fehlerberichte und grundlegende Daten wie Versionsnummer und Betriebssystem, damit {{brandName}} Probleme in der App identifizieren und lösen kann.", "preferencesAccountDataTelemetry": "Nutzungsdaten ermöglichen {{brandName}} zu verstehen, wie die Anwendung verwendet wird und wie sie verbessert werden kann. Die Daten sind anonym und umfassen nicht den Inhalt Ihrer Kommunikation (wie Nachrichten, Dateien oder Anrufe).", "preferencesAccountDataTelemetryCheckbox": "Anonyme Nutzungsdaten senden", "preferencesAccountDelete": "Benutzerkonto löschen", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Nicht überprüft", "proteusVerified": "Überprüft", "proteusVerifiedDetails": "Überprüft (Proteus)", + "qualityFeedback.bad": "Schlecht", + "qualityFeedback.description": "Wie beurteilen Sie die Gesamtqualität des Anrufs?", + "qualityFeedback.doNotAskAgain": "Nicht erneut fragen", + "qualityFeedback.excellent": "Sehr Gut", + "qualityFeedback.fair": "Befriedigend", + "qualityFeedback.heading": "Rückmeldung zur Anrufqualität", + "qualityFeedback.notificationSubmitted": "Ihr Feedback wurde übermittelt. Vielen Dank!", + "qualityFeedback.skip": "Überspringen", "readReceiptsToggleInfo": "Wenn diese Option aktiviert ist, können Sender sehen, wenn ihre Nachrichten gelesen werden.", "readReceiptsToggleName": "Lesebestätigungen", "receiptToggleInfo": "Wenn diese Option aktiviert ist, können Sender sehen, wenn ihre Nachrichten gelesen werden.", @@ -1365,7 +1373,7 @@ "searchContacts": "Kontakte", "searchConversations": "Unterhaltung suchen", "searchConversationsNoResult": "Keine Ergebnisse gefunden", - "searchConversationsNoResultConnectSuggestion": "Connect with new users or start a new conversation", + "searchConversationsNoResultConnectSuggestion": "Mit neuen Personen verbinden oder eine neue Unterhaltung beginnen", "searchCreateGroup": "Gruppe erstellen", "searchCreateGuestRoom": "Gästebereich erstellen", "searchDirectConversations": "1:1 Unterhaltung suchen", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Unterhaltung öffnen", "searchServicePlaceholder": "Nach Namen suchen", "searchServices": "Dienste", + "searchTeamGroups": "Team-Unterhaltungen", "searchTeamMembers": "Team-Mitglieder", "searchTopPeople": "Top Kontakte", "searchTrySearch": "Kontakte anhand ihres\nNamens oder Benutzernamens finden", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Code erneut senden", "verify.subhead": "Geben Sie den Bestätigungscode ein, den wir an{newline}{email} gesendet haben", "videoCallOverlayCamera": "Kamera", + "videoCallOverlayChangeViewMode": "Ansicht wechseln", + "videoCallOverlayCloseFullScreen": "Zurück zur minimierten Ansicht", "videoCallOverlayConversations": "Unterhaltungen", "videoCallOverlayFitVideoLabel": "Doppelklicken, um Vollbildmodus anzuzeigen", "videoCallOverlayFitVideoLabelGoBack": "Doppelklicken, um alle Teilnehmer anzuzeigen", "videoCallOverlayHangUp": "Auflegen", + "videoCallOverlayHideParticipantsList": "Teilnehmerliste ausblenden", "videoCallOverlayMicrophone": "Mikrofon", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "In neuem Fenster öffnen", + "videoCallOverlayParticipantsListLabel": "Teilnehmer ({{count}})", "videoCallOverlayShareScreen": "Bildschirm teilen", + "videoCallOverlayShowParticipantsList": "Teilnehmerliste anzeigen", + "videoCallOverlayViewModeAll": "Alle Teilnehmer anzeigen", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Nur aktive Sprecher anzeigen", "videoCallParticipantConnecting": "Verbinden…", "videoCallPaused": "Video wurde angehalten", "videoCallScreenShareNotSupported": "Bildschirmfreigabe wird von diesem Browser nicht unterstützt", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Kamera", "videoSpeakersTabAll": "Alle {{count}}", "videoSpeakersTabSpeakers": "Sprecher", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Diese Version von {{brandName}} kann nicht an Anrufen teilnehmen. Bitte nutzen Sie", "warningCallQualityPoor": "Schlechte Verbindung", "warningCallUnsupportedIncoming": "{{user}} ruft an. Ihr Browser unterstützt keine Anrufe.", diff --git a/src/i18n/el-GR.json b/src/i18n/el-GR.json index 5b329d7f3f5..43c862e498c 100644 --- a/src/i18n/el-GR.json +++ b/src/i18n/el-GR.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Καλεί…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Επισκέπτης", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", και ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Αποδοχή", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "’Οχι", "modalIntegrationUnavailableHeadline": "Προσωρινά δεν είναι διαθέσιμο", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Δημιουργήστε μια ομάδα", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Διαγραφή λογαριασμού", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Αναζήτηση βάση ονόματος", "searchServices": "Υπηρεσίες", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Μέλη ομάδας", "searchTopPeople": "Αγαπημένα άτομα", "searchTrySearch": "Εύρεση ατόμων βάση ονόματος ή ονόματος χρήστη", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Επαναποστολή κωδικού", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Τερματισμός κλήσης", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Αυτή η έκδοση του {{brandName}} δεν μπορεί να μετέχει στην κλήση. Παρακαλούμε χρησιμοποιήστε", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} καλεί. Το πρόγραμμα περιήγησής σας δεν υποστηρίζει κλήσεις.", diff --git a/src/i18n/en-US.json b/src/i18n/en-US.json index a7c3742d5b4..4f0fd1ecbdb 100644 --- a/src/i18n/en-US.json +++ b/src/i18n/en-US.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -436,6 +437,7 @@ "conversationDetailsActionDevices": "Devices", "conversationDetailsActionGuestOptions": "Guests", "conversationDetailsActionLeave": "Leave group", + "conversationDetailsActionRemove": "Remove group", "conversationDetailsActionNotifications": "Notifications", "conversationDetailsActionServicesOptions": "Services", "conversationDetailsActionTimedMessages": "Self-deleting messages", @@ -477,6 +479,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -533,7 +536,8 @@ "conversationPing": " pinged", "conversationPingConfirmTitle": "Are you sure you want to ping {{memberCount}} people?", "conversationPingYou": " pinged", - "conversationPlaybackError": "Unable to play", + "conversationPlaybackError": "Unsupported video type, please download", + "conversationPlaybackErrorDownload": "Download", "conversationPopoverFavorite": "Add to favorites", "conversationPopoverUnfavorite": "Remove from favorites", "conversationProtocolUpdatedToMLS": "This conversation now uses the new Messaging Layer Security (MLS) protocol. To communicate seamlessly, always use the latest version of Wire on your devices. [link]Learn more about MLS[/link]", @@ -634,6 +638,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1051,6 +1059,9 @@ "modalConversationRemoveGuestsOrServicesAction": "Disable", "modalConversationRemoveHeadline": "Remove?", "modalConversationRemoveMessage": "{{user}} won’t be able to send or receive messages in this conversation.", + "modalConversationRemoveGroupAction": "Remove", + "modalConversationRemoveGroupHeadline": "Remove group conversation?", + "modalConversationRemoveGroupMessage": "This will delete the group on this device. There is no option to restore the content.", "modalConversationRemoveServicesHeadline": "Disable services access?", "modalConversationRemoveServicesMessage": "Current services will be removed from the conversation. New services will not be allowed.", "modalConversationRevokeLinkAction": "Revoke link", @@ -1075,10 +1086,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1243,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1347,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1373,7 +1386,8 @@ "searchFederatedDomainNotAvailable": "The federated domain is currently not available.", "searchFederatedDomainNotAvailableLearnMore": "Learn more", "searchGroupConversations": "Search group conversations", - "searchGroups": "Groups", + "searchGroupParticipants": "Group participants", + "searchConversationNames": "Conversation names", "searchInvite": "Invite people to join {{brandName}}", "searchInviteButtonContacts": "From Contacts", "searchInviteDetail": "Sharing your contacts helps you connect with others. We anonymize all the information and do not share it with anyone else.", @@ -1400,6 +1414,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,15 +1536,28 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", + "videoCallScreenShareEnded": "Your screen share has ended.", + "videoCallScreenShareEndConfirm": "End screen share", + "videoCallScreenShareEndConfirmDescription": "If you minimize the window, your screen share will end.", "videoCallaudioInputMicrophone": "Microphone", "videoCallaudioOutputSpeaker": "Speaker", "videoCallbackgroundBlur": "Blur my background", @@ -1538,6 +1566,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", @@ -1561,5 +1590,8 @@ "wireLinux": "{{brandName}} for Linux", "wireMacos": "{{brandName}} for macOS", "wireWindows": "{{brandName}} for Windows", - "wire_for_web": "{{brandName}} for Web" -} + "wire_for_web": "{{brandName}} for Web", + "teamUpgradeBannerHeader": "Enjoy benefits of a team", + "teamUpgradeBannerContent": "Explore extra features for free with the same level of security.", + "teamUpgradeBannerButtonText": "Create Wire Team" +} \ No newline at end of file diff --git a/src/i18n/es-ES.json b/src/i18n/es-ES.json index 686dee058f5..273c4b0251e 100644 --- a/src/i18n/es-ES.json +++ b/src/i18n/es-ES.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} está llamando", "callStateOutgoing": "Sonando…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Invitado", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Únase a la conversación", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": " y ", "ephemeralRemaining": "restantes", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Aceptar", - "modalImproveWireHeadline": "Ayúdenos a mejorar {{brandName}}", - "modalImproveWireMessage": "El envío anónimo de informes de uso y fallos nos ayuda a mejorar nuestros productos y servicios. No usamos esta información para nada más.", - "modalImproveWireSecondary": "No ahora", "modalIntegrationUnavailableHeadline": "Bots no disponibles por el momento", "modalIntegrationUnavailableMessage": "Gracias por su interés en los bots. El servicio está suspendido mientras trabajamos en la próxima versión.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Crear un equipo", "preferencesAccountData": "Permisos de uso de datos", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Eliminar cuenta", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Abrir conversación", "searchServicePlaceholder": "Buscar por nombre", "searchServices": "Servicios", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Miembros del equipo", "searchTopPeople": "Personas más importantes", "searchTrySearch": "Encontrar personas por nombre o usuario", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Reenviar código", "verify.subhead": "Introduzca el código de verificación que enviamos a{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Colgar", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Compartir pantalla", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video pausado", "videoCallScreenShareNotSupported": "Este navegador no permite compartir pantallas", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Esta versión de {{brandName}} no puede participar en la llamada. Por favor, usa", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} está llamando. Tu navegador no está configurada para llamadas.", diff --git a/src/i18n/et-EE.json b/src/i18n/et-EE.json index 0d013cd193a..28a782b54b3 100644 --- a/src/i18n/et-EE.json +++ b/src/i18n/et-EE.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} helistab", "callStateOutgoing": "Heliseb…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Külaline", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Ühine vestlusega", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": " ja ", "ephemeralRemaining": "on jäänud", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Nõustu", - "modalImproveWireHeadline": "Aita meil {{brandName}} paremaks muuta", - "modalImproveWireMessage": "Nõustun, et {{brandName}} võib luua ja kasutada anonüümseid kasutus- ja veateateid, parandamaks {{brandName}}’i rakendust. Ma saan selle nõusoleku igal ajal tühistada.", - "modalImproveWireSecondary": "Ei", "modalIntegrationUnavailableHeadline": "Vestlusrobotid pole hetkel saadaval", "modalIntegrationUnavailableMessage": "Täname vestlusrobotite vastu huvi tundmise eest. See teenus on hetkel suletud, kuni me töötame järgmise versiooni kallal. Hoia end kursis.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Loo meeskond", "preferencesAccountData": "Andmekasutuse õigused", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Kustuta konto", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "Kui see on sees, saavad inimesed näha, kas nende vestluse sõnumid on loetud.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Lisa teenus", "searchServicePlaceholder": "Otsi nime järgi", "searchServices": "Teenused", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Meeskonnaliikmed", "searchTopPeople": "Lemmikinimesed", "searchTrySearch": "Leia inimesi\nnime või kasutajanime järgi", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Saada kood uuesti", "verify.subhead": "Sisesta kinnituskood, mille saatsime aadressile{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Lõpeta kõne", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Jaga ekraani", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video pausil", "videoCallScreenShareNotSupported": "Sinu brauser ei toeta ekraanijagamist", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "See {{brandName}}’i versioon ei saa kõnes osaleda. Palun kasuta", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} helistab. Sinu brauser ei toeta kõnesid.", diff --git a/src/i18n/fa-IR.json b/src/i18n/fa-IR.json index 2b0efb516f9..1f05490107c 100644 --- a/src/i18n/fa-IR.json +++ b/src/i18n/fa-IR.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "در‌حال تماس…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "مهمان", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "میپذیرم", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "خیر", "modalIntegrationUnavailableHeadline": "ربات ها در حال حاظر غیر قابل دسترس اند", "modalIntegrationUnavailableMessage": "از شما برای علاقه مندی به ربات ها سپاس گذاریم. این سرویس در حال حاظر تعلیق شده تا زمانیکه ما بر روی نسخه بعدی کار کنیم. به گوش باشید.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "یک تیم ایجاد کنید", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "حذف حساب کاربری", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "جستجو بر اساس نام", "searchServices": "سرویس‌ها", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "اعضای تیم", "searchTopPeople": "دوستان محبوب", "searchTrySearch": "پیدا کردن افراد با نام و یا نام‌کاربری", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "قطع کنید", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "با این نسخه از وایر نمی‌توانید تماس بگیرید، لطفا استفاده کنید از ", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} در حال تماس است. مرورگر شما از تماس ها پشتیبانی نمی کند.", diff --git a/src/i18n/fi-FI.json b/src/i18n/fi-FI.json index ea03cab63e7..5f300e7e185 100644 --- a/src/i18n/fi-FI.json +++ b/src/i18n/fi-FI.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} soittaa", "callStateOutgoing": "Soi…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Vieras", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Hyväksy", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Ei", "modalIntegrationUnavailableHeadline": "Botit eivät käytettävissä", "modalIntegrationUnavailableMessage": "Kiitos mielenkiinnostasi botteihin. Tämä palvelu on väliaikaisesti pois käytöstä, koska työstämme seuraavaa versiota.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Luo tiimi", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Poista tili", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Hae nimellä", "searchServices": "Palvelut", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Tiimin jäsenet", "searchTopPeople": "Eniten käytetyt kontaktit", "searchTrySearch": "Etsi käyttäjiä nimellä tai käyttäjänimellä", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Katkaise puhelu", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Tämä versio Wirestä ei pysty osallistumaan puheluun. Käytä", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} soittaa. Selaimesi ei tue puheluja.", diff --git a/src/i18n/fr-FR.json b/src/i18n/fr-FR.json index 95303692fdd..0a8345d672b 100644 --- a/src/i18n/fr-FR.json +++ b/src/i18n/fr-FR.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} appelle", "callStateOutgoing": "Sonnerie…", "callWasEndedBecause": "Votre appel a été terminé parce que", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Contact externe", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Rejoignez la conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Ne saisissez vos identifiants que si vous êtes sûr d'être connecté à votre organisation.", "customEnvRedirect.redirectHeadline": "Redirection...", "customEnvRedirect.redirectTo": "Vous allez être redirigé vers votre service entreprise dédié.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", et ", "ephemeralRemaining": "restant", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Accepter", - "modalImproveWireHeadline": "Aidez-nous à améliorer {{brandName}}", - "modalImproveWireMessage": "{{brandName}} est autorisé à créer et utiliser des rapports d’utilisation et d’erreurs anonymes afin d’améliorer l’application. Vous pouvez révoquer cette autorisation à tout moment.", - "modalImproveWireSecondary": "Non", "modalIntegrationUnavailableHeadline": "Les bots sont indisponibles pour le moment", "modalIntegrationUnavailableMessage": "Merci de votre intérêt pour les bots. Ce service est actuellement désactivé pendant que nous travaillons sur la prochaine version. Restez à l’écoute.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Créer une équipe", "preferencesAccountData": "Utilisation des Données Personnelles", - "preferencesAccountDataCheckbox": "Envoyer des rapports d’erreur anonymes", - "preferencesAccountDataDetail": "Vous pouvez nous aider à améliorer {{brandName}} en envoyant des rapports d’erreur et des données anonymes sur votre utilisation.", "preferencesAccountDataTelemetry": "Les données d'utilisation permettent à {{brandName}} de comprendre comment l'application est utilisée et comment elle peut être améliorée. Les données sont anonymes et n'incluent pas le contenu de vos communications (messages, fichiers ou appels).", "preferencesAccountDataTelemetryCheckbox": "Envoyer des données d'utilisation anonymes", "preferencesAccountDelete": "Supprimer le compte", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "Quand cette option est activée, les participants peuvent voir quand leurs messages ont été lus dans cette conversation.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Ajouter un service", "searchServicePlaceholder": "Rechercher par nom", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Membres de l’équipe", "searchTopPeople": "Contacts favoris", "searchTrySearch": "Trouver des contacts par\nnom ou identifiant", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Renvoyer le code", "verify.subhead": "Entrez le code de vérification que nous avons envoyé à{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Raccrocher", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Partager l’Écran", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Vidéo en pause", "videoCallScreenShareNotSupported": "Le partage d’écran n’est pas compatible avec ce navigateur", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Cette version de {{brandName}} ne peut pas participer à cet appel. Utilisez plutôt", "warningCallQualityPoor": "Mauvaise connexion", "warningCallUnsupportedIncoming": "{{user}} vous appelle. Votre navigateur ne prend pas en charge les appels.", diff --git a/src/i18n/ga-IE.json b/src/i18n/ga-IE.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/ga-IE.json +++ b/src/i18n/ga-IE.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/he-IL.json b/src/i18n/he-IL.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/he-IL.json +++ b/src/i18n/he-IL.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/hi-IN.json b/src/i18n/hi-IN.json index 146b8500029..1af006bc38f 100644 --- a/src/i18n/hi-IN.json +++ b/src/i18n/hi-IN.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "मेहमान", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "स्वीकार करें", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "बॉट फिलहाल अनुपलब्ध", "modalIntegrationUnavailableMessage": "बॉट में आपकी रुचि के लिए शुक्रिया। सेवा फिलहाल निलंबित है जब तक हम अगले संस्करण पर काम कर रहे हैं। देखते रहें।", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "खाते को हटाएं", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/hr-HR.json b/src/i18n/hr-HR.json index 2f64ecde9a5..d675023ee09 100644 --- a/src/i18n/hr-HR.json +++ b/src/i18n/hr-HR.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} zove", "callStateOutgoing": "Zvoni...", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Gost", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", i ", "ephemeralRemaining": "preostalo", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Prihvati", - "modalImproveWireHeadline": "Pomozite nam učiniti {{brandName}} boljim", - "modalImproveWireMessage": "Slažem se da {{brandName}} može stvoriti i upotrijebiti anonimne izvještaje o uporabi i greškama, kako bi unaprijedio {{brandName}} aplikaciju. Možete promijeniti ovo u bilo koje vrijeme.", - "modalImproveWireSecondary": "Ne", "modalIntegrationUnavailableHeadline": "Botovi trenutno nedostupni", "modalIntegrationUnavailableMessage": "Hvala za tvoj interes oko botova. Usluga je trenutno suspendirana dok se radi na idućoj verziji. Pratite nas.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Stvorite tim", "preferencesAccountData": "Dozvole za upotrebu podataka", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Brisanje računa", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "Kada uključeno, osobe mogu vidjeti kada su njihove poruke pročitane u ovom razgovoru.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Otvori razgovor", "searchServicePlaceholder": "Traži po imenu", "searchServices": "Usluge", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Članovi tima", "searchTopPeople": "Najčešće kontaktirani", "searchTrySearch": "Traženje ljudi po imenu ili korisničkom imenu", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Ponovno pošalji kod", "verify.subhead": "Unesite kontrolni kod koji smo Vam poslali na{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Poklopi", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Dijeljenje ekrana", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video pauziran", "videoCallScreenShareNotSupported": "Dijeljenje ekrana nije podržano u ovom pregledniku", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Ova verzija {{brandName}} nema pozive. Molimo vas da koristite", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} zove. Vaš preglednik ne podržava pozive.", diff --git a/src/i18n/hu-HU.json b/src/i18n/hu-HU.json index 33270613e33..d54107c3097 100644 --- a/src/i18n/hu-HU.json +++ b/src/i18n/hu-HU.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} hív", "callStateOutgoing": "Kicsengés…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Vendég", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Csatlakozz a beszélgetéshez", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": "és ", "ephemeralRemaining": "fennmaradó", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Elfogadás", - "modalImproveWireHeadline": "Segíts nekünk jobbá tenni a {{brandName}}-t", - "modalImproveWireMessage": "Segíts jobbá tenni termékeinket és szolgáltatásainkat azzal, hogy névtelen használati és hibajelentéseket küldesz. Ezeket az információkat semmi másra nem használjuk.", - "modalImproveWireSecondary": "Most nem", "modalIntegrationUnavailableHeadline": "A botok jelenleg nem elérhetőek", "modalIntegrationUnavailableMessage": "Köszönjük, hogy érdeklődsz a botokkal kapcsolatban. A szolgáltatást jelenleg felfüggesztettük, amíg a következő verzión dolgozunk. Hamarosan jelentkezünk.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Csapat létrehozása", "preferencesAccountData": "Adatokhasználati engedélyek", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Fiók törlése", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "Amikor ez be van kapcsolva, az emberek láthatják, mikor olvasták el a beszélgetés üzeneteit.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Új szolgáltatás", "searchServicePlaceholder": "Keresés név szerint", "searchServices": "Szolgáltatások", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Csapattagok", "searchTopPeople": "Top Partnerek", "searchTrySearch": "Partnerek keresése\nnév vagy felhasználónév alapján", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Kód újraküldése", "verify.subhead": "Írd be az ellenőrző kódot, amit a {newline}{email} címre küldtünk", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hívás befejezése", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Képernyő megosztása", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Videó szüneteltetve", "videoCallScreenShareNotSupported": "A képernyőmegosztást ez a böngésző nem támogatja", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Ezzel a {{brandName}} verzióval nem tudsz részt venni a hívásban. Kérjük, használd ezt:", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} hív. Böngésződ nem támogatja a hanghívásokat.", diff --git a/src/i18n/id-ID.json b/src/i18n/id-ID.json index 885db025e70..1d1cb389cb8 100644 --- a/src/i18n/id-ID.json +++ b/src/i18n/id-ID.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Berdering…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Tamu", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Terima", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Tidak", "modalIntegrationUnavailableHeadline": "Bot saat ini tidak tersedia", "modalIntegrationUnavailableMessage": "Terima kasih atas ketertarikan Anda pada bot. Layanan saat ini ditangguhkan saat kami mengerjakan versi berikutnya. Tetap disini.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Buat tim", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Hapus akun", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Cari berdasarkan nama", "searchServices": "Layanan", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Anggota tim", "searchTopPeople": "Orang Top", "searchTrySearch": "Temukan orang berdasarkan nama atau nama pengguna", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Tutup", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Versi {{brandName}} ini tidak dapat berpartisipasi dalam panggilan. Silakan gunakan", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} sedang menelepon Browser Anda tidak mendukung panggilan.", diff --git a/src/i18n/is-IS.json b/src/i18n/is-IS.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/is-IS.json +++ b/src/i18n/is-IS.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/it-IT.json b/src/i18n/it-IT.json index 322efa5620b..af9c039a01a 100644 --- a/src/i18n/it-IT.json +++ b/src/i18n/it-IT.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} sta chiamando", "callStateOutgoing": "Sta squillando…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Ospite", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", e ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Accetta", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Crea un team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Elimina account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Cerca per nome", "searchServices": "Servizi", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Membri del team", "searchTopPeople": "Contattati frequenti", "searchTrySearch": "Trova le persone per nome o username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Riattacca", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Condividi schermo", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video in pausa", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Questa versione di {{brandName}} non può partecipare alla chiamata. Per favore usa", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} sta chiamando. Il tuo browser non supporta le chiamate.", diff --git a/src/i18n/ja-JP.json b/src/i18n/ja-JP.json index d993ffec758..6041ed141fd 100644 --- a/src/i18n/ja-JP.json +++ b/src/i18n/ja-JP.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} が呼び出し中", "callStateOutgoing": "呼び出し中...", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "ゲスト", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "会話に参加します", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": "および ", "ephemeralRemaining": "残り", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "承諾", - "modalImproveWireHeadline": "{{brandName}} の改善にご協力を", - "modalImproveWireMessage": "匿名の使用状況とクラッシュレポートを送信することにより、当社の製品およびサービスの改善に役立ちます。\n我々はそれ以外の目的のためにこれらの情報を使うことはありません。", - "modalImproveWireSecondary": "後で", "modalIntegrationUnavailableHeadline": " Bot は現在利用できません。", "modalIntegrationUnavailableMessage": "ボットにご関心いただきありがとうございます。次のバージョンまで提供は中断しています。ご期待ください。", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "新しいチームを作成", "preferencesAccountData": "データ利用許可", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "アカウントを削除", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "この設定がオンのとき、この会話内のメッセージが読まれたかどうか確認できます。", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "サービスを追加", "searchServicePlaceholder": "名前で検索する", "searchServices": "サービス", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "チームメンバー", "searchTopPeople": "トップピープル", "searchTrySearch": "名前またはユーザー名で人を見つける", @@ -1521,12 +1530,22 @@ "verify.resendCode": "コードを再送する", "verify.subhead": "以下のメールアドレスに送信された認証コードを入力してください。{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "電話を切る", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "画面共有", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "ビデオ停止中", "videoCallScreenShareNotSupported": "このブラウザでは画面共有はサポートされません。", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "このバージョンの{{brandName}}は電話に参加できません。使用してください", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} が呼び出し中。お使いのブラウザは電話をサポートしていません。", diff --git a/src/i18n/lt-LT.json b/src/i18n/lt-LT.json index d688456b642..cb15153cf65 100644 --- a/src/i18n/lt-LT.json +++ b/src/i18n/lt-LT.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} jums skambina", "callStateOutgoing": "Kviečiama…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Svečias", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Prisijungti prie susirašinėjimo", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", ir ", "ephemeralRemaining": "liko", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Priimti", - "modalImproveWireHeadline": "Pagerinkite „{{brandName}}“", - "modalImproveWireMessage": "Siųsdami anoniminę naudojimosi ir trikčių ataskaitą padedate mums pagerinti produktus ir paslaugas. Šios informacijos daugiau niekur nenaudojame.", - "modalImproveWireSecondary": "Ne dabar", "modalIntegrationUnavailableHeadline": "Šiuo metu robotai negalimi", "modalIntegrationUnavailableMessage": "Dėkojame, kad domitės robotais. Paslauga šiuo metu yra pristabdyta tol, kol mes ruošiame kitą versiją. Sekite naujienas.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Sukurti komandą", "preferencesAccountData": "Duomenų naudojimo leidimas", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Ištrinti paskyrą", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "Tai įjungus, žmonės šiame pokalbyje galės matyti kai jų žinutės yra perskaitomos.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Pridėti paslaugą", "searchServicePlaceholder": "Ieškokite pagal vardą", "searchServices": "Paslaugos", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Komandos dalyviai", "searchTopPeople": "Svarbiausi žmonės", "searchTrySearch": "Raskite žmones pagal vardą arba naudotojo vardą", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Siųsti kodą dar kartą", "verify.subhead": "Įrašykite patvirtinimo kodą, kuris buvo išsiųstas į
{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Užbaigti", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Siųsti ekrano vaizdą", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Vaizdas pristabdytas", "videoCallScreenShareNotSupported": "Ši naršykle nepalaiko ekrano rodymo", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Ši „{{brandName}}“ versija negali dalyvauti pokalbyje. Naudokite", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "Skambina {{user}}. Jūsų naršyklė nepalaiko skambučių.", diff --git a/src/i18n/lv-LV.json b/src/i18n/lv-LV.json index 7f9485dbf5a..678dc510db7 100644 --- a/src/i18n/lv-LV.json +++ b/src/i18n/lv-LV.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Zvana…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Pieņemt", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Nē", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Dzēst kontu", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Pakalpojumi", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Aktīvākie kontakti", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Beigt Zvanu", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/ms-MY.json b/src/i18n/ms-MY.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/ms-MY.json +++ b/src/i18n/ms-MY.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/nl-NL.json b/src/i18n/nl-NL.json index d426ed6b03d..1438e3bd53c 100644 --- a/src/i18n/nl-NL.json +++ b/src/i18n/nl-NL.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Bellen…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Gast", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", en ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Neem op", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Nee", "modalIntegrationUnavailableHeadline": "Bots zijn nu niet beschikbaar", "modalIntegrationUnavailableMessage": "Dank je voor je intress voor bots. De service is nu gestopt, terwijl we nu werken aan een volgende versie.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Maak een team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Verwijder account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Service toevoegen", "searchServicePlaceholder": "Zoeken op naam", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Teamleden", "searchTopPeople": "Frequente personen", "searchTrySearch": "Vind mensen met hun \nnaam of gebruikersnaam", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Code opnieuw verzenden", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Ophangen", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Deze versie kan niet deelnemen met het bellen. Gebruik alsjeblieft", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} belt, maar je browser ondersteund geen gesprekken.", diff --git a/src/i18n/no-NO.json b/src/i18n/no-NO.json index 3440b6dd6f3..4ab0e7caae3 100644 --- a/src/i18n/no-NO.json +++ b/src/i18n/no-NO.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} ringer", "callStateOutgoing": "Ringer …", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Gjest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", og ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Godta", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Nei", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Slett konto", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Tjenester", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Legg på", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Del skjerm", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video pauset", "videoCallScreenShareNotSupported": "Skjermdeling støttes ikke i denne nettleseren", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/pl-PL.json b/src/i18n/pl-PL.json index f9347263230..d8fb5160eb7 100644 --- a/src/i18n/pl-PL.json +++ b/src/i18n/pl-PL.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Dzwoni…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Gość", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", i ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Odbierz", - "modalImproveWireHeadline": "Pomóż nam ulepszyć {{brandName}}", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Nie teraz", "modalIntegrationUnavailableHeadline": "Boty są obecnie niedostępne", "modalIntegrationUnavailableMessage": "Dziękujemy za zainteresowanie naszymi botami. Usługa jest obecnie zawieszona, ponieważ pracujemy nad następną wersją. Bądź na bieżąco.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Utwórz nowy zespół", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Usuń konto", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Szukaj według nazwy", "searchServices": "Usługi", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Członkowie zespołu", "searchTopPeople": "Osoby", "searchTrySearch": "Znajdź osoby według nazwy lub nazwy użytkownika", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Ponownie wyślij kod", "verify.subhead": "Wpisz kod weryfikacyjny, który wysłaliśmy na adres{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Rozłącz", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Ta wersja {{brandName}} nie może brać udziału w rozmowie. Proszę użyj", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "Dzwoni {{user}}. Twoja przeglądarka nie obsługuje rozmów.", diff --git a/src/i18n/pt-BR.json b/src/i18n/pt-BR.json index e9ecf227752..d52c321d11f 100644 --- a/src/i18n/pt-BR.json +++ b/src/i18n/pt-BR.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} está chamando", "callStateOutgoing": "Tocando…", "callWasEndedBecause": "Sua chamada foi encerrada porque", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Sua equipe está atualmente no plano Básico gratuito. Atualize para o plano Empresarial para ter acesso a recursos como o início de conferências e muito mais. [link]Saiba mais sobre o {{brandName}} Empresarial[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Atualizar para o plano Empresarial", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Atualizar agora", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Convidado", "conversationImageAssetRestricted": "Receber imagens é proibido", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Entrar na conversa", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Forneça credenciais apenas se tiver certeza de que este é o login da sua organização.", "customEnvRedirect.redirectHeadline": "Redirecionando...", "customEnvRedirect.redirectTo": "Você está sendo redirecionado para seu serviço empresarial dedicado.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", e ", "ephemeralRemaining": "restantes", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Eu concordo", - "modalImproveWireHeadline": "Ajude-nos a tornar o {{brandName}} melhor", - "modalImproveWireMessage": "Eu concordo que o {{brandName}} pode criar e usar relatórios anônimos de uso e erros para melhorar o aplicativo. Posso revogar esse consentimento a qualquer momento.", - "modalImproveWireSecondary": "Não", "modalIntegrationUnavailableHeadline": "Bots atualmente indisponíveis", "modalIntegrationUnavailableMessage": "Obrigado pelo seu interesse nos bots. O serviço está suspenso enquanto trabalhamos na próxima versão. Fique ligado.", "modalLegalHoldConversationMissingConsentMessage": "Devido à Retenção Legal, apenas membros da equipe podem ser adicionados a esta conversa. [link]Saiba mais[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copiar link do perfil", "preferencesAccountCreateTeam": "Criar uma equipe", "preferencesAccountData": "Permissões de uso de dados", - "preferencesAccountDataCheckbox": "Envie relatórios anônimos de falhas", - "preferencesAccountDataDetail": "Envie relatórios anônimos de falhas e dados básicos como número da versão e sistema operacional para ajudar o {{brandName}} a identificar e resolver problemas no aplicativo.", "preferencesAccountDataTelemetry": "Os dados de uso permitem que o {{brandName}} entenda como o aplicativo está sendo usado e como pode ser melhorado. Os dados são anônimos e não incluem o conteúdo de suas comunicações (como mensagens, arquivos ou chamadas).", "preferencesAccountDataTelemetryCheckbox": "Enviar dados de uso anônimos", "preferencesAccountDelete": "Excluir conta", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "Quando ativado, as pessoas podem ver quando suas mensagens nesta conversa são lidas.", "readReceiptsToggleName": "Confirmações de leitura", "receiptToggleInfo": "Quando isso está ativado, as pessoas podem ver quando suas mensagens nessa conversa são lidas.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Abrir conversa", "searchServicePlaceholder": "Pesquisar por nome", "searchServices": "Serviços", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Membros da equipe", "searchTopPeople": "Contatos frequentes", "searchTrySearch": "Encontre pessoas pelo \nnome ou nome de usuário", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Reenviar código", "verify.subhead": "Digite o código de verificação que enviamos para{newline}{email}", "videoCallOverlayCamera": "Câmera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversas", "videoCallOverlayFitVideoLabel": "Clique duas vezes para ver em tela cheia", "videoCallOverlayFitVideoLabelGoBack": "Clique duas vezes para mostrar todos os participantes", "videoCallOverlayHangUp": "Encerrar", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microfone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Compartilhar tela", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Conectando...", "videoCallPaused": "Vídeo pausado", "videoCallScreenShareNotSupported": "Seu navegador não oferece suporte a compartilhamento de tela", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "Todos ({{count}})", "videoSpeakersTabSpeakers": "Alto-falantes", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Esta versão do {{brandName}} não pode participar da chamada. Por favor, use", "warningCallQualityPoor": "Conexão lenta", "warningCallUnsupportedIncoming": "{{user}} está chamando. Seu navegador não oferece suporte a chamadas.", diff --git a/src/i18n/pt-PT.json b/src/i18n/pt-PT.json index 9d2f34759d9..f52d1b30798 100644 --- a/src/i18n/pt-PT.json +++ b/src/i18n/pt-PT.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "A tocar…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Convidado", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", e ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Aceitar", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Não", "modalIntegrationUnavailableHeadline": "Atualmente os \"bots\" não estão disponíveis", "modalIntegrationUnavailableMessage": "Obrigado pelo seu interesse nos \"bots\". Atualmente o serviço está suspenso enquanto preparamos a nova versão. Fique atento.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Criar uma equipa", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Eliminar conta", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Pesquisar por nome", "searchServices": "Serviços", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Membros da equipa", "searchTopPeople": "Pessoas mais contactadas", "searchTrySearch": "Encontrar pessoas pelo nome ou nome de utilizador", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Desligar", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Esta versão do {{brandName}} não pode participar na chamada. Por favor, use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} está a chamar. O seu navegador não suporta chamadas.", diff --git a/src/i18n/ro-RO.json b/src/i18n/ro-RO.json index 73c4f979a45..0f3a946ceef 100644 --- a/src/i18n/ro-RO.json +++ b/src/i18n/ro-RO.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Se sună…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Vizitator", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", și ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Acceptă", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Nu", "modalIntegrationUnavailableHeadline": "Roboții nu sunt momentan disponibili", "modalIntegrationUnavailableMessage": "Mulțumim că ești interesat de roboți. Acest serviciu este momentan suspendat în timp ce lucrăm la următoarea versiune. Fii pe fază.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Creează o echipă", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Șterge contul", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Adaugă serviciu", "searchServicePlaceholder": "Caută după nume", "searchServices": "Servicii", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Membrii echipei", "searchTopPeople": "Top persoane", "searchTrySearch": "Caută oameni după\nnume sau nume utilizator", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Închide", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Această versiune de {{brandName}} nu poate participa într-un apel. Te rugăm să folosești", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} te sună. Browserul tău nu suportă apelurile.", diff --git a/src/i18n/ru-RU.json b/src/i18n/ru-RU.json index 12aa1413726..e499821a80f 100644 --- a/src/i18n/ru-RU.json +++ b/src/i18n/ru-RU.json @@ -188,7 +188,7 @@ "acme.renewCertificate.button.secondary": "Напомнить позже", "acme.renewCertificate.gracePeriodOver.paragraph": "Срок действия сертификата сквозной идентификации для этого устройства истек. Чтобы обеспечить максимальный уровень безопасности связи Wire, обновите сертификат.

На следующем шаге введите учетные данные поставщика идентификационных данных, чтобы автоматически обновить сертификат.

Подробнее о сквозной идентификации ", "acme.renewCertificate.headline.alt": "Обновление сертификата сквозной идентификации", - "acme.renewCertificate.paragraph": "Срок действия сертификата сквозной идентификации для этого устройства истекает в ближайшее время. Чтобы обеспечить максимальный уровень безопасности ваших коммуникаций, обновите сертификат прямо сейчас.

На следующем шаге введите учетные данные поставщика идентификационных данных, чтобы автоматически обновить сертификат.

Подробнее о сквозной идентификации ", + "acme.renewCertificate.paragraph": "Срок действия сертификата сквозной идентификации для этого устройства истекает в ближайшее время. Чтобы поддерживать связь на самом высоком уровне безопасности, обновите сертификат прямо сейчас.

На следующем шаге введите учетные данные поставщика идентификационных данных, чтобы автоматически обновить сертификат.

Узнать больше о сквозной идентификации ", "acme.renewal.done.headline": "Сертификат обновлен", "acme.renewal.done.paragraph": "Теперь сертификат обновлен и ваше устройство верифицировано. Более подробную информацию об этом сертификате можно найти в настройках вашего [bold]Wire[/bold] под [bold]Устройствами.[/bold]

Узнайте больше о сквозной идентификации ", "acme.renewal.inProgress.headline": "Обновление сертификата...", @@ -324,7 +324,7 @@ "callParticipants": "{{number}} участника(-ов)", "callReactionButtonAriaLabel": "Выбрать смайлик {{emoji}}", "callReactionButtonsAriaLabel": "Панель выбора смайликов", - "callReactions": "Reactions", + "callReactions": "Реакции", "callReactionsAriaLabel": "Смайлик {{emoji}} из {{from}}", "callStateCbr": "Постоянный битрейт", "callStateConnecting": "Подключение…", @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} вызывает", "callStateOutgoing": "Вызываем…", "callWasEndedBecause": "Ваш звонок был завершен, поскольку", + "callingPopOutWindowTitle": "Звонок {{brandName}}", "callingRestrictedConferenceCallOwnerModalDescription": "В настоящее время ваша команда использует бесплатный тарифный план Basic. Перейдите на тарифный план Enterprise, чтобы получить доступ к таким возможностям, как проведение конференций и многим другим. [link]Узнать больше об {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Перейти на Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Перейти сейчас", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "Вы пока не участвуете ни в одной групповой беседе.", "conversationGuestIndicator": "Гость", "conversationImageAssetRestricted": "Получение изображений запрещено", + "conversationInternalEnvironmentDisclaimer": "Это НЕ WIRE, а внутренняя среда тестирования. Разрешено использовать только сотрудникам Wire. Любое публичное использование ЗАПРЕЩЕНО. Данные пользователей этой тестовой среды тщательно записываются и анализируются. Чтобы воспользоваться защищенным мессенджером Wire, перейдите по ссылке [link]{{url}}[/link].", "conversationJoin.existentAccountJoinInBrowser": "Войти в браузере", "conversationJoin.existentAccountJoinWithoutLink": "Присоединиться к беседе", "conversationJoin.existentAccountUserName": "Вы вошли как {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Указывайте учетные данные, только если уверены, что это страница авторизации вашей организации.", "customEnvRedirect.redirectHeadline": "Перенаправление...", "customEnvRedirect.redirectTo": "Вы будете перенаправлены на специализированное обслуживание вашей организации.", + "dataSharingModalAgree": "Принять", + "dataSharingModalDecline": "Отклонить", + "dataSharingModalDescription": "Помогите улучшить Wire, поделившись данными о своем использовании с помощью псевдонимного идентификатора. Эти данные не связаны с вашей личной информацией и не передаются третьим лицам, помимо Wire Group. К ним относятся, например, время использования функции, версия вашего приложения, тип устройства или операционная система. Эти данные будут удалены не позднее чем через 365 дней.
Более подробную информацию вы найдете в нашей [link]Политике конфиденциальности[/link]. Вы можете отозвать свое согласие в любое время.", + "dataSharingModalTitle": "Согласие на передачу пользовательских данных", "downloadLatestMLS": "Загрузить последнюю версию MLS Wire", "enumerationAnd": ", и ", "ephemeralRemaining": "осталось", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Используйте как минимум {{minPasswordLength}} символов, включая одну строчную букву, одну заглавную букву, цифру и специальный символ.", "modalGuestLinkJoinLabel": "Установить пароль", "modalGuestLinkJoinPlaceholder": "Введите пароль", - "modalImproveWireAction": "Принять", - "modalImproveWireHeadline": "Помогите нам сделать {{brandName}} лучше", - "modalImproveWireMessage": "Я согласен с тем, что {{brandName}} может создавать и использовать анонимные отчеты об использовании и ошибках для улучшения приложения {{brandName}}. Я могу отозвать свое согласие в любое время.", - "modalImproveWireSecondary": "Нет", "modalIntegrationUnavailableHeadline": "Боты в настоящее время недоступны", "modalIntegrationUnavailableMessage": "Благодарим вас за интерес к ботам. В настоящее время сервис приостановлен, пока мы работаем над следующей версией. Следите за обновлениями.", "modalLegalHoldConversationMissingConsentMessage": "Из-за юридических ограничений в эту беседу могут быть добавлены только члены команды. [link]Подробнее[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Скопировать ссылку на профиль", "preferencesAccountCreateTeam": "Создать команду", "preferencesAccountData": "Разрешения на использование данных", - "preferencesAccountDataCheckbox": "Отправлять анонимные отчеты о падениях", - "preferencesAccountDataDetail": "Отправлять анонимные сообщения о сбоях и базовые данные, такие как номер версии и операционная система, чтобы помочь {{brandName}} идентифицировать и решить проблемы с приложением.", "preferencesAccountDataTelemetry": "Данные об использовании позволяют {{brandName}} определить, как используется приложение и как его можно улучшить. Эти данные анонимны и не включают в себя содержимое ваших сообщений (например, сообщения, файлы или звонки).", "preferencesAccountDataTelemetryCheckbox": "Отправка анонимных данных об использовании", "preferencesAccountDelete": "Удалить аккаунт", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Не верифицирован", "proteusVerified": "Верифицирован", "proteusVerifiedDetails": "Верифицировано (Proteus)", + "qualityFeedback.bad": "Ужасно", + "qualityFeedback.description": "Как вы оцениваете общее качество звонка?", + "qualityFeedback.doNotAskAgain": "Больше не спрашивать", + "qualityFeedback.excellent": "Прекрасно", + "qualityFeedback.fair": "Приемлемо", + "qualityFeedback.heading": "Отзыв о качестве звонка", + "qualityFeedback.notificationSubmitted": "Ваш отзыв отправлен. Спасибо!", + "qualityFeedback.skip": "Пропустить", "readReceiptsToggleInfo": "При включении ваши собеседники смогут видеть когда было прочитано их сообщение.", "readReceiptsToggleName": "Отчеты о прочтении", "receiptToggleInfo": "При включении ваши собеседники смогут видеть когда было прочитано их сообщение.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Добавить сервис", "searchServicePlaceholder": "Поиск по имени", "searchServices": "Сервисы", + "searchTeamGroups": "Беседы команды", "searchTeamMembers": "Участники команды", "searchTopPeople": "Топ-контакты", "searchTrySearch": "Ищите пользователей по\nимени или псевдониму", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Запросить код повторно", "verify.subhead": "Введите код подтверждения, который мы отправили на {newline}{email}", "videoCallOverlayCamera": "Камера", + "videoCallOverlayChangeViewMode": "Изменить режим просмотра", + "videoCallOverlayCloseFullScreen": "Вернуться к свернутому виду", "videoCallOverlayConversations": "Беседы", "videoCallOverlayFitVideoLabel": "Дважды щелкните для просмотра во весь экран", "videoCallOverlayFitVideoLabelGoBack": "Дважды щелкните, чтобы показать всех участников", "videoCallOverlayHangUp": "Завершить вызов", + "videoCallOverlayHideParticipantsList": "Скрыть список участников", "videoCallOverlayMicrophone": "Микрофон", + "videoCallOverlayOpenFullScreen": "Открыть вызов в полноэкранном режиме", + "videoCallOverlayOpenPopupWindow": "Открыть в новом окне", + "videoCallOverlayParticipantsListLabel": "Участники ({{count}})", "videoCallOverlayShareScreen": "Поделиться экраном", + "videoCallOverlayShowParticipantsList": "Показать список участников", + "videoCallOverlayViewModeAll": "Показать всех участников", + "videoCallOverlayViewModeLabel": "Режим просмотра", + "videoCallOverlayViewModeSpeakers": "Показывать только активных спикеров", "videoCallParticipantConnecting": "Подключение...", "videoCallPaused": "Видео приостановлено", "videoCallScreenShareNotSupported": "Ваш браузер не поддерживает совместное использование экрана", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Камера", "videoSpeakersTabAll": "Все ({{count}})", "videoSpeakersTabSpeakers": "Динамики", + "viewingInAnotherWindow": "Просмотр в другом окне", "warningCallIssues": "Эта версия {{brandName}} не может участвовать в вызове. Пожалуйста, используйте", "warningCallQualityPoor": "Плохое подключение", "warningCallUnsupportedIncoming": "{{user}} вызывает. Ваш браузер не поддерживает вызовы.", diff --git a/src/i18n/si-LK.json b/src/i18n/si-LK.json index e26b93c2632..5159afdd963 100644 --- a/src/i18n/si-LK.json +++ b/src/i18n/si-LK.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} අමතමින්", "callStateOutgoing": "නාද වෙමින්…", "callWasEndedBecause": "ඔබගේ ඇමතුම අවසන් වුණි මන්ද", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "ඔබගේ කණ්ඩායම දැනට මූලික නොමිලේ සැලසුමෙහි සිටියි. සම්මන්ත්‍රණ ඇරඹීමට සහ වෙනත් විශේෂාංග සඳහා ප්‍රවේශයට ව්‍යවසාය වෙත උත්ශ්‍රේණි කරන්න. [link]{{brandName}} ව්‍යවසාය[/link] ගැන තව දැනගන්න", "callingRestrictedConferenceCallOwnerModalTitle": "ව්‍යවසාය වෙත උත්ශ්‍රේණිය", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "උත්ශ්‍රේණි කරන්න", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "අමුත්තා", "conversationImageAssetRestricted": "රූප ලැබීම වළක්වා ඇත", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "අතිරික්සුවෙන් එක්වන්න", "conversationJoin.existentAccountJoinWithoutLink": "සංවාදයට එක්වන්න", "conversationJoin.existentAccountUserName": "{selfName} ලෙස ඔබ ඇතුළු වී ඇත", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "මෙය ඔබගේ සංවිධානයේ පිවිසුම බව විශ්වාස නම් පමණක් අක්තපත්‍ර සපයන්න.", "customEnvRedirect.redirectHeadline": "හරවා යවමින්...", "customEnvRedirect.redirectTo": "ඔබගේ සමර්පිත ව්‍යවසාය සේවාව වෙත හරවා යවනු ලැබේ.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "නවතම වයර් MLS අනුවාදය බාගන්න", "enumerationAnd": ", සහ ", "ephemeralRemaining": "ඉතිරියි", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "කුඩා සහ ලොකු අකුරක් ද, අංකයක් ද, විශේෂ අකුරක් ද සහිතව අවම වශයෙන් අකුරු {{minPasswordLength}} ක් යොදා ගන්න.", "modalGuestLinkJoinLabel": "මුරපදය සකසන්න", "modalGuestLinkJoinPlaceholder": "මුරපදය යොදන්න", - "modalImproveWireAction": "මම එකඟයි", - "modalImproveWireHeadline": "{{brandName}} වැඩිදියුණු කිරීමට උදව් කරන්න", - "modalImproveWireMessage": "{{brandName}} යෙදුමේ වැඩිදියුණුව උදෙසා දෝෂ වාර්තා සෑදීමට සහ නිර්නාමික භාවිතය {{brandName}} වෙත ලබා දීමට එකඟ වෙමි. මට ඕනෑම වේලාවක මෙම කැමැත්ත අවලංගු කිරීමට හැකිය.", - "modalImproveWireSecondary": "නැහැ", "modalIntegrationUnavailableHeadline": "ස්වයං ක්‍රමලේඛ දැනට නැත", "modalIntegrationUnavailableMessage": "ස්වයං ක්‍රමලේඛ ගැන ඔබ දැක්වූ උනන්දුවට ස්තූතියි. අපි ඊළඟ අනුවාදයේ වැඩ කරන අතරතුර සේවාව දැනට අත්හිටුවා ඇත. රැඳී සිටින්න.", "modalLegalHoldConversationMissingConsentMessage": "නෛතික රැඳවුම නිසා මෙම සංවාදයට කණ්ඩායමේ සාමාජිකයින් පමණක් එකතු කිරීමට හැකිය. [link]තව දැනගන්න[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "පැතිකඩ සබැඳියේ පිටපතක්", "preferencesAccountCreateTeam": "කණ්ඩායමක් සාදන්න", "preferencesAccountData": "දත්ත භාවිත අවසර", - "preferencesAccountDataCheckbox": "නිර්නාමික බිඳ වැටීම් වාර්තා යවන්න", - "preferencesAccountDataDetail": "{{brandName}} ට යෙදුමේ ඇති ගැටලු හඳුනා ගැනීමට සහ විසඳීමට නිර්ණාමික බිඳ වැටීම් වාර්තා, අනුවාද අංකය සහ මෙහෙයුම් පද්ධතිය වැනි මූලික දත්ත යවයි.", "preferencesAccountDataTelemetry": "{{brandName}} ට යෙදුම භාවිතා කරන්නේ කෙසේද සහ එය වැඩි දියුණු කළ හැකි වන්නේ කෙසේද යන්න තේරුම් ගැනීමට භාවිත දත්ත ඉඩ සලසයි. දත්ත නිර්නාමික වන අතර ඔබගේ සන්නිවේදනයේ අන්තර්ගතය (පණිවිඩ, ගොනු හෝ ඇමතුම් වැනි) ඇතුළත් නොවේ.", "preferencesAccountDataTelemetryCheckbox": "නිර්නාමික භාවිත දත්ත යවන්න", "preferencesAccountDelete": "ගිණුම මකන්න", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "සත්‍යාපිත නොවේ", "proteusVerified": "සත්‍යාපිතයි", "proteusVerifiedDetails": "සත්‍යාපිතයි (ප්‍රෝටියස්)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "මෙය සක්‍රිය නම්, පුද්ගලයින්ට මෙම සංවාදය තුළ ඔවුන්ගේ පණිවිඩ කියවූ විට දැකගත හැකිය.", "readReceiptsToggleName": "කියවූ බවට ලදුපත්", "receiptToggleInfo": "මෙය සක්‍රිය විට, පුද්ගලයින්ට මෙම සංවාදය තුළ ඔවුන්ගේ පණිවිඩ කියවූ විට දැකගත හැකිය.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "සංවාදය අරින්න", "searchServicePlaceholder": "නමින් සොයන්න", "searchServices": "සේවා", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "කණ්ඩායමේ සාමාජිකයින්", "searchTopPeople": "ජනප්‍රිය පුද්ගලයින්", "searchTrySearch": "නමකින් හෝ පරිශ්‍රීලක නාමයෙන් \nපුද්ගලයින් සොයා ගන්න", @@ -1521,12 +1530,22 @@ "verify.resendCode": "කේතය නැවත යවන්න", "verify.subhead": "වි-තැපෑල වෙත එවූ සත්‍යාපන කේතය ඇතුල් කරන්න. {newline}{email} ", "videoCallOverlayCamera": "රූගතය", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "සංවාද", "videoCallOverlayFitVideoLabel": "පූර්ණ තිරය දැකීමට දෙවරක් ඔබන්න", "videoCallOverlayFitVideoLabelGoBack": "සියළුම සහභාගීන් දැකීමට දෙවරක් ඔබන්න", "videoCallOverlayHangUp": "තබන්න", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "ශබ්දවාහිනිය", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "තිරය බෙදාගන්න", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "සම්බන්ධ වෙමින්...", "videoCallPaused": "දෘශ්‍ය විරාමයකි", "videoCallScreenShareNotSupported": "තිරය බෙදාගැනීමට මෙම අතිරික්සුව සහය නොදක්වයි", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "රූගතය", "videoSpeakersTabAll": "සියල්ල ({{count}})", "videoSpeakersTabSpeakers": "විකාශක", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "මෙම {{brandName}} අනුවාදය භාවිතයෙන් ඇමතුමට සහභාගී වීමට නොහැකිය. මෙය භාවිතා කරන්න", "warningCallQualityPoor": "සම්බන්ධතාවය දුර්වලයි", "warningCallUnsupportedIncoming": "{{user}} අමතමින්. ඔබගේ අතිරික්සුව ඇමතුම් සඳහා සහාය නොදක්වයි.", diff --git a/src/i18n/sk-SK.json b/src/i18n/sk-SK.json index 39a2601f2bd..66d293b86c6 100644 --- a/src/i18n/sk-SK.json +++ b/src/i18n/sk-SK.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Zvoní…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Hosť", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Prijať", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Nie", "modalIntegrationUnavailableHeadline": "Boti sú momentálne nedostupní", "modalIntegrationUnavailableMessage": "Ďakujeme o Váš záujem o Botov. Služba je momentálne pozastavená kvôli práci na novej verzii.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Vytvoriť tím", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Vymazať účet", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Vyhľadať podľa mena", "searchServices": "Služby", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Členovia tímu", "searchTopPeople": "Top kontakty", "searchTrySearch": "Nájsť ľudí podľa názvu, alebo užívateľského mena", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Ukončiť", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Táto verzia {{brandName}} sa nemôže zúčastniť volania. Prosím použite", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "Volá {{user}}. Váš prehliadač nepodporuje hovory.", diff --git a/src/i18n/sl-SI.json b/src/i18n/sl-SI.json index 39a4e383b9c..74607992ec0 100644 --- a/src/i18n/sl-SI.json +++ b/src/i18n/sl-SI.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Zvonjenje…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Sprejmi", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Ne", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Ustvari novo ekipo", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Izbriši račun", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Iskanje po imenu", "searchServices": "Storitve", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Člani ekipe", "searchTopPeople": "Top osebe", "searchTrySearch": "Najdi osebe po imenu ali uporabniškem imenu", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Odloži", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Ta različica {{brandName}} ne more sodelovati v klicu. Prosimo uporabite", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} kliče. Vaš brskalnik ne podpira klicev.", diff --git a/src/i18n/sr-SP.json b/src/i18n/sr-SP.json index 6839d69138f..b8425eee7c2 100644 --- a/src/i18n/sr-SP.json +++ b/src/i18n/sr-SP.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} неко зове", "callStateOutgoing": "Звони…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Гост", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Придружите се разговору", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", и", "ephemeralRemaining": "преостали", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Прихвати", - "modalImproveWireHeadline": "Помозите нам да направимо {{brandName}} боље", - "modalImproveWireMessage": "Слажем се да {{brandName}} може креирати и користити анонимне извештаје о коришћењу и грешкама за побољшање апликације {{brandName}} Могу у било којем тренутку опозвати ову сагласност.", - "modalImproveWireSecondary": "Не", "modalIntegrationUnavailableHeadline": "Ботови тренутно нису доступни", "modalIntegrationUnavailableMessage": "Хвала на интересовању за робота. Услуга је тренутно обустављена док радимо на следећој верзији. Будите у току.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Створите тим", "preferencesAccountData": "Дозволе за коришћење података", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Обриши налог", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "Када је ово укључено, људи могу видети када се читају њихове поруке у овом разговору.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Отворени разговор", "searchServicePlaceholder": "Претрага по имену", "searchServices": "Услуге", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Чланови тима", "searchTopPeople": "Најчешће контактирани", "searchTrySearch": "Нађите људе по имену\nили корисничком имену", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Поново пошаљи шифру", "verify.subhead": "Унесите верификациони код који смо послали {newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Прекини везу", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Подели екран", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Видео је паузиран", "videoCallScreenShareNotSupported": "Дељење екрана није подржано у овом прегледачу", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Ова верзија Вајера не може да учествује у позиву. Користите", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} зове. Ваш прегледач не подржава позиве.", diff --git a/src/i18n/sv-SE.json b/src/i18n/sv-SE.json index c3dcfae54c8..5d458f8cf9c 100644 --- a/src/i18n/sv-SE.json +++ b/src/i18n/sv-SE.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} ringer", "callStateOutgoing": "Ringer…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Gäst", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", och ", "ephemeralRemaining": "återstår", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Acceptera", - "modalImproveWireHeadline": "Hjälp oss att göra {{brandName}} bättre", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "Inte nu", "modalIntegrationUnavailableHeadline": "Botar är för närvarande o-tillgängliga", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Skapa ett team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Ta bort konto", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Lägg till tjänst", "searchServicePlaceholder": "Sök efter namn", "searchServices": "Tjänster", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Teammedlemmar", "searchTopPeople": "Topp-personer", "searchTrySearch": "Hitta personer efter\nnamn eller användarnamn", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Lägg på", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Dela skärm", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Klippet pausades", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Denna version av {{brandName}} kan inte delta i samtal. Använd", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} ringer. Din webbläsare har inte stöd för samtal.", diff --git a/src/i18n/th-TH.json b/src/i18n/th-TH.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/th-TH.json +++ b/src/i18n/th-TH.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/tr-TR.json b/src/i18n/tr-TR.json index 8660d516cb5..f8c0bf82816 100644 --- a/src/i18n/tr-TR.json +++ b/src/i18n/tr-TR.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} Aranıyor", "callStateOutgoing": "Çalıyor…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Misafir", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Sohbete katılın", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", ve ", "ephemeralRemaining": "kalan", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Kabul et", - "modalImproveWireHeadline": "{{brandName}}'ı daha iyi hale getirmemize yardımcı olun", - "modalImproveWireMessage": "{{brandName}} uygulamasının {{brandName}} Uygulamasını iyileştirmek için adsız kullanım ve hata raporları oluşturup kullanabileceğini kabul ediyorum. Bu izni herhangi bir zamanda iptal edebilirim.", - "modalImproveWireSecondary": "Hayır", "modalIntegrationUnavailableHeadline": "Botlar şuanda kullanılabilir değil", "modalIntegrationUnavailableMessage": "Botlara ilginiz için teşekkür ederiz. Bir bir sonraki sürüm üzerinde çalışırken hizmet askıya alınmış. Beklemede kalın.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Bir takım oluştur", "preferencesAccountData": "Veri kullanım izinleri", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Hesabı Sil", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "Bu açıkken, insanlar bu görüşmedeki mesajlarının ne zaman okunduğunu görebilir.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Sohbet Başlat", "searchServicePlaceholder": "İsme göre ara", "searchServices": "Servisler", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Takım Üyeleri", "searchTopPeople": "Enler", "searchTrySearch": "İnsanları isimlerine veya kullanıcı adlarına göre bul", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Kodu yeniden gönder", "verify.subhead": "{email} e-postasına{newline}gönderdiğimiz doğrulama kodunu girin", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Kapat", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Ekran Paylaşımı", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video duraklatıldı", "videoCallScreenShareNotSupported": "Ekran paylaşımı bu tarayıcıda desteklenmiyor", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "{{brandName}}’ın bu versiyonu aramalara katılamaz. Lütfen kullanın", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} arıyor. Ancak tarayıcınız sesli aramaları desteklemiyor.", diff --git a/src/i18n/uk-UA.json b/src/i18n/uk-UA.json index e3b57756681..cb418cc4dea 100644 --- a/src/i18n/uk-UA.json +++ b/src/i18n/uk-UA.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} дзвонить", "callStateOutgoing": "Дзвінок…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Гість", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Приєднатися до розмови", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", і ", "ephemeralRemaining": "залишилось", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "Прийняти", - "modalImproveWireHeadline": "Допоможіть нам зробити {{brandName}} кращим", - "modalImproveWireMessage": "Я згоден(-а) з тим, що {{brandName}} може створювати та використовувати анонімні звіти про використання програми та збої в роботі для покращення якості додатку. Я можу відкликати мою згоду в будь-який час.", - "modalImproveWireSecondary": "Ні", "modalIntegrationUnavailableHeadline": "Боти в даний час недоступні", "modalIntegrationUnavailableMessage": "Дякуємо вам за інтерес, проявлений до ботів. В даний час ця функціональність заморожена, але ми працюємо над тим, щоб активувати її в наступній версії. Слідкуйте за оновленнями.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Створити команду", "preferencesAccountData": "Дозвіл на використання даних", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Видалити акаунт", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "Якщо ця опція увімкнена, учасники розмови можуть бачити, коли їхні повідомлення в цій розмові були переглянуті.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Додати сервіс", "searchServicePlaceholder": "Пошук за іменем", "searchServices": "Сервіси", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Учасники команди", "searchTopPeople": "Топ-контакти", "searchTrySearch": "Шукайте людай\nза іменем або ніком", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Надіслати код повторно", "verify.subhead": "Введіть код підтвердження, який ми надіслали на{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Завершити", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Показати свій екран", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Відео призупинене", "videoCallScreenShareNotSupported": "Ваш браузер не підтримує спільний доступ до екрану", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "Ця версія {{brandName}} не може брати участь у дзвінку. Будь ласка, використовуйте", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} дзвонить. Ваш браузер не підтримує дзвінки.", diff --git a/src/i18n/uz-UZ.json b/src/i18n/uz-UZ.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/uz-UZ.json +++ b/src/i18n/uz-UZ.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/vi-VN.json b/src/i18n/vi-VN.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/vi-VN.json +++ b/src/i18n/vi-VN.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/zh-CN.json b/src/i18n/zh-CN.json index aae1a7a0cd5..28ccfc2a3ae 100644 --- a/src/i18n/zh-CN.json +++ b/src/i18n/zh-CN.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} 正在通话中", "callStateOutgoing": "响铃中...", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "访客", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "加入会话", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ",和 ", "ephemeralRemaining": "还剩", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "接受", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "否", "modalIntegrationUnavailableHeadline": "机器人目前不可用。", "modalIntegrationUnavailableMessage": "感谢您对机器人服务的关注。我们目前正在开发下一个版本,因此机器人服务暂停使用。敬请关注。", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "创建团队", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "删除账户", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "添加服务", "searchServicePlaceholder": "按名字搜索", "searchServices": "服务", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "团队成员", "searchTopPeople": "常用联系人", "searchTrySearch": "通过昵称或用户名寻找好友", @@ -1521,12 +1530,22 @@ "verify.resendCode": "重新发送验证码", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "挂断", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "分享屏幕", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "视频已暂停", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "此版本{{brandName}}无法使用呼叫功能。请使用", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} 正在呼叫您,但您的浏览器不支持通话。", diff --git a/src/i18n/zh-HK.json b/src/i18n/zh-HK.json index a7c3742d5b4..d92dfcc9061 100644 --- a/src/i18n/zh-HK.json +++ b/src/i18n/zh-HK.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "Ringing…", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "Guest", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "I Agree", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "No", "modalIntegrationUnavailableHeadline": "Bots currently unavailable", "modalIntegrationUnavailableMessage": "Thank you for your interest in bots. The service is currently suspended while we work on the next version. Stay tuned.", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "Create a team", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "Delete account", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "Search by name", "searchServices": "Services", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "Team members", "searchTopPeople": "Top people", "searchTrySearch": "Find people by\nname or username", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "Hang Up", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "This version of {{brandName}} can not participate in the call. Please use", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} is calling. Your browser doesn’t support calls.", diff --git a/src/i18n/zh-TW.json b/src/i18n/zh-TW.json index a09ef55f955..ae4000dd34c 100644 --- a/src/i18n/zh-TW.json +++ b/src/i18n/zh-TW.json @@ -332,6 +332,7 @@ "callStateIncomingGroup": "{{user}} is calling", "callStateOutgoing": "響鈴中...", "callWasEndedBecause": "Your call was ended because", + "callingPopOutWindowTitle": "{{brandName}} Call", "callingRestrictedConferenceCallOwnerModalDescription": "Your team is currently on the free Basic plan. Upgrade to Enterprise for access to features such as starting conferences and more. [link]Learn more about {{brandName}} Enterprise[/link]", "callingRestrictedConferenceCallOwnerModalTitle": "Upgrade to Enterprise", "callingRestrictedConferenceCallOwnerModalUpgradeButton": "Upgrade now", @@ -477,6 +478,7 @@ "conversationGroupEmptyMessage": "You are not part of any group conversation yet.", "conversationGuestIndicator": "訪客", "conversationImageAssetRestricted": "Receiving images is prohibited", + "conversationInternalEnvironmentDisclaimer": "This is NOT WIRE but an internal testing environment. Authorized for use by Wire employees only. Any public USE is PROHIBITED. The data of the users of this test environment is extensively recorded and analysed. To use the secure messenger Wire, please visit [link]{{url}}[/link]", "conversationJoin.existentAccountJoinInBrowser": "Join in the browser", "conversationJoin.existentAccountJoinWithoutLink": "Join the conversation", "conversationJoin.existentAccountUserName": "You are logged in as {selfName}", @@ -634,6 +636,10 @@ "customEnvRedirect.credentialsInfo": "Provide credentials only if you're sure this is your organization's login.", "customEnvRedirect.redirectHeadline": "Redirecting...", "customEnvRedirect.redirectTo": "You are being redirected to your dedicated enterprise service.", + "dataSharingModalAgree": "Agree", + "dataSharingModalDecline": "Decline", + "dataSharingModalDescription": "Help to improve Wire by sharing your usage data via a pseudonymous ID. The data is neither linked to your personal information nor shared with third parties besides Wire Group. It includes, for example, when you use a feature, your app version, device type, or your operating system. This data will be deleted at the latest after 365 days.
Find further details in our [link]Privacy Policy[/link]. You can revoke your consent at any time.", + "dataSharingModalTitle": "Consent to share user data", "downloadLatestMLS": "Download the latest MLS Wire version", "enumerationAnd": ", and ", "ephemeralRemaining": "remaining", @@ -1075,10 +1081,6 @@ "modalGuestLinkJoinHelperText": "Use at least {{minPasswordLength}} characters, with one lowercase letter, one capital letter, a number, and a special character.", "modalGuestLinkJoinLabel": "Set password", "modalGuestLinkJoinPlaceholder": "Enter password", - "modalImproveWireAction": "接聽", - "modalImproveWireHeadline": "Help us make {{brandName}} better", - "modalImproveWireMessage": "I agree that {{brandName}} may create and use anonymous usage and error reports to improve the {{brandName}} App. I can revoke this consent at any time.", - "modalImproveWireSecondary": "否", "modalIntegrationUnavailableHeadline": "機器人目前不可用。", "modalIntegrationUnavailableMessage": "感謝您對機器人服務感興趣。我們目前正在開發下一個版本,因此機器人服務暫停使用,敬請靜候佳音。", "modalLegalHoldConversationMissingConsentMessage": "Due to legal hold, only team members can be added to this conversation. [link]Learn more[/link]", @@ -1236,8 +1238,6 @@ "preferencesAccountCopyLink": "Copy Profile Link", "preferencesAccountCreateTeam": "建立團隊", "preferencesAccountData": "Data usage permissions", - "preferencesAccountDataCheckbox": "Send anonymous crash reports", - "preferencesAccountDataDetail": "Send anonymous crash reports and basic data like version number and operating system to help {{brandName}} identify and solve issues in the app.", "preferencesAccountDataTelemetry": "Usage data allows {{brandName}} to understand how the app is being used and how it can be improved. The data is anonymous and does not include the content of your communications (such as messages, files or calls).", "preferencesAccountDataTelemetryCheckbox": "Send anonymous usage data", "preferencesAccountDelete": "刪除帳號", @@ -1342,6 +1342,14 @@ "proteusNotVerified": "Not Verified", "proteusVerified": "Verified", "proteusVerifiedDetails": "Verified (Proteus)", + "qualityFeedback.bad": "Bad", + "qualityFeedback.description": "How do you rate the overall quality of the call?", + "qualityFeedback.doNotAskAgain": "Don't ask again", + "qualityFeedback.excellent": "Excellent", + "qualityFeedback.fair": "Fair", + "qualityFeedback.heading": "Call Quality Feedback", + "qualityFeedback.notificationSubmitted": "Your feedback has been submitted. Thank you!", + "qualityFeedback.skip": "Skip", "readReceiptsToggleInfo": "When this is on, people can see when their messages in this conversation are read.", "readReceiptsToggleName": "Read receipts", "receiptToggleInfo": "When this is on, people can see when their messages in this conversation are read.", @@ -1400,6 +1408,7 @@ "searchServiceConfirmButton": "Open Conversation", "searchServicePlaceholder": "按名稱搜尋", "searchServices": "服務", + "searchTeamGroups": "Team conversations", "searchTeamMembers": "群組成員", "searchTopPeople": "常用連絡人", "searchTrySearch": "依照使用者名稱或別名來找人", @@ -1521,12 +1530,22 @@ "verify.resendCode": "Resend code", "verify.subhead": "Enter the verification code we sent to{newline}{email}", "videoCallOverlayCamera": "Camera", + "videoCallOverlayChangeViewMode": "Change view mode", + "videoCallOverlayCloseFullScreen": "Go back to minimized view", "videoCallOverlayConversations": "Conversations", "videoCallOverlayFitVideoLabel": "Double-click to view full screen", "videoCallOverlayFitVideoLabelGoBack": "Double-click to show all participants", "videoCallOverlayHangUp": "結束通話", + "videoCallOverlayHideParticipantsList": "Hide participants list", "videoCallOverlayMicrophone": "Microphone", + "videoCallOverlayOpenFullScreen": "Open the call in full screen", + "videoCallOverlayOpenPopupWindow": "Open in a new window", + "videoCallOverlayParticipantsListLabel": "Participants ({{count}})", "videoCallOverlayShareScreen": "Share Screen", + "videoCallOverlayShowParticipantsList": "Show participants list", + "videoCallOverlayViewModeAll": "Show all participants", + "videoCallOverlayViewModeLabel": "View mode", + "videoCallOverlayViewModeSpeakers": "Show active speakers only", "videoCallParticipantConnecting": "Connecting...", "videoCallPaused": "Video paused", "videoCallScreenShareNotSupported": "Screen sharing is not supported in this browser", @@ -1538,6 +1557,7 @@ "videoCallvideoInputCamera": "Camera", "videoSpeakersTabAll": "All ({{count}})", "videoSpeakersTabSpeakers": "Speakers", + "viewingInAnotherWindow": "Viewing in another window", "warningCallIssues": "使用此版本的 {{brandName}} 無法參與此次對話,請使用", "warningCallQualityPoor": "Poor connection", "warningCallUnsupportedIncoming": "{{user}} 正在呼叫您,但是您的瀏覽器不支援對話功能。", diff --git a/src/page/auth.ejs b/src/page/auth.ejs index ded418ba85e..c2d36fbf930 100644 --- a/src/page/auth.ejs +++ b/src/page/auth.ejs @@ -21,6 +21,20 @@ + + + diff --git a/src/page/countlyBoomerangCustom.js b/src/page/countlyBoomerangCustom.js new file mode 100644 index 00000000000..dab56494287 --- /dev/null +++ b/src/page/countlyBoomerangCustom.js @@ -0,0 +1,215 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +/* + * This is a modified version of the Countly Boomerang plugin - countly_boomerang.js. + * The original plugin can be found at https://github.com/Countly/countly-sdk-web + */ + +'use strict'; + +/* global Countly */ +/* +Countly APM based on Boomerang JS +Plugin being used - RT, AutoXHR, Continuity, NavigationTiming, ResourceTiming +*/ +(function cly_load_track_performance() { + // will be used to trim UUIDs from URLs for network traces + function trimUUIDFromURL(url) { + const removedUUIDUrl = url.replace( + /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g, + '#id#', + ); + const removedHexadecimalIdUrl = removedUUIDUrl.replace(/[0-9a-fA-F]{32}/g, '#id#'); + + return removedHexadecimalIdUrl; + } + + if (typeof window === 'undefined') { + return; // apm plugin needs window to be defined due to boomerang.js. Can't be used in webworkers + } + var Countly = window.Countly || {}; + Countly.onload = Countly.onload || []; + if (typeof Countly.CountlyClass === 'undefined') { + return Countly.onload.push(function () { + cly_load_track_performance(); + if (!Countly.track_performance && Countly.i) { + Countly.track_performance = Countly.i[Countly.app_key].track_performance; + } + }); + } + /** + * Enables tracking performance through boomerang.js + * @memberof Countly + * @param {object} config - Boomerang js configuration + */ + Countly.CountlyClass.prototype.track_performance = function (config) { + var self = this; + config = config || { + // page load timing + RT: {}, + // required for automated networking traces + instrument_xhr: true, + captureXhrRequestResponse: true, + AutoXHR: { + alwaysSendXhr: true, + monitorFetch: true, + captureXhrRequestResponse: true, + }, + // required for screen freeze traces + Continuity: { + enabled: true, + monitorLongTasks: true, + monitorPageBusy: true, + monitorFrameRate: true, + monitorInteractions: true, + afterOnload: true, + }, + }; + var initedBoomr = false; + /** + * Initialize Boomerang + * @param {Object} BOOMR - Boomerang object + */ + function initBoomerang(BOOMR) { + if (BOOMR && !initedBoomr) { + BOOMR.subscribe('before_beacon', function (beaconData) { + self._internals.log('[INFO]', 'Boomerang, before_beacon:', JSON.stringify(beaconData, null, 2)); + var trace = {}; + if (beaconData['rt.start'] !== 'manual' && !beaconData['http.initiator'] && beaconData['rt.quit'] !== '') { + trace.type = 'device'; + trace.apm_metrics = {}; + if (typeof beaconData['pt.fp'] !== 'undefined') { + trace.apm_metrics.first_paint = beaconData['pt.fp']; + } else if (typeof beaconData.nt_first_paint !== 'undefined') { + trace.apm_metrics.first_paint = beaconData.nt_first_paint - beaconData['rt.tstart']; + } + if (typeof beaconData['pt.fcp'] !== 'undefined') { + trace.apm_metrics.first_contentful_paint = beaconData['pt.fcp']; + } + if (typeof beaconData.nt_domint !== 'undefined') { + trace.apm_metrics.dom_interactive = beaconData.nt_domint - beaconData['rt.tstart']; + } + if ( + typeof beaconData.nt_domcontloaded_st !== 'undefined' && + typeof beaconData.nt_domcontloaded_end !== 'undefined' + ) { + trace.apm_metrics.dom_content_loaded_event_end = + beaconData.nt_domcontloaded_end - beaconData.nt_domcontloaded_st; + } + if (typeof beaconData.nt_load_st !== 'undefined' && typeof beaconData.nt_load_end !== 'undefined') { + trace.apm_metrics.load_event_end = beaconData.nt_load_end - beaconData.nt_load_st; + } + if (typeof beaconData['c.fid'] !== 'undefined') { + trace.apm_metrics.first_input_delay = beaconData['c.fid']; + } + } else if ( + beaconData['http.initiator'] && + ['xhr', 'spa', 'spa_hard'].indexOf(beaconData['http.initiator']) !== -1 + ) { + var responseTime; + var responsePayloadSize; + var requestPayloadSize; + var responseCode; + responseTime = beaconData.t_resp; + // t_resp - Time taken from the user initiating the request to the first byte of the response. - Added by RT + responseCode = typeof beaconData['http.errno'] !== 'undefined' ? beaconData['http.errno'] : 200; + + try { + var restiming = JSON.parse(beaconData.restiming); + var ResourceTimingDecompression = window.ResourceTimingDecompression; + if (ResourceTimingDecompression && restiming) { + // restiming contains information regarging all the resources that are loaded in any + // spa, spa_hard or xhr requests. + // xhr requests should ideally have only one entry in the array which is the one for + // which the beacon is being sent. + // But for spa_hard requests it can contain multiple entries, one for each resource + // that is loaded in the application. Example - all images, scripts etc. + // ResourceTimingDecompression is not included in the official boomerang library. + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var decompressedData = ResourceTimingDecompression.decompressResources(restiming); + var currentBeacon = decompressedData.filter(function (resource) { + return resource.name === beaconData.u; + }); + + if (currentBeacon.length) { + responsePayloadSize = currentBeacon[0].decodedBodySize; + responseTime = currentBeacon[0].duration ? currentBeacon[0].duration : responseTime; + // duration - Returns the difference between the resource's responseEnd timestamp and its startTime timestamp - ResourceTiming API + } + } + } catch (e) { + self._internals.log('[ERROR]', 'Boomerang, Error while using resource timing data decompression', config); + } + + trace.type = 'network'; + trace.apm_metrics = { + response_time: responseTime, + response_payload_size: responsePayloadSize, + request_payload_size: requestPayloadSize, + response_code: responseCode, + }; + } + + if (trace.type) { + trace.name = trimUUIDFromURL((beaconData.u + '').split('//').pop().split('?')[0]); + trace.stz = beaconData['rt.tstart']; + trace.etz = beaconData['rt.end']; + self.report_trace(trace); + } + }); + + BOOMR.xhr_excludes = BOOMR.xhr_excludes || {}; + BOOMR.xhr_excludes[self.url.split('//').pop()] = true; + if (typeof config.beacon_disable_sendbeacon === 'undefined') { + config.beacon_disable_sendbeacon = true; + } + BOOMR.init(config); + BOOMR.t_end = new Date().getTime(); + Countly.BOOMR = BOOMR; + initedBoomr = true; + self._internals.log('[INFO]', 'Boomerang initiated:', config); + } else { + self._internals.log('[WARNING]', 'Boomerang called without its instance or was already initialized'); + } + } + if (window.BOOMR) { + initBoomerang(window.BOOMR); + } else { + self._internals.log('[WARNING]', 'Boomerang not yet loaded, waiting for it to load'); + // Modern browsers + if (document.addEventListener) { + document.addEventListener('onBoomerangLoaded', function (e) { + initBoomerang(e.detail.BOOMR); + }); + } + // IE 6, 7, 8 we use onPropertyChange and look for propertyName === "onBoomerangLoaded" + else if (document.attachEvent) { + document.attachEvent('onpropertychange', function (e) { + if (!e) { + e = event; + } + if (e.propertyName === 'onBoomerangLoaded') { + initBoomerang(e.detail.BOOMR); + } + }); + } + } + }; +})(); diff --git a/src/page/index.ejs b/src/page/index.ejs index aea610f224a..bbf0d286bf9 100644 --- a/src/page/index.ejs +++ b/src/page/index.ejs @@ -6,10 +6,10 @@ - - - - + + + + @@ -21,6 +21,20 @@ + + + diff --git a/src/script/Config.ts b/src/script/Config.ts index 34df51a3107..37a5e6ef619 100644 --- a/src/script/Config.ts +++ b/src/script/Config.ts @@ -78,7 +78,7 @@ const config = { ALLOWED_IMAGE_TYPES: ['image/bmp', 'image/gif', 'image/jpeg', 'image/jpg', 'image/png'], /** Which min and max version of the backend api do we support */ - SUPPORTED_API_RANGE: [1, env.ENABLE_DEV_BACKEND_API ? Infinity : 5], + SUPPORTED_API_RANGE: [1, env.ENABLE_DEV_BACKEND_API ? Infinity : 6], /** DataDog client api keys acces */ dataDog: { @@ -95,6 +95,9 @@ const Config = { getConfig: () => { return config; }, + _dangerouslySetConfigFeaturesForDebug: (newConfigFeatures: Configuration['FEATURE']) => { + (config.FEATURE as unknown) = newConfigFeatures; + }, getDesktopConfig: () => { if (!Runtime.isDesktopApp) { return undefined; diff --git a/src/script/assets/AssetRepository.test.ts b/src/script/assets/AssetRepository.test.ts index 2ade966ec77..1816542abb6 100644 --- a/src/script/assets/AssetRepository.test.ts +++ b/src/script/assets/AssetRepository.test.ts @@ -21,14 +21,10 @@ import {AssetUploadData} from '@wireapp/api-client/lib/asset/'; import {container} from 'tsyringe'; import {createUuid} from 'Util/uuid'; -import {ValidationUtilError} from 'Util/ValidationUtil'; import {AssetRemoteData} from './AssetRemoteData'; import {AssetRepository, AssetUploadOptions} from './AssetRepository'; -import {AssetService} from './AssetService'; -import {EventMapper} from '../conversation/EventMapper'; -import {APIClient} from '../service/APIClientSingleton'; import {Core} from '../service/CoreSingleton'; describe('AssetRepository', () => { @@ -40,19 +36,7 @@ describe('AssetRepository', () => { beforeEach(async () => { core = container.resolve(Core); - const mockedAPIClient = { - asset: { - api: { - postAsset: jest.fn().mockImplementation(() => - Promise.resolve({ - cancel: () => {}, - response: Promise.resolve({}), - }), - ), - }, - }, - } as unknown as APIClient; - assetRepository = new AssetRepository(new AssetService(mockedAPIClient), core); + assetRepository = new AssetRepository(core); }); describe('load unencrypted v1 asset', () => { @@ -99,98 +83,6 @@ describe('AssetRepository', () => { }); }); - it('detects a malformed asset key', async () => { - const event: any = { - category: 128, - conversation: '61350a90-e522-4ee5-90b7-f55b648e34da', - data: { - content_length: 73029, - content_type: 'image/jpeg', - info: {height: 448, name: null, nonce: '51b8e5c5-4088-4177-a5ad-b001fef11eac', tag: 'medium', width: 588}, - key: '../../../search/contacts', - otr_key: { - '0': 130, - '1': 255, - '10': 2, - '11': 194, - '12': 160, - '13': 122, - '14': 173, - '15': 82, - '16': 36, - '17': 77, - '18': 50, - '19': 186, - '2': 81, - '20': 246, - '21': 54, - '22': 36, - '23': 235, - '24': 81, - '25': 19, - '26': 179, - '27': 69, - '28': 127, - '29': 113, - '3': 125, - '30': 248, - '31': 74, - '4': 202, - '5': 165, - '6': 197, - '7': 175, - '8': 79, - '9': 18, - }, - sha256: { - '0': 49, - '1': 137, - '10': 37, - '11': 35, - '12': 87, - '13': 175, - '14': 205, - '15': 157, - '16': 225, - '17': 173, - '18': 63, - '19': 43, - '2': 43, - '20': 133, - '21': 197, - '22': 115, - '23': 195, - '24': 142, - '25': 44, - '26': 98, - '27': 222, - '28': 75, - '29': 56, - '3': 166, - '30': 159, - '31': 66, - '4': 242, - '5': 222, - '6': 42, - '7': 96, - '8': 44, - '9': 22, - }, - status: 'uploaded', - token: 'hYQytxHS6hSP6DlemD13uQ==&size=100&q=test', - }, - from: '532af01e-1e24-4366-aacf-33b67d4ee376', - id: '51b8e5c5-4088-4177-a5ad-b001fef11eac', - primary_key: 6, - status: 1, - time: '2017-08-16T16:13:01.168Z', - type: 'conversation.asset-add', - }; - - const asset_et = new EventMapper()['_mapAssetImage'](event); - await expect(assetRepository.generateAssetUrl(asset_et.resource())).rejects.toThrow(ValidationUtilError); - }); - it('keeps track of current uploads and removes it once finished', async () => { spyOn(core.service!.asset, 'uploadAsset').and.callFake(() => { expect(assetRepository.getNumberOfOngoingUploads()).toBe(1); diff --git a/src/script/assets/AssetRepository.ts b/src/script/assets/AssetRepository.ts index daf703fe025..340b2fa1939 100644 --- a/src/script/assets/AssetRepository.ts +++ b/src/script/assets/AssetRepository.ts @@ -29,7 +29,6 @@ import {downloadBlob, loadFileBuffer, loadImage} from 'Util/util'; import {WebWorker} from 'Util/worker'; import {AssetRemoteData} from './AssetRemoteData'; -import {AssetService} from './AssetService'; import {AssetTransferState} from './AssetTransferState'; import {getAssetUrl, setAssetUrl} from './AssetURLCache'; @@ -38,6 +37,7 @@ import {FileAsset} from '../entity/message/FileAsset'; import type {User} from '../entity/User'; import {Core} from '../service/CoreSingleton'; import {TeamState} from '../team/TeamState'; +import {stripImageExifData} from '../util/ImageUtil'; interface CompressedImage { compressedBytes: Uint8Array; @@ -61,7 +61,6 @@ export class AssetRepository { logger: Logger; constructor( - private readonly assetService = container.resolve(AssetService), private readonly core = container.resolve(Core), private readonly teamState = container.resolve(TeamState), ) { @@ -72,7 +71,7 @@ export class AssetRepository { return this.core.service!.asset; } - async getObjectUrl(asset: AssetRemoteData): Promise { + async getObjectUrl(asset: AssetRemoteData): Promise { const objectUrl = getAssetUrl(asset.identifier); if (objectUrl) { return objectUrl; @@ -111,31 +110,6 @@ export class AssetRepository { } } - public generateAssetUrl(asset: AssetRemoteData) { - switch (asset.urlData.version) { - case 3: - return this.assetService.generateAssetUrlV3( - asset.urlData.assetKey, - asset.urlData.assetToken, - asset.urlData.forceCaching, - ); - case 2: - return this.assetService.generateAssetUrlV2( - asset.urlData.assetId, - asset.urlData.conversationId, - asset.urlData.forceCaching, - ); - case 1: - return this.assetService.generateAssetUrl( - asset.urlData.assetId, - asset.urlData.conversationId, - asset.urlData.forceCaching, - ); - default: - throw Error('Cannot map URL data.'); - } - } - private loadBuffer(asset: AssetRemoteData) { const isEncryptedAsset = !!asset.otrKey && !!asset.sha256; const progressCallback = (fraction: number) => { @@ -187,9 +161,11 @@ export class AssetRepository { mediumImageKey: {domain?: string; key: string}; previewImageKey: {domain?: string; key: string}; }> { + const strippedImage = await stripImageExifData(image); + const [{compressedBytes: previewImage}, {compressedBytes: mediumImage}] = await Promise.all([ - this.compressImage(image), - this.compressImage(image, true), + this.compressImage(strippedImage), + this.compressImage(strippedImage, true), ]); const options: AssetUploadOptions = { diff --git a/src/script/assets/AssetService.ts b/src/script/assets/AssetService.ts deleted file mode 100644 index 8322e8ce0e2..00000000000 --- a/src/script/assets/AssetService.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Wire - * Copyright (C) 2018 Wire Swiss GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - */ - -import {singleton, container} from 'tsyringe'; - -import {legacyAsset, assetV3, isValidApiPath} from 'Util/ValidationUtil'; - -import {APIClient} from '../service/APIClientSingleton'; - -@singleton() -export class AssetService { - constructor(private readonly apiClient = container.resolve(APIClient)) {} - - async generateAssetUrl(assetId: string, conversationId: string, forceCaching: boolean): Promise { - legacyAsset(assetId, conversationId); - - const path = `/assets/${assetId}`; - isValidApiPath(path); - - const url = `${this.apiClient.config.urls.rest}${path}`; - const cachingParam = forceCaching ? '&forceCaching=true' : ''; - const conversationIdParam = `&conv_id=${encodeURIComponent(conversationId)}`; - return `${url}?access_token=${this.apiClient['accessTokenStore'].accessToken?.access_token}${conversationIdParam}${cachingParam}`; - } - - async generateAssetUrlV2(assetId: string, conversationId: string, forceCaching: boolean): Promise { - legacyAsset(assetId, conversationId); - - const path = `/conversations/${conversationId}/otr/assets/${assetId}`; - isValidApiPath(path); - - const url = `${this.apiClient.config.urls.rest}${path}`; - const cachingParam = forceCaching ? '&forceCaching=true' : ''; - return `${url}?access_token=${this.apiClient['accessTokenStore'].accessToken?.access_token}${cachingParam}`; - } - - async generateAssetUrlV3(assetKey: string, assetToken: string, forceCaching: boolean): Promise { - assetV3(assetKey, assetToken); - - const path = `/assets/v3/${assetKey}`; - isValidApiPath(path); - - const url = `${this.apiClient.config.urls.rest}${path}`; - const assetTokenParam = assetToken ? `&asset_token=${encodeURIComponent(assetToken)}` : ''; - const cachingParam = forceCaching ? '&forceCaching=true' : ''; - return `${url}?access_token=${this.apiClient['accessTokenStore'].accessToken?.access_token}${assetTokenParam}${cachingParam}`; - } -} diff --git a/src/script/auth/page/Index.test.tsx b/src/script/auth/page/Index.test.tsx index 218eda06532..5dd352ff919 100644 --- a/src/script/auth/page/Index.test.tsx +++ b/src/script/auth/page/Index.test.tsx @@ -36,8 +36,10 @@ jest.mock('react-router-dom', () => ({ describe('when visiting the index page', () => { let configSpy: jest.SpyInstance; + beforeEach(() => { configSpy = jest.spyOn(Config, 'getConfig').mockReturnValue({ + APP_BASE: 'https://app.wire.com', BACKEND_NAME: 'mybrand', FEATURE: { ENABLE_ACCOUNT_REGISTRATION: true, @@ -81,7 +83,9 @@ describe('when visiting the index page', () => { it('shows the welcome text with custom backend name', () => { const customBackendName = 'Test'; + configSpy.mockReturnValue({ + APP_BASE: 'https://app.wire.com', BACKEND_NAME: customBackendName, FEATURE: { ENABLE_ACCOUNT_REGISTRATION: true, @@ -111,6 +115,7 @@ describe('when visiting the index page', () => { it('navigates to SSO login page when clicking SSO login button', async () => { configSpy.mockReturnValue({ + APP_BASE: 'https://app.wire.com', FEATURE: { ENABLE_DOMAIN_DISCOVERY: true, ENABLE_SSO: true, @@ -152,6 +157,7 @@ describe('when visiting the index page', () => { describe('and the account registration is enabled', () => { beforeEach(() => { configSpy.mockReturnValue({ + APP_BASE: 'https://app.wire.com', FEATURE: { ENABLE_ACCOUNT_REGISTRATION: true, }, @@ -182,6 +188,7 @@ describe('when visiting the index page', () => { describe('and SSO & domain discovery is disabled', () => { beforeEach(() => { configSpy.mockReturnValue({ + APP_BASE: 'https://app.wire.com', FEATURE: { ENABLE_DOMAIN_DISCOVERY: false, ENABLE_SSO: false, @@ -199,6 +206,7 @@ describe('when visiting the index page', () => { describe('and SSO, domain discovery & account registration is disabled', () => { beforeEach(() => { configSpy.mockReturnValue({ + APP_BASE: 'https://app.wire.com', FEATURE: { ENABLE_ACCOUNT_REGISTRATION: false, ENABLE_DOMAIN_DISCOVERY: false, diff --git a/src/script/auth/page/Index.tsx b/src/script/auth/page/Index.tsx index 2d50c84504a..8f81af2642a 100644 --- a/src/script/auth/page/Index.tsx +++ b/src/script/auth/page/Index.tsx @@ -28,7 +28,8 @@ import {UrlUtil} from '@wireapp/commons'; import {Button, ButtonVariant, ContainerXS, ErrorMessage, Text} from '@wireapp/react-ui-kit'; import {LogoFullIcon} from 'Components/Icon'; -import {Environment} from 'Util/Environment'; +import {isDataDogEnabled} from 'Util/DataDog'; +import {getWebEnvironment} from 'Util/Environment'; import {Page} from './Page'; @@ -46,8 +47,6 @@ const IndexComponent = ({defaultSSOCode}: Props & ConnectedProps & DispatchProps const navigate = useNavigate(); const [logoutReason, setLogoutReason] = useState(); - const isProduction = Environment.frontend.isProduction(); - useEffect(() => { const queryLogoutReason = UrlUtil.getURLParameter(QUERY_KEY.LOGOUT_REASON) || null; if (queryLogoutReason) { @@ -85,7 +84,7 @@ const IndexComponent = ({defaultSSOCode}: Props & ConnectedProps & DispatchProps {_(indexStrings.welcome, {brandName: Config.getConfig().BACKEND_NAME})} - {!isProduction && ( + {!getWebEnvironment().isProduction && isDataDogEnabled() && ( ; public readonly joinedCall: ko.PureComputed; public readonly activeCallViewTab = ko.observable(CallViewTab.ALL); - readonly isChoosingScreen: ko.PureComputed; + readonly hasAvailableScreensToShare: ko.PureComputed; readonly isSpeakersViewActive: ko.PureComputed; public readonly viewMode = ko.observable(CallingViewMode.MINIMIZED); + public readonly detachedWindow = ko.observable(null); + public readonly isScreenSharingSourceFromDetachedWindow = ko.observable(false); + public readonly detachedWindowCallQualifiedId = ko.observable(null); + public readonly desktopScreenShareMenu = ko.observable(DesktopScreenShareMenu.NONE); + private currentViewMode = this.viewMode(); constructor() { this.joinedCall = ko.pureComputed(() => this.calls().find(call => call.state() === CALL_STATE.MEDIA_ESTAB)); @@ -72,9 +87,6 @@ export class CallState { call => call.state() === CALL_STATE.INCOMING && call.reason() !== CALL_REASON.ANSWERED_ELSEWHERE, ), ); - this.isChoosingScreen = ko.pureComputed( - () => this.selectableScreens().length > 0 || this.selectableWindows().length > 0, - ); this.calls.subscribe(activeCalls => { const activeCallIds = activeCalls.map(call => call.conversation.qualifiedId); @@ -84,8 +96,18 @@ export class CallState { }); this.isSpeakersViewActive = ko.pureComputed(() => this.activeCallViewTab() === CallViewTab.SPEAKERS); - this.isChoosingScreen = ko.pureComputed( + this.hasAvailableScreensToShare = ko.pureComputed( () => this.selectableScreens().length > 0 || this.selectableWindows().length > 0, ); + + // Capture the viewMode value before change + this.viewMode.subscribe(newVal => (this.currentViewMode = newVal), this, 'beforeChange'); + + this.viewMode.subscribe(() => { + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.UI.CALLING_UI_SIZE, { + [Segmentation.CALLING_UI_SIZE.FROM]: this.currentViewMode, + [Segmentation.CALLING_UI_SIZE.TO]: this.viewMode(), + }); + }); } } diff --git a/src/script/calling/CallingRepository.ts b/src/script/calling/CallingRepository.ts index fda6772a55b..ae967894554 100644 --- a/src/script/calling/CallingRepository.ts +++ b/src/script/calling/CallingRepository.ts @@ -19,6 +19,7 @@ import type {CallConfigData} from '@wireapp/api-client/lib/account/CallConfigData'; import {QualifiedUserClients} from '@wireapp/api-client/lib/conversation'; +import {FEATURE_KEY} from '@wireapp/api-client/lib/team'; import type {QualifiedId} from '@wireapp/api-client/lib/user'; import type {WebappProperties} from '@wireapp/api-client/lib/user/data'; import {MessageSendingState} from '@wireapp/core/lib/conversation'; @@ -52,24 +53,30 @@ import { import {Runtime} from '@wireapp/commons'; import {WebAppEvents} from '@wireapp/webapp-events'; +import {showAppNotification} from 'Components/AppNotification'; +import {useCallAlertState} from 'Components/calling/useCallAlertState'; +import {CALL_QUALITY_FEEDBACK_KEY} from 'Components/Modals/QualityFeedbackModal/constants'; import {flatten} from 'Util/ArrayUtil'; +import {calculateChildWindowPosition} from 'Util/DOM/caculateChildWindowPosition'; +import {isDetachedCallingFeatureEnabled} from 'Util/isDetachedCallingFeatureEnabled'; import {t} from 'Util/LocalizerUtil'; import {getLogger, Logger} from 'Util/Logger'; import {roundLogarithmic} from 'Util/NumberUtil'; import {matchQualifiedIds} from 'Util/QualifiedId'; +import {copyStyles} from 'Util/renderElement'; import {TIME_IN_MILLIS} from 'Util/TimeUtil'; import {createUuid} from 'Util/uuid'; import {Call, SerializedConversationId} from './Call'; import {callingSubscriptions} from './callingSubscriptionsHandler'; -import {CallState, MuteState} from './CallState'; +import {CallingViewMode, CallState, MuteState} from './CallState'; import {CALL_MESSAGE_TYPE} from './enum/CallMessageType'; import {LEAVE_CALL_REASON} from './enum/LeaveCallReason'; import {ClientId, Participant, UserId} from './Participant'; import {PrimaryModal} from '../components/Modals/PrimaryModal'; import {Config} from '../Config'; -import {isGroupMLSConversation, isMLSConversation, MLSConversation} from '../conversation/ConversationSelectors'; +import {isMLSConversation, MLSConversation} from '../conversation/ConversationSelectors'; import {ConversationState} from '../conversation/ConversationState'; import {ConversationVerificationState} from '../conversation/ConversationVerificationState'; import {EventBuilder} from '../conversation/EventBuilder'; @@ -87,6 +94,7 @@ import {APIClient} from '../service/APIClientSingleton'; import {Core} from '../service/CoreSingleton'; import {TeamState} from '../team/TeamState'; import type {ServerTimeHandler} from '../time/serverTimeHandler'; +import {isCountlyEnabledAtCurrentEnvironment} from '../tracking/Countly.helpers'; import {EventName} from '../tracking/EventName'; import * as trackingHelpers from '../tracking/Helpers'; import {Segmentation} from '../tracking/Segmentation'; @@ -101,7 +109,9 @@ interface MediaStreamQuery { screen?: boolean; } -export type QualifiedWcallMember = Omit & {userId: QualifiedId}; +export type QualifiedWcallMember = Omit & { + userId: QualifiedId; +}; interface SendMessageTarget { clients: WcallClient[]; @@ -125,7 +135,11 @@ enum CALL_DIRECTION { OUTGOING = 'outgoing', } -type SubconversationData = {epoch: number; secretKey: string; members: SubconversationEpochInfoMember[]}; +type SubconversationData = { + epoch: number; + secretKey: string; + members: SubconversationEpochInfoMember[]; +}; export class CallingRepository { private readonly acceptVersionWarning: (conversationId: QualifiedId) => void; @@ -332,7 +346,7 @@ export class CallingRepository { private configureCallingApi(wCall: Wcall): Wcall { wCall.setLogHandler(this.avsLogHandler); - const avsEnv = Runtime.isFirefox() ? AVS_ENV.FIREFOX : AVS_ENV.DEFAULT; + const avsEnv = Runtime.isFirefox() || Runtime.isSafari() ? AVS_ENV.FIREFOX : AVS_ENV.DEFAULT; wCall.init(avsEnv); wCall.setUserMediaHandler(this.getCallMediaStream); wCall.setAudioStreamHandler(this.updateCallAudioStreams); @@ -372,7 +386,7 @@ export class CallingRepository { () => {}, // `answer () => {}, // `estabh`, this.callClosed, // `closeh`, - () => {}, // `metricsh`, + this.metricsReceived, // `metricsh`, this.requestConfig, // `cfg_reqh`, this.audioCbrChanged, // `acbrh`, this.videoStateChanged, // `vstateh`, @@ -407,6 +421,10 @@ export class CallingRepository { activeCall?.muteState(isMuted ? this.nextMuteState : MuteState.NOT_MUTED); }; + private readonly isMLSConference = (conversation: Conversation): conversation is MLSConversation => { + return isMLSConversation(conversation) && this.getConversationType(conversation) === CONV_TYPE.CONFERENCE_MLS; + }; + public async pushClients(call: Call | undefined = this.callState.joinedCall(), checkMismatch?: boolean) { if (!call) { return false; @@ -415,12 +433,15 @@ export class CallingRepository { const allClients = await this.core.service!.conversation.fetchAllParticipantsClients(conversation.qualifiedId); - if (!isGroupMLSConversation(conversation)) { + if (!this.isMLSConference(conversation)) { const qualifiedClients = flattenUserMap(allClients); const clients: Clients = flatten( qualifiedClients.map(({data, userId}) => - data.map(clientid => ({clientid, userid: this.serializeQualifiedId(userId)})), + data.map(clientid => ({ + clientid, + userid: this.serializeQualifiedId(userId), + })), ), ); @@ -656,7 +677,10 @@ export class CallingRepository { private extractTargetedConversationId(event: CallingEvent): QualifiedId { const {targetConversation, conversation, qualified_conversation} = event; const targetedConversationId = targetConversation || qualified_conversation; - const conversationId = targetedConversationId ?? {domain: '', id: conversation}; + const conversationId = targetedConversationId ?? { + domain: '', + id: conversation, + }; return conversationId; } @@ -805,7 +829,7 @@ export class CallingRepository { this.serializeQualifiedId(conversation.qualifiedId), this.serializeQualifiedId(userId), conversation && isMLSConversation(conversation) ? senderClientId : clientId, - conversation && isGroupMLSConversation(conversation) ? CONV_TYPE.CONFERENCE_MLS : CONV_TYPE.CONFERENCE, + conversation && this.getConversationType(conversation), ); if (res !== 0) { @@ -827,17 +851,22 @@ export class CallingRepository { //############################################################################## private getConversationType(conversation: Conversation): CONV_TYPE { - if (!conversation.isGroup()) { - return CONV_TYPE.ONEONONE; - } + const useSFTForOneToOneCalls = + this.teamState.teamFeatures()?.[FEATURE_KEY.CONFERENCE_CALLING]?.config?.useSFTForOneToOneCalls; - if (isGroupMLSConversation(conversation)) { - return CONV_TYPE.CONFERENCE_MLS; + if (conversation.isGroup() || useSFTForOneToOneCalls) { + if (isMLSConversation(conversation)) { + return CONV_TYPE.CONFERENCE_MLS; + } + + return this.supportsConferenceCalling ? CONV_TYPE.CONFERENCE : CONV_TYPE.GROUP; } - return this.supportsConferenceCalling ? CONV_TYPE.CONFERENCE : CONV_TYPE.GROUP; + + return CONV_TYPE.ONEONONE; } async startCall(conversation: Conversation, callType: CALL_TYPE): Promise { + void this.setViewModeMinimized(); if (!this.selfUser || !this.selfClientId) { this.logger.warn( `Calling repository is not initialized correctly \n ${JSON.stringify({ @@ -891,7 +920,7 @@ export class CallingRepository { this.removeCall(call); } - if (isGroupMLSConversation(conversation)) { + if (this.isMLSConference(conversation)) { await this.joinMlsConferenceSubconversation(conversation); } @@ -976,7 +1005,109 @@ export class CallingRepository { } }; + onPageHide = (event: PageTransitionEvent) => { + if (event.persisted) { + return; + } + + this.callState.detachedWindow()?.close(); + }; + + handleThemeUpdateEvent = () => { + const detachedWindow = this.callState.detachedWindow(); + if (detachedWindow) { + detachedWindow.document.body.className = window.document.body.className; + } + }; + + closeDetachedWindow = () => { + this.callState.detachedWindow(null); + this.callState.detachedWindowCallQualifiedId(null); + amplify.unsubscribe(WebAppEvents.PROPERTIES.UPDATE.INTERFACE.THEME, this.handleThemeUpdateEvent); + this.callState.viewMode(CallingViewMode.MINIMIZED); + + const joinedCall = this.callState.joinedCall(); + const isSharingScreen = joinedCall?.getSelfParticipant().sharesScreen(); + const isScreenSharingSourceFromDetachedWindow = this.callState.isScreenSharingSourceFromDetachedWindow(); + + if (joinedCall && isSharingScreen && isScreenSharingSourceFromDetachedWindow) { + this.callState.isScreenSharingSourceFromDetachedWindow(false); + void this.toggleScreenshare(joinedCall); + showAppNotification(t('videoCallScreenShareEnded')); + } + }; + + setViewModeMinimized = () => { + const isDetachedWindowSupported = isDetachedCallingFeatureEnabled(); + + if (!isDetachedWindowSupported) { + this.callState.viewMode(CallingViewMode.MINIMIZED); + return; + } + + this.callState.detachedWindow()?.close(); + this.closeDetachedWindow(); + }; + + setViewModeFullScreen = () => { + this.callState.viewMode(CallingViewMode.FULL_SCREEN); + }; + + async setViewModeDetached( + detachedViewModeOptions: {name: string; height: number; width: number} = { + name: 'WIRE_PICTURE_IN_PICTURE_CALL', + width: 1026, + height: 829, + }, + ) { + if (!isDetachedCallingFeatureEnabled()) { + this.setViewModeFullScreen(); + return; + } + + const {name, width, height} = detachedViewModeOptions; + const {top, left} = calculateChildWindowPosition(height, width); + + const detachedWindow = window.open( + '', + name, + ` + width=${width} + height=${height}, + top=${top}, + left=${left} + location=no, + menubar=no, + resizable=yes, + status=no, + toolbar=no, + `, + ); + + this.callState.detachedWindow(detachedWindow); + + this.callState.detachedWindowCallQualifiedId(this.callState.joinedCall()?.conversation.qualifiedId ?? null); + + if (!detachedWindow) { + return; + } + + // New window is not opened on the same domain (it's about:blank), so we cannot use any of the dom loaded events to copy the styles. + setTimeout(() => copyStyles(window.document, detachedWindow.document), 0); + + detachedWindow.document.title = t('callingPopOutWindowTitle', {brandName: Config.getConfig().BRAND_NAME}); + + detachedWindow.addEventListener('beforeunload', this.closeDetachedWindow); + detachedWindow.addEventListener('pagehide', this.closeDetachedWindow); + window.addEventListener('pagehide', this.onPageHide); + + amplify.subscribe(WebAppEvents.PROPERTIES.UPDATE.INTERFACE.THEME, this.handleThemeUpdateEvent); + + this.callState.viewMode(CallingViewMode.DETACHED_WINDOW); + } + async answerCall(call: Call, callType?: CALL_TYPE): Promise { + void this.setViewModeMinimized(); const {conversation} = call; try { callType ??= call.getSelfParticipant().sharesCamera() ? call.initialType : CALL_TYPE.NORMAL; @@ -1028,7 +1159,7 @@ export class CallingRepository { [Segmentation.CALL.DIRECTION]: this.getCallDirection(call), }); - if (!conversation || !isGroupMLSConversation(conversation)) { + if (!conversation || !this.isMLSConference(conversation)) { return; } @@ -1045,6 +1176,18 @@ export class CallingRepository { return this.conversationState.findConversation(conversationId); } + private readonly leave1on1MLSConference = async (conversationId: QualifiedId) => { + if (isCountlyEnabledAtCurrentEnvironment()) { + this.showCallQualityFeedbackModal(); + } + + await this.subconversationService.leaveConferenceSubconversation(conversationId); + + const conversationIdStr = this.serializeQualifiedId(conversationId); + this.wCall?.end(this.wUser, conversationIdStr); + callingSubscriptions.removeCall(conversationId); + }; + private readonly leaveMLSConference = async (conversationId: QualifiedId) => { await this.subconversationService.leaveConferenceSubconversation(conversationId); }; @@ -1062,7 +1205,7 @@ export class CallingRepository { private readonly updateConferenceSubconversationEpoch = async (conversationId: QualifiedId) => { const conversation = this.getConversationById(conversationId); - if (!conversation || !isGroupMLSConversation(conversation)) { + if (!conversation || !this.isMLSConference(conversation)) { return; } @@ -1081,7 +1224,7 @@ export class CallingRepository { private readonly handleCallParticipantChange = (conversationId: QualifiedId, members: QualifiedWcallMember[]) => { const conversation = this.getConversationById(conversationId); - if (!conversation || !isGroupMLSConversation(conversation)) { + if (!conversation || !this.isMLSConference(conversation)) { return; } @@ -1163,7 +1306,33 @@ export class CallingRepository { this.wCall?.requestVideoStreams(this.wUser, convId, VSTREAMS.LIST, JSON.stringify(payload)); } + readonly showCallQualityFeedbackModal = () => { + if (!this.selfUser || !this.hasActiveCall()) { + return; + } + + const {setQualityFeedbackModalShown} = useCallAlertState.getState(); + + try { + const qualityFeedbackStorage = localStorage.getItem(CALL_QUALITY_FEEDBACK_KEY); + const currentStorageData = qualityFeedbackStorage ? JSON.parse(qualityFeedbackStorage) : {}; + const currentUserDate = currentStorageData?.[this.selfUser.id]; + const currentDate = new Date().getTime(); + + if (currentUserDate === undefined || (currentUserDate !== null && currentDate >= currentUserDate)) { + setQualityFeedbackModalShown(true); + } + } catch (error) { + this.logger.warn(`Storage data can't found: ${(error as Error).message}`); + setQualityFeedbackModalShown(true); + } + }; + readonly leaveCall = (conversationId: QualifiedId, reason: LEAVE_CALL_REASON): void => { + if (isCountlyEnabledAtCurrentEnvironment()) { + this.showCallQualityFeedbackModal(); + } + this.logger.info(`Ending call with reason ${reason} \n Stack trace: `, new Error().stack); const conversationIdStr = this.serializeQualifiedId(conversationId); delete this.poorCallQualityUsers[conversationIdStr]; @@ -1195,12 +1364,8 @@ export class CallingRepository { return this.mediaStreamHandler.requestMediaStream(audio, camera, screen, isGroup).then(stream => { return this.mediaDevicesHandler .initializeMediaDevices(camera) - .then(() => { - return stream; - }) - .catch(() => { - return stream; - }); + .then(() => stream) + .catch(() => stream); }); } @@ -1287,7 +1452,7 @@ export class CallingRepository { const {conversation} = call; if (mediaType === MediaType.AUDIO) { - const audioTracks = mediaStream.getAudioTracks().map(track => track.clone()); + const audioTracks = mediaStream.getAudioTracks(); if (audioTracks.length > 0) { selfParticipant.setAudioStream(new MediaStream(audioTracks), true); this.wCall?.replaceTrack(this.serializeQualifiedId(conversation.qualifiedId), audioTracks[0]); @@ -1428,7 +1593,9 @@ export class CallingRepository { }; readonly sendInCallEmoji = async (emojis: string, call: Call) => { - void this.messageRepository.sendInCallEmoji(call.conversation, {[emojis]: 1}); + void this.messageRepository.sendInCallEmoji(call.conversation, { + [emojis]: 1, + }); }; readonly sendModeratorMute = (conversationId: QualifiedId, participants: Participant[]) => { @@ -1486,13 +1653,30 @@ export class CallingRepository { Warnings.hideWarning(Warnings.TYPE.CALL_QUALITY_POOR); const conversationId = this.parseQualifiedId(convId); const call = this.findCall(conversationId); + const conversation = this.getConversationById(conversationId); if (!call) { return; } + if ( + matchQualifiedIds( + call.conversation.qualifiedId, + this.callState.detachedWindowCallQualifiedId() ?? { + id: '', + domain: '', + }, + ) + ) { + void this.setViewModeMinimized(); + } + // There's nothing we need to do for non-mls calls if (call.conversationType === CONV_TYPE.CONFERENCE_MLS) { - await this.leaveMLSConference(conversationId); + if (!conversation?.is1to1()) { + await this.leaveMLSConference(conversationId); + } else { + await this.leave1on1MLSConference(conversationId); + } } // Remove all the tasks related to the call @@ -1619,7 +1803,11 @@ export class CallingRepository { if (!conversation || !this.selfUser || !this.selfClientId) { this.logger.warn( 'Unable to process incoming call', - JSON.stringify({conversationId, selfClientId: this.selfClientId, selfUser: this.selfUser}), + JSON.stringify({ + conversationId, + selfClientId: this.selfClientId, + selfUser: this.selfUser, + }), ); return; } @@ -1743,6 +1931,7 @@ export class CallingRepository { userId: this.parseQualifiedId(member.userid), })); + this.handleOneToOneMlsCallParticipantLeave(conversationId, members); this.updateParticipantList(call, members); this.updateParticipantMutedState(call, members); this.updateParticipantVideoState(call, members); @@ -1750,6 +1939,43 @@ export class CallingRepository { this.handleCallParticipantChange(conversationId, members); }; + private readonly handleOneToOneMlsCallParticipantLeave = ( + conversationId: QualifiedId, + members: QualifiedWcallMember[], + ) => { + const conversation = this.getConversationById(conversationId); + const call = this.findCall(conversationId); + + if (!conversation || !this.isMLSConference(conversation) || !conversation?.is1to1() || !call) { + return; + } + + const selfParticipant = call.getSelfParticipant(); + + const nextOtherParticipant = members.find( + participant => !matchQualifiedIds(participant.userId, selfParticipant.user.qualifiedId), + ); + + if (!nextOtherParticipant) { + return; + } + + const currentOtherParticipant = call + .participants() + .find(participant => matchQualifiedIds(nextOtherParticipant.userId, participant.user.qualifiedId)); + + if (!currentOtherParticipant) { + return; + } + + const isCurrentlyEstablished = currentOtherParticipant.isAudioEstablished(); + const {aestab: newEstablishedStatus} = nextOtherParticipant; + + if (isCurrentlyEstablished && newEstablishedStatus === AUDIO_STATE.CONNECTING) { + void this.leave1on1MLSConference(conversationId); + } + }; + private readonly requestClients = async (wUser: number, convId: SerializedConversationId, __: number) => { const call = this.findCall(this.parseQualifiedId(convId)); if (!call) { @@ -1759,7 +1985,7 @@ export class CallingRepository { const {conversation} = call; - if (isGroupMLSConversation(conversation)) { + if (conversation && this.isMLSConference(conversation)) { const subconversationEpochInfo = await this.subconversationService.getSubconversationEpochInfo( conversation.qualifiedId, conversation.groupId, @@ -1910,7 +2136,7 @@ export class CallingRepository { const user = this.userRepository.findUserById(userId); if (user) { participant = new Participant(user, remoteClientId); - call.addParticipant(participant); + call?.addParticipant(participant); } } @@ -1985,6 +2211,10 @@ export class CallingRepository { .forEach(participant => participant.videoState(state)); }; + private readonly metricsReceived = (_: string, metrics: string) => { + this.logger.info('Calling metrics:', metrics); + }; + private readonly sendCallingEvent = ( eventName: string, call: Call, diff --git a/src/script/components/AppContainer/AppContainer.tsx b/src/script/components/AppContainer/AppContainer.tsx index 27c9ca6221b..77bd87a2999 100644 --- a/src/script/components/AppContainer/AppContainer.tsx +++ b/src/script/components/AppContainer/AppContainer.tsx @@ -26,10 +26,12 @@ import {StyledApp, THEME_ID} from '@wireapp/react-ui-kit'; import {DetachedCallingCell} from 'Components/calling/DetachedCallingCell'; import {PrimaryModalComponent} from 'Components/Modals/PrimaryModal/PrimaryModal'; +import {QualityFeedbackModal} from 'Components/Modals/QualityFeedbackModal'; import {SIGN_OUT_REASON} from 'src/script/auth/SignOutReason'; import {useAppSoftLock} from 'src/script/hooks/useAppSoftLock'; import {useSingleInstance} from 'src/script/hooks/useSingleInstance'; import {PROPERTIES_TYPE} from 'src/script/properties/PropertiesType'; +import {isDetachedCallingFeatureEnabled} from 'Util/isDetachedCallingFeatureEnabled'; import {useAccentColor} from './hooks/useAccentColor'; import {useTheme} from './hooks/useTheme'; @@ -96,16 +98,19 @@ export const AppContainer: FC = ({config, clientType}) => { return ; }} + + - + {isDetachedCallingFeatureEnabled() && ( + + )} ); }; diff --git a/src/script/components/AppNotification/AppNotification.styles.ts b/src/script/components/AppNotification/AppNotification.styles.ts new file mode 100644 index 00000000000..07424c66de6 --- /dev/null +++ b/src/script/components/AppNotification/AppNotification.styles.ts @@ -0,0 +1,63 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const wrapper: CSSObject = { + alignItems: 'center', + backgroundColor: 'var(--main-color)', + borderRadius: '8px', + boxShadow: '0 2px 16px 0 var(--background-fade-16)', + display: 'flex', + gap: '16px', + padding: '8px 16px', + + position: 'fixed', + top: '0', + left: '50%', + translate: '-50% 0', + transition: 'top 0.3s, opacity 0.3s', + zIndex: '99999', + + 'body.theme-dark &': { + backgroundColor: 'var(--gray-80)', + boxShadow: '0 2px 16px 0 rgba(151, 151, 151, 0.12)', + }, +}; + +export const content: CSSObject = { + color: 'var(--white)', + fontSize: 'var(--font-size-base)', + fontWeight: 'var(--font-weight-regular)', + lineHeight: 'var(--line-height-lg)', +}; + +export const buttonStyles: CSSObject = { + background: 'transparent', + border: 'none', + padding: 0, + + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +}; + +export const closeIcon: CSSObject = { + fill: 'var(--white)', +}; diff --git a/src/script/components/AppNotification/AppNotification.test.tsx b/src/script/components/AppNotification/AppNotification.test.tsx new file mode 100644 index 00000000000..00c91c898a2 --- /dev/null +++ b/src/script/components/AppNotification/AppNotification.test.tsx @@ -0,0 +1,87 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {render, fireEvent, act} from '@testing-library/react'; + +import {AppNotification, showAppNotification} from './AppNotification'; + +jest.useFakeTimers(); + +describe('AppNotification', () => { + afterEach(() => { + jest.clearAllTimers(); + }); + + it('renders the message correctly', () => { + const message = 'Test notification'; + const onClose = jest.fn(); + const {getByText} = render(); + + expect(getByText(message)).toBeDefined(); + }); + + it('calls onClose when the close button is clicked', () => { + const message = 'Test notification'; + const onClose = jest.fn(); + const {getByRole} = render(); + const button = getByRole('button'); + + fireEvent.click(button); + + expect(onClose).toHaveBeenCalledTimes(1); + }); + + it('calls onClose after the notificationTimeout', () => { + const message = 'Test notification'; + const onClose = jest.fn(); + const notificationTimeout = 3000; + + render(); + + act(() => { + jest.advanceTimersByTime(notificationTimeout); + }); + + expect(onClose).toHaveBeenCalledTimes(1); + }); +}); + +describe('showAppNotification', () => { + afterEach(() => { + jest.clearAllTimers(); + const appNotificationContainer = document.querySelector('#app-notification'); + if (appNotificationContainer) { + appNotificationContainer.innerHTML = ''; + } + }); + + it('renders AppNotification into the container', () => { + const appNotificationContainer = document.createElement('div'); + appNotificationContainer.setAttribute('id', 'app-notification'); + document.body.appendChild(appNotificationContainer); + + const message = 'Test notification'; + + act(() => { + showAppNotification(message); + }); + + expect(appNotificationContainer.textContent).toContain(message); + }); +}); diff --git a/src/script/components/AppNotification/AppNotification.tsx b/src/script/components/AppNotification/AppNotification.tsx new file mode 100644 index 00000000000..b0106d304fb --- /dev/null +++ b/src/script/components/AppNotification/AppNotification.tsx @@ -0,0 +1,90 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {useCallback, useEffect, useRef, useState} from 'react'; + +import {createRoot} from 'react-dom/client'; + +import * as Icon from 'Components/Icon'; + +import {buttonStyles, closeIcon, content, wrapper} from './AppNotification.styles'; + +const DEFAULT_NOTIFICATION_TIMEOUT = 3000; +const ANIMATION_DURATION = 300; +const APP_NOTIFICATION_SELECTOR = '#app-notification'; + +interface AppNotificationProps { + message: string; + onClose: () => void; + notificationTimeout?: number; +} + +export const AppNotification = ({ + message, + onClose, + notificationTimeout = DEFAULT_NOTIFICATION_TIMEOUT, +}: AppNotificationProps) => { + const closeTimeoutRef = useRef | null>(null); + + const [isAnimated, setIsAnimated] = useState(false); + const [isClosing, setIsClosing] = useState(false); + + const handleCloseNotification = useCallback(() => { + closeTimeoutRef.current = setTimeout(() => { + setIsClosing(true); + setTimeout(() => { + onClose(); + }, ANIMATION_DURATION); + }, notificationTimeout - ANIMATION_DURATION); + }, [notificationTimeout, onClose]); + + useEffect(() => { + setIsAnimated(true); + handleCloseNotification(); + + return () => { + if (closeTimeoutRef.current) { + clearTimeout(closeTimeoutRef.current); + } + }; + }, [handleCloseNotification]); + + return ( +
+
{message}
+ + +
+ ); +}; + +export const showAppNotification = (message: string) => { + const appNotificationContainer = document.querySelector(APP_NOTIFICATION_SELECTOR); + + if (!appNotificationContainer) { + return; + } + + const root = createRoot(appNotificationContainer); + const closeNotification = () => root.unmount(); + + root.render(); +}; diff --git a/src/style/components/group-list.less b/src/script/components/AppNotification/index.ts similarity index 84% rename from src/style/components/group-list.less rename to src/script/components/AppNotification/index.ts index 9fac8ef92a6..dabe9b7d82b 100644 --- a/src/style/components/group-list.less +++ b/src/script/components/AppNotification/index.ts @@ -1,6 +1,6 @@ /* * Wire - * Copyright (C) 2018 Wire Swiss GmbH + * Copyright (C) 2024 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,10 +17,4 @@ * */ -group-list { - display: block; - - .search-list-item-image { - .flex-center; - } -} +export * from './AppNotification'; diff --git a/src/script/components/Avatar/AvatarImage.tsx b/src/script/components/Avatar/AvatarImage.tsx index ec746b5c2fb..5a6778ebe31 100644 --- a/src/script/components/Avatar/AvatarImage.tsx +++ b/src/script/components/Avatar/AvatarImage.tsx @@ -23,7 +23,7 @@ import {CSSObject} from '@emotion/serialize'; import {Transition} from 'react-transition-group'; import {container} from 'tsyringe'; -import {InViewport} from 'Components/utils/InViewport'; +import {InViewport} from 'Components/InViewport'; import {CSS_FILL_PARENT} from 'Util/CSSMixin'; import {AssetRemoteData} from '../../assets/AssetRemoteData'; diff --git a/src/script/components/Avatar/PlaceholderAvatar/DefaultAvatarImage.tsx b/src/script/components/Avatar/PlaceholderAvatar/DefaultAvatarImage.tsx index a1541b5bc86..aaae4d3ab90 100644 --- a/src/script/components/Avatar/PlaceholderAvatar/DefaultAvatarImage.tsx +++ b/src/script/components/Avatar/PlaceholderAvatar/DefaultAvatarImage.tsx @@ -42,7 +42,7 @@ export function DefaultAvatarImageSmall({diameter}: {diameter: number}) { export function DefaultAvatarImageLarge({diameter}: {diameter: number}) { return ( - + { const {MLS, proteus} = useConversationVerificationState(conversation); diff --git a/src/script/components/VerificationBadge/index.ts b/src/script/components/Badge/components/VerificationBadges/index.ts similarity index 100% rename from src/script/components/VerificationBadge/index.ts rename to src/script/components/Badge/components/VerificationBadges/index.ts diff --git a/src/script/components/Badge/index.tsx b/src/script/components/Badge/index.tsx new file mode 100644 index 00000000000..119fd9ed0f1 --- /dev/null +++ b/src/script/components/Badge/index.tsx @@ -0,0 +1,22 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './components/UserStatusBadges'; +export * from './components/UserBlockedBadge'; +export * from './components/VerificationBadges'; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModal.styles.ts b/src/script/components/BannerPortal/BannerPortal.styles.ts similarity index 67% rename from src/script/components/Modals/PrimaryModal/PrimaryModal.styles.ts rename to src/script/components/BannerPortal/BannerPortal.styles.ts index 2a670c45de4..26186ca5928 100644 --- a/src/script/components/Modals/PrimaryModal/PrimaryModal.styles.ts +++ b/src/script/components/BannerPortal/BannerPortal.styles.ts @@ -1,6 +1,6 @@ /* * Wire - * Copyright (C) 2023 Wire Swiss GmbH + * Copyright (C) 2024 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,13 +19,9 @@ import {CSSObject} from '@emotion/react'; -export const guestLinkPasswordInputStyles: CSSObject = { - flexDirection: 'column', - input: { - borderRadius: 12, - border: '1px solid var(--button-tertiary-border)', - boxShadow: 'none !important', - '&:hover': {boxShadow: 'none'}, - '&:focus': {border: '1px solid var(--accent-color)'}, - }, +export const portalContainerCss: CSSObject = { + zIndex: 1000, + position: 'fixed', + boxShadow: '0px 0px 12px 0px var(--background-fade-32)', + borderRadius: '0.5rem', }; diff --git a/src/script/components/BannerPortal/BannerPortal.tsx b/src/script/components/BannerPortal/BannerPortal.tsx new file mode 100644 index 00000000000..5d4198e9604 --- /dev/null +++ b/src/script/components/BannerPortal/BannerPortal.tsx @@ -0,0 +1,69 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {ReactNode, useEffect, useRef} from 'react'; + +import {createPortal} from 'react-dom'; + +import {useActiveWindowState} from 'src/script/hooks/useActiveWindow'; + +import {portalContainerCss} from './BannerPortal.styles'; + +interface Props { + onClose: () => void; + positionX?: number; + positionY?: number; + children: ReactNode; +} + +export const BannerPortal = ({onClose, positionX = 0, positionY = 0, children}: Props) => { + const bannerRef = useRef(null); + + const {activeWindow} = useActiveWindowState.getState(); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (bannerRef.current && !bannerRef.current.contains(event.target as Node)) { + onClose(); + } + }; + + activeWindow.document.addEventListener('mousedown', handleClickOutside); + return () => { + activeWindow.document.removeEventListener('mousedown', handleClickOutside); + }; + }, [activeWindow.document, onClose]); + + const updateRef = (element: HTMLDivElement) => { + bannerRef.current = element; + + if (!element) { + return; + } + + element.style.top = `${positionY - element.clientHeight}px`; + }; + + return createPortal( +
+ {children} +
, + activeWindow.document.body, + ); +}; diff --git a/src/script/components/Checkbox/Checkbox.styles.ts b/src/script/components/Checkbox/Checkbox.styles.ts deleted file mode 100644 index ba2058d4c58..00000000000 --- a/src/script/components/Checkbox/Checkbox.styles.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Wire - * Copyright (C) 2022 Wire Swiss GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - */ - -import {CSSObject} from '@emotion/react'; - -export const getLabelCSS = (disabled?: boolean): CSSObject => ({ - '&:hover': { - cursor: !disabled && 'pointer', - }, - '&:hover svg': { - borderColor: !disabled && 'var(--accent-color-500)', - }, - alignItems: 'center', - display: 'flex', - fontSize: '', -}); - -export const getInputCSS = (isChecked: boolean, disabled?: boolean): CSSObject => ({ - '&:active + svg': { - borderColor: !disabled && 'var(--accent-color-500)', - }, - '&:focus + svg, &:focus-visible + svg': { - backgroundColor: isChecked && 'var(--accent-color-600)', - borderColor: 'var(--accent-color-600)', - outline: '1px solid var(--accent-color-700)', - }, - clip: 'rect(0 0 0 0)', - clipPath: 'inset(50%)', - height: 1, - overflow: 'hidden', - position: 'absolute', - whiteSpace: 'nowrap', - width: 1, -}); - -export const getInputCSSDark = (isChecked: boolean, disabled?: boolean): CSSObject => ({ - 'body.theme-dark .input-dark': { - '& + svg': { - ...(isChecked && { - background: 'var(--accent-color-600)', - borderColor: 'var(--accent-color-600)', - }), - }, - '&:active + svg': { - borderColor: !disabled && 'var(--accent-color-600)', - }, - '&:focus + svg, &:focus-visible + svg': { - backgroundColor: isChecked && 'var(--accent-color-700)', - borderColor: 'var(--accent-color-700)', - outline: '1px solid var(--accent-color-700)', - }, - }, -}); - -export const getSvgCSS = (isChecked: boolean, disabled?: boolean): CSSObject => ({ - background: 'var(--checkbox-background)', - border: '1.5px var(--checkbox-border) solid', - borderRadius: 3, - - // set to `inline-block` as `inline elements ignore `height` and `width` - display: 'inline-block', - height: 20, - marginRight: 8, - width: 20, - ...(isChecked && { - background: 'var(--accent-color-500)', - borderColor: 'var(--accent-color-500)', - }), - ...(disabled && { - background: isChecked ? 'var(--checkbox-background-disabled-selected)' : 'var(--checkbox-background-disabled)', - borderColor: 'var(--checkbox-border-disabled)', - pointerEvents: 'none', - }), -}); diff --git a/src/script/components/Checkbox/Checkbox.tsx b/src/script/components/Checkbox/Checkbox.tsx deleted file mode 100644 index f6d43ce076c..00000000000 --- a/src/script/components/Checkbox/Checkbox.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Wire - * Copyright (C) 2018 Wire Swiss GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - */ - -import React from 'react'; - -import {Global} from '@emotion/react'; - -import {getInputCSS, getInputCSSDark, getLabelCSS, getSvgCSS} from './Checkbox.styles'; - -interface CheckboxProps { - disabled?: boolean; - isChecked: boolean; - label: string; - name: string; - onCheckedChanged: () => void; - uieName: string; -} - -const Checkbox: React.FC = ({disabled, label, isChecked, name, onCheckedChanged, uieName}) => { - return ( - <> - - - - - ); -}; - -export {Checkbox}; diff --git a/src/script/components/input/ClassifiedBar.test.tsx b/src/script/components/ClassifiedBar/ClassifiedBar.test.tsx similarity index 100% rename from src/script/components/input/ClassifiedBar.test.tsx rename to src/script/components/ClassifiedBar/ClassifiedBar.test.tsx diff --git a/src/script/components/input/ClassifiedBar.tsx b/src/script/components/ClassifiedBar/ClassifiedBar.tsx similarity index 89% rename from src/script/components/input/ClassifiedBar.tsx rename to src/script/components/ClassifiedBar/ClassifiedBar.tsx index c261f2e7ea2..c4adaa66161 100644 --- a/src/script/components/input/ClassifiedBar.tsx +++ b/src/script/components/ClassifiedBar/ClassifiedBar.tsx @@ -17,8 +17,6 @@ * */ -import React from 'react'; - import {CSSObject} from '@emotion/react'; import cx from 'classnames'; @@ -50,12 +48,7 @@ interface UserClassifiedBarProps extends ClassifiedBarProps { users: User[]; } -export const UserClassifiedBar: React.FC = ({ - users, - conversationDomain, - classifiedDomains, - style, -}) => { +export const UserClassifiedBar = ({users, conversationDomain, classifiedDomains, style}: UserClassifiedBarProps) => { if (typeof classifiedDomains === 'undefined') { return null; } @@ -79,10 +72,7 @@ interface ConversationClassifiedBarProps extends ClassifiedBarProps { conversation: Conversation; } -export const ConversationClassifiedBar: React.FC = ({ - conversation, - ...classifiedBarProps -}) => { +export const ConversationClassifiedBar = ({conversation, ...classifiedBarProps}: ConversationClassifiedBarProps) => { const {allUserEntities: users} = useKoSubscribableChildren(conversation, ['allUserEntities']); return ; }; diff --git a/src/script/components/ConfigToolbar/ConfigToolbar.styles.tsx b/src/script/components/ConfigToolbar/ConfigToolbar.styles.tsx new file mode 100644 index 00000000000..758bea4dade --- /dev/null +++ b/src/script/components/ConfigToolbar/ConfigToolbar.styles.tsx @@ -0,0 +1,33 @@ +/* + * Wire + * Copyright (C) 2022 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const wrapperStyles: CSSObject = { + position: 'fixed', + top: 0, + right: 0, + width: '400px', + height: '100vh', + backgroundColor: 'var(--sidebar-bg)', + padding: '20px', + overflow: 'auto', + zIndex: 100000000, + boxShadow: '3px 1px 6px 1px #000', +}; diff --git a/src/script/components/ConfigToolbar/ConfigToolbar.tsx b/src/script/components/ConfigToolbar/ConfigToolbar.tsx new file mode 100644 index 00000000000..d8dda991200 --- /dev/null +++ b/src/script/components/ConfigToolbar/ConfigToolbar.tsx @@ -0,0 +1,192 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {useState, useEffect, useRef} from 'react'; + +import keyboardjs from 'keyboardjs'; +import {container} from 'tsyringe'; + +import {Button, Input, Switch} from '@wireapp/react-ui-kit'; + +import {Config, Configuration} from 'src/script/Config'; +import {ConversationState} from 'src/script/conversation/ConversationState'; +import {useClickOutside} from 'src/script/hooks/useClickOutside'; + +import {wrapperStyles} from './ConfigToolbar.styles'; + +export function ConfigToolbar() { + const [showConfig, setShowConfig] = useState(false); + const [configFeaturesState, setConfigFeaturesState] = useState(Config.getConfig().FEATURE); + const [intervalId, setIntervalId] = useState(null); // For managing setInterval + const messageCountRef = useRef(0); // For the message count + const [prefix, setPrefix] = useState('Message -'); // Prefix input + const wrapperRef = useRef(null); + + // Toggle config tool on 'cmd/ctrl + shift + 2' + useEffect(() => { + const handleKeyDown = () => { + setShowConfig(prev => !prev); + }; + + keyboardjs.bind(['command+shift+2', 'ctrl+shift+2'], handleKeyDown); + + return () => { + keyboardjs.unbind(['command+shift+2', 'ctrl+shift+2'], handleKeyDown); + }; + }, []); + + const startSendingMessages = () => { + if (intervalId) { + return; + } + + let isRequestInProgress = false; + + const id = window.setInterval(async () => { + if (isRequestInProgress) { + return; + } + + const conversationState = container.resolve(ConversationState); + const activeConversation = conversationState?.activeConversation(); + if (!activeConversation) { + return; + } + + isRequestInProgress = true; + + try { + await window.wire.app.repository.message.sendTextWithLinkPreview( + activeConversation, + `${prefix} ${messageCountRef.current}`, + [], + undefined, + ); + + messageCountRef.current++; + } catch (error) { + console.error('Error sending message:', error); + } finally { + isRequestInProgress = false; + } + }, 100); + + setIntervalId(id); + }; + + // Stop sending messages and reset the counter + const stopSendingMessages = () => { + if (intervalId) { + clearInterval(intervalId); + setIntervalId(null); + messageCountRef.current = 0; + } + }; + + // Update the config state when form input changes + const handleChange = (path: string, value: string | boolean | string[]) => { + const updateConfig = (obj: any, keys: string[]): void => { + if (keys.length === 1) { + obj[keys[0]] = value; + } else { + updateConfig(obj[keys[0]], keys.slice(1)); + } + }; + + const updatedConfig = {...configFeaturesState}; + updateConfig(updatedConfig, path.split('.')); + setConfigFeaturesState(updatedConfig); + Config._dangerouslySetConfigFeaturesForDebug(updatedConfig); + }; + + const renderInput = (value: string | boolean | string[] | number | object | null, path: string) => { + if (typeof value === 'boolean') { + return handleChange(path, isChecked)} />; + } + + if (Array.isArray(value)) { + return ( + + handleChange( + path, + event.currentTarget.value.split(',').map(value => value.trim()), + ) + } + /> + ); + } + + if (typeof value === 'object' && value !== null) { + return renderConfig(value, path); + } + + return ( + handleChange(path, event.currentTarget.value)} /> + ); + }; + + const renderConfig = (configObj: object, parentPath: string = '') => { + const entries = Object.entries(configObj); + + return entries.map(([key, value]) => { + const path = parentPath ? `${parentPath}.${key}` : key; + return ( +
+ + {renderInput(value, path)} +
+ ); + }); + }; + + useClickOutside(wrapperRef, () => setShowConfig(false)); + + if (!showConfig) { + return null; + } + + return ( +
+

Configuration Tool

+

+ Caution: Modifying these settings can affect the behavior of the application. Ensure you understand the + implications of each change before proceeding. Changes may cause unexpected behavior. +

+
{renderConfig(configFeaturesState)}
+ +

Debug Functions

+ + + + +

Message Automation

+ setPrefix(event.currentTarget.value)} + placeholder="Prefix for the messages" + /> + + +
+ ); +} diff --git a/src/script/components/ConnectRequests/ConnectionRequests.tsx b/src/script/components/ConnectRequests/ConnectionRequests.tsx index e1a030523ca..1f91ee603f1 100644 --- a/src/script/components/ConnectRequests/ConnectionRequests.tsx +++ b/src/script/components/ConnectRequests/ConnectionRequests.tsx @@ -24,7 +24,7 @@ import {container} from 'tsyringe'; import {Button, ButtonVariant, IconButton, IconButtonVariant, useMatchMedia} from '@wireapp/react-ui-kit'; import {Avatar, AVATAR_SIZE} from 'Components/Avatar'; -import {UserClassifiedBar} from 'Components/input/ClassifiedBar'; +import {UserClassifiedBar} from 'Components/ClassifiedBar/ClassifiedBar'; import {UnverifiedUserWarning} from 'Components/Modals/UserModal'; import {UserName} from 'Components/UserName'; import {SidebarTabs, useSidebarStore} from 'src/script/page/LeftSidebar/panels/Conversations/useSidebarStore'; diff --git a/src/script/components/Conversation/Conversation.tsx b/src/script/components/Conversation/Conversation.tsx index da588116975..c779fc93a67 100644 --- a/src/script/components/Conversation/Conversation.tsx +++ b/src/script/components/Conversation/Conversation.tsx @@ -33,7 +33,7 @@ import {showDetailViewModal} from 'Components/Modals/DetailViewModal'; import {PrimaryModal} from 'Components/Modals/PrimaryModal'; import {showWarningModal} from 'Components/Modals/utils/showWarningModal'; import {TitleBar} from 'Components/TitleBar'; -import {CallingViewMode, CallState} from 'src/script/calling/CallState'; +import {CallState} from 'src/script/calling/CallState'; import {Config} from 'src/script/Config'; import {PROPERTIES_TYPE} from 'src/script/properties/PropertiesType'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; @@ -117,9 +117,7 @@ export const Conversation = ({ const inTeam = teamState.isInTeam(selfUser); - const {activeCalls, viewMode} = useKoSubscribableChildren(callState, ['activeCalls', 'viewMode']); - - const isCallWindowDetached = viewMode === CallingViewMode.DETACHED_WINDOW; + const {activeCalls} = useKoSubscribableChildren(callState, ['activeCalls']); const [isMsgElementsFocusable, setMsgElementsFocusable] = useState(true); @@ -495,22 +493,17 @@ export const Conversation = ({ return null; } - if (isCallWindowDetached) { - return null; - } - return ( -
- -
+ ); })} diff --git a/src/script/components/list/ConversationListCell.tsx b/src/script/components/ConversationListCell/ConversationListCell.tsx similarity index 60% rename from src/script/components/list/ConversationListCell.tsx rename to src/script/components/ConversationListCell/ConversationListCell.tsx index a28fc2d685a..25384973b43 100644 --- a/src/script/components/list/ConversationListCell.tsx +++ b/src/script/components/ConversationListCell/ConversationListCell.tsx @@ -30,14 +30,14 @@ import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; import cx from 'classnames'; import {Avatar, AVATAR_SIZE, GroupAvatar} from 'Components/Avatar'; -import {UserBlockedBadge} from 'Components/UserBlockedBadge/UserBlockedBadge'; +import {UserBlockedBadge} from 'Components/Badge'; import {UserInfo} from 'Components/UserInfo'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {isKey, isOneOfKeys, KEY} from 'Util/KeyboardUtil'; import {t} from 'Util/LocalizerUtil'; import {noop, setContextMenuPosition} from 'Util/util'; -import {ConversationListCellStatusIcon} from './ConversationListCellStatusIcon'; +import {StatusIcon} from './components/StatusIcon'; import {generateCellState} from '../../conversation/ConversationCellState'; import type {Conversation} from '../../entity/Conversation'; @@ -57,7 +57,7 @@ export interface ConversationListCellProps { resetConversationFocus: () => void; } -const ConversationListCell = ({ +export const ConversationListCell = ({ showJoinButton, conversation, onJoinCall, @@ -155,95 +155,96 @@ const ConversationListCell = ({ }, [isFocused]); return ( -
  • +
  • { + event.stopPropagation(); + event.preventDefault(); + onClick(event); + }} + onKeyDown={handleDivKeyDown} + data-uie-name="go-open-conversation" + tabIndex={isFocused ? TabIndex.FOCUSABLE : TabIndex.UNFOCUSABLE} + aria-label={t('accessibility.openConversation', displayName)} + aria-describedby={contextMenuKeyboardShortcut} > + +
    - + {isGroup && } -
    - {isGroup && } - - {!isGroup && !!users.length && } -
    - -
    - {is1to1 ? ( - - {isConversationWithBlockedUser && } - - ) : ( - - {displayName} - - )} - - {cellState.description && ( - - {cellState.description} - - )} -
    + {!isGroup && !!users.length && }
    -
    - + {cellState.description} + )}
    + +
    + + )} +
  • ); }; - -export {ConversationListCell}; diff --git a/src/script/components/list/ConversationListCellStatusIcon.tsx b/src/script/components/ConversationListCell/components/StatusIcon/StatusIcon.tsx similarity index 89% rename from src/script/components/list/ConversationListCellStatusIcon.tsx rename to src/script/components/ConversationListCell/components/StatusIcon/StatusIcon.tsx index 92fd6269de1..eabd7cb05ae 100644 --- a/src/script/components/list/ConversationListCellStatusIcon.tsx +++ b/src/script/components/ConversationListCell/components/StatusIcon/StatusIcon.tsx @@ -23,15 +23,15 @@ import * as Icon from 'Components/Icon'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {t} from 'Util/LocalizerUtil'; -import {generateCellState} from '../../conversation/ConversationCellState'; -import {ConversationStatusIcon} from '../../conversation/ConversationStatusIcon'; -import type {Conversation} from '../../entity/Conversation'; +import {generateCellState} from '../../../../conversation/ConversationCellState'; +import {ConversationStatusIcon} from '../../../../conversation/ConversationStatusIcon'; +import type {Conversation} from '../../../../entity/Conversation'; -export interface ConversationListCellStatusIconProps { +export interface Props { conversation: Conversation; } -const ConversationListCellStatusIcon = ({conversation}: ConversationListCellStatusIconProps) => { +export const StatusIcon = ({conversation}: Props) => { const {unreadState, mutedState, isRequest} = useKoSubscribableChildren(conversation, [ 'unreadState', 'mutedState', @@ -115,5 +115,3 @@ const ConversationListCellStatusIcon = ({conversation}: ConversationListCellStat ); }; - -export {ConversationListCellStatusIcon}; diff --git a/src/script/components/ConversationListCell/components/StatusIcon/index.tsx b/src/script/components/ConversationListCell/components/StatusIcon/index.tsx new file mode 100644 index 00000000000..f65239a6aca --- /dev/null +++ b/src/script/components/ConversationListCell/components/StatusIcon/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './StatusIcon'; diff --git a/src/script/components/ConversationListCell/index.tsx b/src/script/components/ConversationListCell/index.tsx new file mode 100644 index 00000000000..2efaa44e55b --- /dev/null +++ b/src/script/components/ConversationListCell/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './ConversationListCell'; diff --git a/src/script/components/CopyToClipboardButton.test.tsx b/src/script/components/CopyToClipboardButton/CopyToClipboardButton.test.tsx similarity index 97% rename from src/script/components/CopyToClipboardButton.test.tsx rename to src/script/components/CopyToClipboardButton/CopyToClipboardButton.test.tsx index b7a687eed73..2e834c9b9b4 100644 --- a/src/script/components/CopyToClipboardButton.test.tsx +++ b/src/script/components/CopyToClipboardButton/CopyToClipboardButton.test.tsx @@ -21,7 +21,7 @@ import {render, waitFor, act} from '@testing-library/react'; import {CopyToClipboardButton} from './CopyToClipboardButton'; -import {withTheme} from '../auth/util/test/TestUtil'; +import {withTheme} from '../../auth/util/test/TestUtil'; jest.mock('Util/ClipboardUtil', () => ({ copyText: jest.fn(), diff --git a/src/script/components/CopyToClipboardButton.tsx b/src/script/components/CopyToClipboardButton/CopyToClipboardButton.tsx similarity index 92% rename from src/script/components/CopyToClipboardButton.tsx rename to src/script/components/CopyToClipboardButton/CopyToClipboardButton.tsx index 9ce4390f788..aa43504f6c4 100644 --- a/src/script/components/CopyToClipboardButton.tsx +++ b/src/script/components/CopyToClipboardButton/CopyToClipboardButton.tsx @@ -17,7 +17,7 @@ * */ -import React, {useState} from 'react'; +import {useState} from 'react'; import {Button, ButtonVariant} from '@wireapp/react-ui-kit'; @@ -34,13 +34,13 @@ interface CopyToClipboardButtonProps { const COPY_CONFIRM_DURATION = 1500; -const CopyToClipboardButton: React.FC = ({ +export const CopyToClipboardButton = ({ disabled, textToCopy, displayText, copySuccessText, onCopySuccess, -}) => { +}: CopyToClipboardButtonProps) => { const [isCopying, setIsCopying] = useState(false); const copyToClipboard = async () => { @@ -67,5 +67,3 @@ const CopyToClipboardButton: React.FC = ({ ); }; - -export {CopyToClipboardButton}; diff --git a/src/script/components/CopyToClipboardButton/index.tsx b/src/script/components/CopyToClipboardButton/index.tsx new file mode 100644 index 00000000000..b580643cb12 --- /dev/null +++ b/src/script/components/CopyToClipboardButton/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './CopyToClipboardButton'; diff --git a/src/script/components/DetachedWindow/DetachedWindow.tsx b/src/script/components/DetachedWindow/DetachedWindow.tsx deleted file mode 100644 index e42c3476ceb..00000000000 --- a/src/script/components/DetachedWindow/DetachedWindow.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Wire - * Copyright (C) 2024 Wire Swiss GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - */ - -import {useEffect, useMemo} from 'react'; - -import createCache from '@emotion/cache'; -import {CacheProvider} from '@emotion/react'; -import weakMemoize from '@emotion/weak-memoize'; -import {createPortal} from 'react-dom'; - -import {StyledApp, THEME_ID} from '@wireapp/react-ui-kit'; - -import {useActiveWindow} from 'src/script/hooks/useActiveWindow'; -import {calculateChildWindowPosition} from 'Util/DOM/caculateChildWindowPosition'; - -import '../../../style/default.less'; - -interface DetachedWindowProps { - children: React.ReactNode; - width?: number; - height?: number; - onClose: () => void; - name: string; -} - -const memoizedCreateCacheWithContainer = weakMemoize((container: HTMLHeadElement) => { - const newCache = createCache({container, key: 'detached-window'}); - return newCache; -}); - -export const DetachedWindow = ({children, name, onClose, width = 600, height = 600}: DetachedWindowProps) => { - const newWindow = useMemo(() => { - const {top, left} = calculateChildWindowPosition(height, width); - - return window.open( - '', - name, - ` - width=${width} - height=${height}, - top=${top}, - left=${left} - location=no, - menubar=no, - resizable=no, - status=no, - toolbar=no, - `, - ); - }, [height, name, width]); - - useActiveWindow(newWindow); - - useEffect(() => { - if (!newWindow) { - return () => {}; - } - - //New window is not opened on the same domain (it's about:blank), so we cannot use any of the dom loaded events to copy the styles. - setTimeout(() => copyStyles(window.document, newWindow.document), 0); - - newWindow.document.title = window.document.title; - - const onPageHide = (event: PageTransitionEvent) => { - if (event.persisted) { - return; - } - onClose(); - newWindow.close(); - }; - - newWindow.addEventListener('beforeunload', onClose); - window.addEventListener('pagehide', onPageHide); - - return () => { - newWindow.close(); - newWindow.removeEventListener('beforeunload', onClose); - window.removeEventListener('pagehide', onPageHide); - }; - }, [height, name, width, onClose, newWindow]); - - return !newWindow - ? null - : createPortal( - - - {children} - - , - newWindow.document.body, - ); -}; - -/** - * Copy styles from one document to another - link, style elements and body element class names. - * @param source the source document object - * @param target the target document object - */ -const copyStyles = (source: Document, target: Document) => { - source.head.querySelectorAll('link, style').forEach(htmlElement => { - target.head.appendChild(htmlElement.cloneNode(true)); - }); - - target.body.className = source.body.className; - target.body.style.height = '100%'; -}; diff --git a/src/script/components/DragableClickWrapper.tsx b/src/script/components/DraggableClickWrapper/DraggableClickWrapper.tsx similarity index 91% rename from src/script/components/DragableClickWrapper.tsx rename to src/script/components/DraggableClickWrapper/DraggableClickWrapper.tsx index 95c94775f82..a8a4e260ddf 100644 --- a/src/script/components/DragableClickWrapper.tsx +++ b/src/script/components/DraggableClickWrapper/DraggableClickWrapper.tsx @@ -21,12 +21,12 @@ import React from 'react'; import {Runtime} from '@wireapp/commons'; -interface DragableClickWrapperProps { +interface DraggableClickWrapperProps { children: React.ReactElement; onClick: React.MouseEventHandler; } -const DragableClickWrapper: React.FC = ({onClick, children}) => { +export const DraggableClickWrapper = ({onClick, children}: DraggableClickWrapperProps) => { const isMacDesktop = Runtime.isDesktopApp() && Runtime.isMacOS(); if (!isMacDesktop) { return React.cloneElement(children, {onClick}); @@ -61,5 +61,3 @@ const DragableClickWrapper: React.FC = ({onClick, chi }, }); }; - -export {DragableClickWrapper}; diff --git a/src/script/components/DraggableClickWrapper/index.tsx b/src/script/components/DraggableClickWrapper/index.tsx new file mode 100644 index 00000000000..3d60d425c93 --- /dev/null +++ b/src/script/components/DraggableClickWrapper/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './DraggableClickWrapper'; diff --git a/src/script/components/ErrorFallback.test.tsx b/src/script/components/ErrorFallback/ErrorFallback.test.tsx similarity index 95% rename from src/script/components/ErrorFallback.test.tsx rename to src/script/components/ErrorFallback/ErrorFallback.test.tsx index ad62144c679..9b90a72fc8a 100644 --- a/src/script/components/ErrorFallback.test.tsx +++ b/src/script/components/ErrorFallback/ErrorFallback.test.tsx @@ -20,8 +20,9 @@ import {render} from '@testing-library/react'; import {ErrorBoundary} from 'react-error-boundary'; +import {PrimaryModal} from 'Components/Modals/PrimaryModal'; + import {ErrorFallback} from './ErrorFallback'; -import {PrimaryModal} from './Modals/PrimaryModal'; const SimpleError: React.FC = () => { throw new Error('failed to render'); diff --git a/src/script/components/ErrorFallback.tsx b/src/script/components/ErrorFallback/ErrorFallback.tsx similarity index 86% rename from src/script/components/ErrorFallback.tsx rename to src/script/components/ErrorFallback/ErrorFallback.tsx index e1764d98e64..4685f559b06 100644 --- a/src/script/components/ErrorFallback.tsx +++ b/src/script/components/ErrorFallback/ErrorFallback.tsx @@ -17,18 +17,17 @@ * */ -import React, {useEffect} from 'react'; +import {useEffect} from 'react'; import {FallbackProps} from 'react-error-boundary'; +import {PrimaryModal} from 'Components/Modals/PrimaryModal'; import {t} from 'Util/LocalizerUtil'; import {getLogger} from 'Util/Logger'; -import {PrimaryModal} from './Modals/PrimaryModal'; - const logger = getLogger('ErrorFallback'); -const ErrorFallback: React.FC = ({error, resetErrorBoundary}) => { +export const ErrorFallback = ({error, resetErrorBoundary}: FallbackProps) => { useEffect(() => { logger.error(error); PrimaryModal.show(PrimaryModal.type.CONFIRM, { @@ -48,7 +47,5 @@ const ErrorFallback: React.FC = ({error, resetErrorBoundary}) => }); }, [error, resetErrorBoundary]); - return null; + return <>; }; - -export {ErrorFallback}; diff --git a/src/script/components/ErrorFallback/index.tsx b/src/script/components/ErrorFallback/index.tsx new file mode 100644 index 00000000000..b45b7606a47 --- /dev/null +++ b/src/script/components/ErrorFallback/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './ErrorFallback'; diff --git a/src/script/components/Image/Image.tsx b/src/script/components/Image/Image.tsx index 15ac207720b..6443122690e 100644 --- a/src/script/components/Image/Image.tsx +++ b/src/script/components/Image/Image.tsx @@ -23,6 +23,7 @@ import {CSSObject} from '@emotion/react'; import cx from 'classnames'; import {container} from 'tsyringe'; +import {InViewport} from 'Components/InViewport'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {getImageStyle, getWrapperStyles} from './Image.styles'; @@ -33,7 +34,6 @@ import {Config} from '../../Config'; import {MediumImage} from '../../entity/message/MediumImage'; import {TeamState} from '../../team/TeamState'; import {AssetUrl, useAssetTransfer} from '../MessagesList/Message/ContentMessage/asset/useAssetTransfer'; -import {InViewport} from '../utils/InViewport'; interface BaseImageProps extends React.HTMLProps { alt?: string; diff --git a/src/script/components/utils/InViewport.tsx b/src/script/components/InViewport/InViewport.tsx similarity index 96% rename from src/script/components/utils/InViewport.tsx rename to src/script/components/InViewport/InViewport.tsx index 9b8cc424934..c3280e54f1a 100644 --- a/src/script/components/utils/InViewport.tsx +++ b/src/script/components/InViewport/InViewport.tsx @@ -32,7 +32,7 @@ interface InViewportParams { checkOverlay?: boolean; } -const InViewport: React.FC> = ({ +export const InViewport = ({ children, onVisible, onVisibilityLost, @@ -41,7 +41,7 @@ const InViewport: React.FC> = allowBiggerThanViewport = false, callVisibilityLostOnUnmount = false, ...props -}) => { +}: InViewportParams & React.HTMLProps) => { const domNode = useRef(null); useEffect(() => { @@ -116,5 +116,3 @@ const InViewport: React.FC> = ); }; - -export {InViewport}; diff --git a/src/script/components/InViewport/index.tsx b/src/script/components/InViewport/index.tsx new file mode 100644 index 00000000000..eebd5fda190 --- /dev/null +++ b/src/script/components/InViewport/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './InViewport'; diff --git a/src/script/components/InputBar/InputBar.test.tsx b/src/script/components/InputBar/InputBar.test.tsx index bf245ded4b5..35050c94386 100644 --- a/src/script/components/InputBar/InputBar.test.tsx +++ b/src/script/components/InputBar/InputBar.test.tsx @@ -28,7 +28,6 @@ import {createUuid} from 'Util/uuid'; import {TestFactory} from '../../../../test/helper/TestFactory'; import {AssetRepository} from '../../assets/AssetRepository'; -import {AssetService} from '../../assets/AssetService'; import {ConversationRepository} from '../../conversation/ConversationRepository'; import {MessageRepository} from '../../conversation/MessageRepository'; import {Conversation} from '../../entity/Conversation'; @@ -71,7 +70,7 @@ describe('InputBar', () => { let propertiesRepository: PropertiesRepository; const getDefaultProps = () => ({ - assetRepository: new AssetRepository(new AssetService()), + assetRepository: new AssetRepository(), conversation: new Conversation(createUuid()), conversationRepository: { sendTypingStart: jest.fn(), diff --git a/src/script/components/InputBar/InputBar.tsx b/src/script/components/InputBar/InputBar.tsx index 130c6e85942..0e0454d98cb 100644 --- a/src/script/components/InputBar/InputBar.tsx +++ b/src/script/components/InputBar/InputBar.tsx @@ -28,8 +28,8 @@ import {useMatchMedia} from '@wireapp/react-ui-kit'; import {WebAppEvents} from '@wireapp/webapp-events'; import {Avatar, AVATAR_SIZE} from 'Components/Avatar'; +import {ConversationClassifiedBar} from 'Components/ClassifiedBar/ClassifiedBar'; import {checkFileSharingPermission} from 'Components/Conversation/utils/checkFileSharingPermission'; -import {ConversationClassifiedBar} from 'Components/input/ClassifiedBar'; import {PrimaryModal} from 'Components/Modals/PrimaryModal'; import {showWarningModal} from 'Components/Modals/utils/showWarningModal'; import {RichTextContent, RichTextEditor} from 'Components/RichTextEditor'; diff --git a/src/script/components/InputBar/components/MessageTimerButton/MessageTimerButton.tsx b/src/script/components/InputBar/components/MessageTimerButton/MessageTimerButton.tsx index f6dbeda460c..994ff99b09a 100644 --- a/src/script/components/InputBar/components/MessageTimerButton/MessageTimerButton.tsx +++ b/src/script/components/InputBar/components/MessageTimerButton/MessageTimerButton.tsx @@ -74,7 +74,7 @@ const MessageTimerButton: React.FC = ({ // Click on ephemeral button const onClick = (event: React.MouseEvent): void => { const entries = setEntries(); - showContextMenu(event, entries, 'message-timer-menu'); + showContextMenu({event, entries, identifier: 'message-timer-menu'}); }; if (!isSelfDeletingMessagesEnabled) { @@ -85,7 +85,7 @@ const MessageTimerButton: React.FC = ({ if (isSpaceOrEnterKey(event.key)) { const newEvent = setContextMenuPosition(event); const entries = setEntries(); - showContextMenu(newEvent, entries, 'message-timer-menu'); + showContextMenu({event: newEvent, entries, identifier: 'message-timer-menu'}); } }; diff --git a/src/script/components/LegalHoldDot.test.tsx b/src/script/components/LegalHoldDot/LegalHoldDot.test.tsx similarity index 100% rename from src/script/components/LegalHoldDot.test.tsx rename to src/script/components/LegalHoldDot/LegalHoldDot.test.tsx diff --git a/src/script/components/LegalHoldDot.tsx b/src/script/components/LegalHoldDot/LegalHoldDot.tsx similarity index 94% rename from src/script/components/LegalHoldDot.tsx rename to src/script/components/LegalHoldDot/LegalHoldDot.tsx index 1f8569a8435..487c95f6ab6 100644 --- a/src/script/components/LegalHoldDot.tsx +++ b/src/script/components/LegalHoldDot/LegalHoldDot.tsx @@ -25,7 +25,7 @@ import * as Icon from 'Components/Icon'; import {useLegalHoldModalState} from 'Components/Modals/LegalHoldModal/LegalHoldModal.state'; import {t} from 'Util/LocalizerUtil'; -import type {Conversation} from '../entity/Conversation'; +import type {Conversation} from '../../entity/Conversation'; export interface LegalHoldDotProps { isInteractive?: boolean; @@ -38,7 +38,7 @@ export interface LegalHoldDotProps { showText?: boolean; } -const LegalHoldDot: React.FC = ({ +export const LegalHoldDot = ({ isInteractive = false, conversation, isPending, @@ -47,7 +47,7 @@ const LegalHoldDot: React.FC = ({ showText = false, className = '', dataUieName = 'legal-hold-dot-pending-icon', -}) => { +}: LegalHoldDotProps) => { const {showRequestModal, showUsers} = useLegalHoldModalState(state => state); const onClick = (event: React.MouseEvent) => { @@ -90,5 +90,3 @@ const LegalHoldDot: React.FC = ({ ); }; - -export {LegalHoldDot}; diff --git a/src/script/components/LegalHoldDot/index.tsx b/src/script/components/LegalHoldDot/index.tsx new file mode 100644 index 00000000000..2cbbe0c5d78 --- /dev/null +++ b/src/script/components/LegalHoldDot/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './LegalHoldDot'; diff --git a/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.styles.ts b/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.styles.ts index bd0ddaff80e..408df55be7a 100644 --- a/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.styles.ts +++ b/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.styles.ts @@ -34,7 +34,7 @@ export const messageBodyActions: CSSObject = { right: '-40px', top: '-34px', userSelect: 'none', - '@media (max-width: @screen-md-min)': { + '@media (max-width: 768px)': { height: '45px', flexDirection: 'column', }, diff --git a/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.tsx b/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.tsx index 721d8a1bf57..7f9fca5ac41 100644 --- a/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.tsx +++ b/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageActions.tsx @@ -102,7 +102,7 @@ const MessageActionsMenu: FC = ({ setCurrentMsgAction(selectedMsgActionName); handleMenuOpen(true); const newEvent = setContextMenuPosition(event); - showContextMenu(newEvent, menuEntries, 'message-options-menu'); + showContextMenu({event: newEvent, entries: menuEntries, identifier: 'message-options-menu'}); } } else if (!event.shiftKey && isTabKey(event) && !reactionsTotalCount) { // if there's no reaction then on tab from context menu hide the message actions menu @@ -127,7 +127,12 @@ const MessageActionsMenu: FC = ({ } else if (selectedMsgActionName) { setCurrentMsgAction(selectedMsgActionName); handleMenuOpen(true); - showContextMenu(event, menuEntries, 'message-options-menu', resetActionMenuStates); + showContextMenu({ + event, + entries: menuEntries, + identifier: 'message-options-menu', + resetMenuStates: resetActionMenuStates, + }); } }, [currentMsgActionName, handleMenuOpen, menuEntries], diff --git a/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageReactions/EmojiPill.tsx b/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageReactions/EmojiPill.tsx index f3c5fc57a51..0e222121910 100644 --- a/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageReactions/EmojiPill.tsx +++ b/src/script/components/MessagesList/Message/ContentMessage/MessageActions/MessageReactions/EmojiPill.tsx @@ -78,20 +78,30 @@ export const EmojiPill = ({ const conversationReactionCaption = () => { if (emojiCount > MAX_USER_NAMES_TO_SHOW) { - return t('conversationLikesCaptionPluralMoreThan2', { - number: (emojiCount - MAX_USER_NAMES_TO_SHOW).toString(), - userNames: reactingUserNames.join(', '), - }); + return t( + 'conversationLikesCaptionPluralMoreThan2', + { + number: (emojiCount - MAX_USER_NAMES_TO_SHOW).toString(), + userNames: reactingUserNames.join(', '), + }, + {}, + true, + ); } if (emojiCount === MAX_USER_NAMES_TO_SHOW) { - return t('conversationLikesCaptionPlural', { - firstUser: reactingUserNames[0], - secondUser: reactingUserNames[1], - }); + return t( + 'conversationLikesCaptionPlural', + { + firstUser: reactingUserNames[0], + secondUser: reactingUserNames[1], + }, + {}, + true, + ); } - return t('conversationLikesCaptionSingular', {userName: reactingUserNames?.[0] || ''}); + return t('conversationLikesCaptionSingular', {userName: reactingUserNames?.[0] || ''}, {}, true); }; const caption = conversationReactionCaption(); diff --git a/src/script/components/MessagesList/Message/ContentMessage/MessageHeader.tsx b/src/script/components/MessagesList/Message/ContentMessage/MessageHeader.tsx index 998d02a0358..7369d2935b6 100644 --- a/src/script/components/MessagesList/Message/ContentMessage/MessageHeader.tsx +++ b/src/script/components/MessagesList/Message/ContentMessage/MessageHeader.tsx @@ -20,8 +20,8 @@ import {Tooltip} from '@wireapp/react-ui-kit'; import {AVATAR_SIZE, Avatar} from 'Components/Avatar'; +import {UserBlockedBadge} from 'Components/Badge'; import * as Icon from 'Components/Icon'; -import {UserBlockedBadge} from 'Components/UserBlockedBadge/UserBlockedBadge'; import {UserName} from 'Components/UserName'; import {ContentMessage} from 'src/script/entity/message/ContentMessage'; import {DeleteMessage} from 'src/script/entity/message/DeleteMessage'; diff --git a/src/script/components/MessagesList/Message/ContentMessage/asset/ImageAsset/ImageAsset.test.tsx b/src/script/components/MessagesList/Message/ContentMessage/asset/ImageAsset/ImageAsset.test.tsx index 90a6b112e78..b033ec28870 100644 --- a/src/script/components/MessagesList/Message/ContentMessage/asset/ImageAsset/ImageAsset.test.tsx +++ b/src/script/components/MessagesList/Message/ContentMessage/asset/ImageAsset/ImageAsset.test.tsx @@ -27,7 +27,7 @@ import {MediumImage} from 'src/script/entity/message/MediumImage'; import {ImageAsset, ImageAssetProps} from './ImageAsset'; -jest.mock('Components/utils/InViewport', () => ({ +jest.mock('Components/InViewport', () => ({ InViewport: ({onVisible, children}: {onVisible: () => void; children: any}) => { setTimeout(onVisible); return
    {children}
    ; diff --git a/src/script/components/MessagesList/Message/ContentMessage/asset/VideoAsset.tsx b/src/script/components/MessagesList/Message/ContentMessage/asset/VideoAsset.tsx index 51f965bf034..8da6da78884 100644 --- a/src/script/components/MessagesList/Message/ContentMessage/asset/VideoAsset.tsx +++ b/src/script/components/MessagesList/Message/ContentMessage/asset/VideoAsset.tsx @@ -20,12 +20,15 @@ import React, {useCallback, useEffect, useState} from 'react'; import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; +import {amplify} from 'amplify'; import cx from 'classnames'; import {container} from 'tsyringe'; -import {useTimeout} from '@wireapp/react-ui-kit'; +import {Button, ButtonVariant, useTimeout} from '@wireapp/react-ui-kit'; +import {WebAppEvents} from '@wireapp/webapp-events'; import {RestrictedVideo} from 'Components/asset/RestrictedVideo'; +import {EventName} from 'src/script/tracking/EventName'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {t} from 'Util/LocalizerUtil'; import {formatSeconds} from 'Util/TimeUtil'; @@ -33,12 +36,13 @@ import {useEffectRef} from 'Util/useEffectRef'; import {MediaButton} from './controls/MediaButton'; import {SeekBar} from './controls/SeekBar'; +import {FileAsset} from './FileAssetComponent'; import {AssetUrl, useAssetTransfer} from './useAssetTransfer'; import {AssetRepository} from '../../../../../assets/AssetRepository'; import {AssetTransferState} from '../../../../../assets/AssetTransferState'; import type {ContentMessage} from '../../../../../entity/message/ContentMessage'; -import type {FileAsset} from '../../../../../entity/message/FileAsset'; +import type {FileAsset as FileAssetType} from '../../../../../entity/message/FileAsset'; import {TeamState} from '../../../../../team/TeamState'; interface VideoAssetProps { @@ -55,7 +59,7 @@ const VideoAsset: React.FC = ({ teamState = container.resolve(TeamState), isFocusable = true, }) => { - const asset = message.getFirstAsset() as FileAsset; + const asset = message.getFirstAsset() as FileAssetType; const {isObfuscated} = useKoSubscribableChildren(message, ['isObfuscated']); const {preview_resource: assetPreviewResource} = useKoSubscribableChildren(asset, ['preview_resource']); const [videoPlaybackError, setVideoPlaybackError] = useState(false); @@ -64,9 +68,10 @@ const VideoAsset: React.FC = ({ const [videoSrc, setVideoSrc] = useState(); const [videoElement, setVideoElement] = useEffectRef(); const [isVideoLoaded, setIsVideoLoaded] = useState(false); + const {isFileSharingReceivingEnabled} = useKoSubscribableChildren(teamState, ['isFileSharingReceivingEnabled']); const [displaySmall, setDisplaySmall] = useState(!!isQuote); - const {transferState, isUploading, isPendingUpload, uploadProgress, cancelUpload, getAssetUrl} = + const {transferState, isUploading, isPendingUpload, uploadProgress, cancelUpload, getAssetUrl, downloadAsset} = useAssetTransfer(message); const [hideControls, setHideControls] = useState(false); @@ -77,12 +82,47 @@ const VideoAsset: React.FC = ({ if (assetPreviewResource && isFileSharingReceivingEnabled) { getAssetUrl(assetPreviewResource).then(setVideoPreview); } + return () => { videoPreview?.dispose(); videoSrc?.dispose(); }; }, []); + // Initial check if video is supported with `canPlayType` method, which checks for MIME type, e.g. 'video/mp4' or 'video/mov'. + // It's not 100% reliable (e.g. doesn't check codecs), but it's synchorous, which is helpful for initial rendering. + const isVideoMimeTypeSupported = (mimeType: string): boolean => { + const video = document.createElement('video'); + const canPlay = video.canPlayType(mimeType) !== ''; + + if (!canPlay) { + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.MESSAGES.VIDEO.PLAY_FAILED); + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.MESSAGES.VIDEO.UNSUPPORTED_MIME_TYPE); + } + + return canPlay; + }; + + // Advanced check for video playability. + // It's more reliable than `isVideoMimeTypeSupported` (e.g. checks for codecs), but it's async, so it's not suitable for initial rendering. + // It's used when user tries to play the video. + const isVideoPlayable = async (url: string): Promise => { + const video = document.createElement('video'); + return new Promise(resolve => { + video.onloadedmetadata = () => { + // Detects is the video track is properly available. + // If these dimensions are 0, typically the video track can't be properly decoded/rendered. + if (video.videoWidth === 0 || video.videoHeight === 0) { + resolve(false); + return; + } + resolve(true); + }; + video.onerror = () => resolve(false); + video.src = url; + }); + }; + const onPlayButtonClicked = async (): Promise => { if (isFileSharingReceivingEnabled) { setDisplaySmall(false); @@ -93,10 +133,22 @@ const VideoAsset: React.FC = ({ asset.status(AssetTransferState.DOWNLOADING); try { - const url = await getAssetUrl(asset.original_resource()); - setVideoSrc(url); + const assetUrl = await getAssetUrl(asset.original_resource()); + const playable = await isVideoPlayable(assetUrl.url); + + if (!playable) { + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.MESSAGES.VIDEO.PLAY_FAILED); + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.MESSAGES.VIDEO.UNPLAYABLE_ERROR); + setVideoPlaybackError(true); + return; + } + + setVideoSrc(assetUrl); setIsVideoLoaded(true); + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.MESSAGES.VIDEO.PLAY_SUCCESS); } catch (error) { + setVideoPlaybackError(true); + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.MESSAGES.VIDEO.PLAY_FAILED); console.error('Failed to load video asset ', error); } @@ -132,7 +184,15 @@ const VideoAsset: React.FC = ({ } }; - return !isObfuscated ? ( + if (isObfuscated) { + return null; + } + + if (!isVideoMimeTypeSupported(asset.file_type || '')) { + return ; + } + + return (
    {!isFileSharingReceivingEnabled ? ( @@ -153,6 +213,7 @@ const VideoAsset: React.FC = ({ poster={videoPreview?.url} onError={event => { setVideoPlaybackError(true); + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.MESSAGES.VIDEO.PLAY_FAILED); console.error('Video cannot be played', event); }} onPlaying={onVideoPlaying} @@ -163,7 +224,12 @@ const VideoAsset: React.FC = ({ tabIndex={TabIndex.UNFOCUSABLE} /> {videoPlaybackError ? ( -
    {t('conversationPlaybackError')}
    +
    +

    {t('conversationPlaybackError')}

    + +
    ) : ( <> {isPendingUpload ? ( @@ -214,7 +280,7 @@ const VideoAsset: React.FC = ({ )}
    - ) : null; + ); }; export {VideoAsset}; diff --git a/src/script/components/MessagesList/Message/MemberMessage/ConnectedMessage.tsx b/src/script/components/MessagesList/Message/MemberMessage/ConnectedMessage.tsx index c8a1d9e4ad0..395921ae475 100644 --- a/src/script/components/MessagesList/Message/MemberMessage/ConnectedMessage.tsx +++ b/src/script/components/MessagesList/Message/MemberMessage/ConnectedMessage.tsx @@ -22,8 +22,8 @@ import React from 'react'; import {Button, ButtonVariant} from '@wireapp/react-ui-kit'; import {Avatar, AVATAR_SIZE} from 'Components/Avatar'; +import {UserClassifiedBar} from 'Components/ClassifiedBar/ClassifiedBar'; import * as Icon from 'Components/Icon'; -import {UserClassifiedBar} from 'Components/input/ClassifiedBar'; import {UnverifiedUserWarning} from 'Components/Modals/UserModal'; import {User} from 'src/script/entity/User'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; diff --git a/src/script/components/MessagesList/Message/VerificationMessage.tsx b/src/script/components/MessagesList/Message/VerificationMessage.tsx index 753bb5ffdde..2cf08ba7abc 100644 --- a/src/script/components/MessagesList/Message/VerificationMessage.tsx +++ b/src/script/components/MessagesList/Message/VerificationMessage.tsx @@ -23,7 +23,7 @@ import {amplify} from 'amplify'; import {WebAppEvents} from '@wireapp/webapp-events'; -import {VerificationIcon} from 'Components/VerifiedIcon'; +import {VerificationIcon} from 'Components/VerificationIcon'; import {SidebarTabs, useSidebarStore} from 'src/script/page/LeftSidebar/panels/Conversations/useSidebarStore'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {Declension, joinNames, t} from 'Util/LocalizerUtil'; diff --git a/src/script/components/MessagesList/Message/index.tsx b/src/script/components/MessagesList/Message/index.tsx index ea77a4d0193..8c09f66fb4a 100644 --- a/src/script/components/MessagesList/Message/index.tsx +++ b/src/script/components/MessagesList/Message/index.tsx @@ -22,7 +22,7 @@ import React, {useLayoutEffect, useRef, useEffect} from 'react'; import {QualifiedId} from '@wireapp/api-client/lib/user'; import cx from 'classnames'; -import {InViewport} from 'Components/utils/InViewport'; +import {InViewport} from 'Components/InViewport'; import {ServiceEntity} from 'src/script/integration/ServiceEntity'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {getAllFocusableElements, setElementsTabIndex} from 'Util/focusUtil'; diff --git a/src/script/components/MessagesList/MessageList.styles.ts b/src/script/components/MessagesList/MessageList.styles.ts index 6b9054b1638..284a24da618 100644 --- a/src/script/components/MessagesList/MessageList.styles.ts +++ b/src/script/components/MessagesList/MessageList.styles.ts @@ -25,6 +25,8 @@ export const jumpToLastMessageButtonStyles: CSSObject = { height: '40px', borderRadius: '100%', bottom: '56px', + marginBottom: '10px', + zIndex: 1, '@media (max-width: 768px)': { bottom: '100px', diff --git a/src/script/components/Modals/GroupCreation/GroupCreationModal.tsx b/src/script/components/Modals/GroupCreation/GroupCreationModal.tsx index 81a2cc106cd..0e656dd0cd5 100644 --- a/src/script/components/Modals/GroupCreation/GroupCreationModal.tsx +++ b/src/script/components/Modals/GroupCreation/GroupCreationModal.tsx @@ -26,12 +26,12 @@ import {amplify} from 'amplify'; import cx from 'classnames'; import {container} from 'tsyringe'; -import {Button, ButtonVariant, Select} from '@wireapp/react-ui-kit'; +import {Button, ButtonVariant, Option, Select} from '@wireapp/react-ui-kit'; import {WebAppEvents} from '@wireapp/webapp-events'; import {FadingScrollbar} from 'Components/FadingScrollbar'; import * as Icon from 'Components/Icon'; -import {ModalComponent} from 'Components/ModalComponent'; +import {ModalComponent} from 'Components/Modals/ModalComponent'; import {SearchInput} from 'Components/SearchInput'; import {TextInput} from 'Components/TextInput'; import {BaseToggle} from 'Components/toggle/BaseToggle'; @@ -296,6 +296,21 @@ const GroupCreationModal: React.FC = ({ setNameError(''); }; + const onProtocolChange = (option: Option | null) => { + if (!isProtocolOption(option)) { + return; + } + + setSelectedProtocol(option); + + if ( + (option.value === ConversationProtocol.MLS && isServicesEnabled) || + (option.value === ConversationProtocol.PROTEUS && !isServicesEnabled) + ) { + clickOnToggleServicesMode(); + } + }; + const groupNameLength = groupName.length; const hasNameError = nameError.length > 0; @@ -425,6 +440,7 @@ const GroupCreationModal: React.FC = ({ conversationRepository={conversationRepository} noUnderline allowRemoteSearch + filterRemoteTeamUsers /> )} @@ -476,16 +492,18 @@ const GroupCreationModal: React.FC = ({ toggleName={t('guestOptionsTitle')} toggleId="guests" /> - + {selectedProtocol.value !== ConversationProtocol.MLS && ( + + )} = ({ <> onPasswordValueChange(event.currentTarget.value)} + pattern={ValidationUtil.getNewPasswordPattern(Config.getConfig().NEW_PASSWORD_MINIMUM_LENGTH)} + markInvalid={isPasswordInputMarkInvalid} + error={isPasswordInputMarkInvalid ? : undefined} + /> + onPasswordConfirmationChange(event.currentTarget.value)} + markInvalid={isPasswordConfirmationMarkInvalid} + /> + + + ); +}; + +const GuestLinkPasswordModalErrorMessage = () => { + return ( + + {t('modalGuestLinkJoinHelperText', { + minPasswordLength: Config.getConfig().MINIMUM_PASSWORD_LENGTH.toString(), + })} + + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/InputForm/InputForm.tsx b/src/script/components/Modals/PrimaryModal/InputForm/InputForm.tsx new file mode 100644 index 00000000000..71acf23d9cb --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/InputForm/InputForm.tsx @@ -0,0 +1,47 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import type {FormEvent} from 'react'; + +interface InputFormProps { + onSubmit: (event: FormEvent) => void; + inputValue: string; + inputPlaceholder: string; + onInputChange: (value: string) => void; +} + +export const InputForm = ({onSubmit, inputValue, inputPlaceholder, onInputChange}: InputFormProps) => { + return ( +
    + + + ref?.focus()} + maxLength={64} + className="modal__input" + id="modal-input" + value={inputValue} + placeholder={inputPlaceholder} + onChange={event => onInputChange(event.target.value)} + /> +
    + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/JoinGuestLinkPasswordForm/JoinGuestLinkPasswordForm.styles.ts b/src/script/components/Modals/PrimaryModal/JoinGuestLinkPasswordForm/JoinGuestLinkPasswordForm.styles.ts new file mode 100644 index 00000000000..a2a4cdec392 --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/JoinGuestLinkPasswordForm/JoinGuestLinkPasswordForm.styles.ts @@ -0,0 +1,43 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const labelStyles: CSSObject = { + fontSize: 'var(--font-size-medium)', + fontWeight: 'var(--font-weight-regular)', + lineHeight: 'var(--line-height-sm)', + color: 'var(--text-input-label)', + marginBottom: 2, +}; + +export const inputStyles: CSSObject = { + boxShadow: '0 0 0 1px var(--text-input-border)', + borderRadius: 12, + margin: 0, +}; + +export const linkStyles: CSSObject = { + marginTop: 24, +}; + +export const linkTextStyles: CSSObject = { + textDecoration: 'underline', + marginBottom: 24, +}; diff --git a/src/script/components/Modals/PrimaryModal/JoinGuestLinkPasswordForm/JoinGuestLinkPasswordForm.tsx b/src/script/components/Modals/PrimaryModal/JoinGuestLinkPasswordForm/JoinGuestLinkPasswordForm.tsx new file mode 100644 index 00000000000..e04c9f65e75 --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/JoinGuestLinkPasswordForm/JoinGuestLinkPasswordForm.tsx @@ -0,0 +1,64 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import type {FormEvent} from 'react'; + +import {COLOR, Form, Link, Text} from '@wireapp/react-ui-kit'; + +import {Config} from 'src/script/Config'; +import {t} from 'Util/LocalizerUtil'; + +import {labelStyles, inputStyles, linkStyles, linkTextStyles} from './JoinGuestLinkPasswordForm.styles'; + +interface JoinGuestLinkPasswordFormProps { + onSubmit: (event: FormEvent) => void; + inputValue: string; + onInputChange: (value: string) => void; +} + +export const JoinGuestLinkPasswordForm = ({onSubmit, inputValue, onInputChange}: JoinGuestLinkPasswordFormProps) => { + return ( +
    + + + onInputChange(event.target.value)} + /> + + + + {t('guestLinkPasswordModal.learnMoreLink')} + + +
    + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/PasswordAdvancedSecurityForm/PasswordAdvancedSecurityForm.tsx b/src/script/components/Modals/PrimaryModal/PasswordAdvancedSecurityForm/PasswordAdvancedSecurityForm.tsx new file mode 100644 index 00000000000..01aa717f580 --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/PasswordAdvancedSecurityForm/PasswordAdvancedSecurityForm.tsx @@ -0,0 +1,63 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import type {ReactElement, ChangeEvent, FormEvent} from 'react'; + +import {Input} from '@wireapp/react-ui-kit'; + +interface PasswordAdvancedSecurityFormProps { + onSubmit: (event: FormEvent) => void; + inputValue: string; + inputPlaceholder: string; + isInputInvalid: boolean; + onInputChange: (value: string) => void; + inputHelperText: string; + error?: ReactElement; +} + +export const PasswordAdvancedSecurityForm = ({ + onSubmit, + inputValue, + inputPlaceholder, + isInputInvalid, + onInputChange, + inputHelperText, + error, +}: PasswordAdvancedSecurityFormProps) => { + return ( +
    + ) => { + onInputChange(event.target.value); + }} + autoComplete="password" + pattern=".{2,64}" + helperText={inputHelperText} + error={error} + /> +
    + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/PasswordForm/PasswordForm.tsx b/src/script/components/Modals/PrimaryModal/PasswordForm/PasswordForm.tsx new file mode 100644 index 00000000000..4c850524366 --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/PasswordForm/PasswordForm.tsx @@ -0,0 +1,46 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import type {FormEvent} from 'react'; + +interface PasswordFormProps { + onSubmit: (event: FormEvent) => void; + inputPlaceholder: string; + inputValue: string; + onInputChange: (value: string) => void; +} + +export const PasswordForm = ({onSubmit, inputPlaceholder, inputValue, onInputChange}: PasswordFormProps) => { + return ( +
    + + + onInputChange(event.target.value)} + /> +
    + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryButton/PrimaryButton.tsx b/src/script/components/Modals/PrimaryModal/PrimaryButton/PrimaryButton.tsx new file mode 100644 index 00000000000..00ae553a8d0 --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/PrimaryButton/PrimaryButton.tsx @@ -0,0 +1,51 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {forwardRef, ReactNode, MouseEvent} from 'react'; + +import cx from 'classnames'; + +interface PrimaryButtonProps { + onClick: (event: MouseEvent) => void; + disabled: boolean; + fullWidth: boolean; + children: ReactNode; +} + +export const PrimaryButton = forwardRef( + ({onClick, disabled, fullWidth, children}, ref) => { + return ( + + ); + }, +); + +PrimaryButton.displayName = 'PrimaryButton'; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModal.test.tsx b/src/script/components/Modals/PrimaryModal/PrimaryModal.test.tsx index d5afe7263b2..0cdc4525b51 100644 --- a/src/script/components/Modals/PrimaryModal/PrimaryModal.test.tsx +++ b/src/script/components/Modals/PrimaryModal/PrimaryModal.test.tsx @@ -19,7 +19,10 @@ import {render, fireEvent, act} from '@testing-library/react'; +import {withTheme} from 'src/script/auth/util/test/TestUtil'; + import {PrimaryModalComponent} from './PrimaryModal'; +import {PrimaryModalType} from './PrimaryModalTypes'; import {PrimaryModal, removeCurrentModal} from '.'; @@ -27,109 +30,139 @@ describe('PrimaryModal', () => { beforeEach(() => { removeCurrentModal(); }); - it('does not render when no item is in the queue', async () => { - const {getByTestId} = render(); - const PrimaryModalWrapper = getByTestId('primary-modals-container'); - expect(PrimaryModalWrapper.children).toHaveLength(0); - }); - it('correctly calls action callback', async () => { - const {getByTestId} = render(); - const actionCallback = jest.fn(); - act(() => { - PrimaryModal.show(PrimaryModal.type.CONFIRM, { - primaryAction: { - action: actionCallback, - text: 'test-text', - }, - secondaryAction: { - action: () => {}, - text: 'secondary-text', - }, - text: { - message: 'test-message', - title: 'test-title', - }, - }); + describe('Confirm', () => { + it('does not render when no item is in the queue', async () => { + const {getByTestId} = render(); + const primaryModalWrapper = getByTestId('primary-modals-container'); + expect(primaryModalWrapper.children).toHaveLength(0); }); - const actionButton = getByTestId('do-action'); - fireEvent.click(actionButton); + it('correctly calls action callback', async () => { + const actionCallback = jest.fn(); + const {getPrimaryActionButton} = renderPrimaryModal(PrimaryModalType.CONFIRM, actionCallback); - expect(actionCallback).toHaveBeenCalledTimes(1); - }); - it('correctly calls secondary action callback', async () => { - const {getByTestId} = render(); - const secondaryActionCallback = jest.fn(); - act(() => { - PrimaryModal.show(PrimaryModal.type.CONFIRM, { - primaryAction: { - action: () => {}, - text: 'test-text', - }, - secondaryAction: { - action: secondaryActionCallback, - text: 'secondary-text', - }, - text: { - message: 'test-message', - title: 'test-title', - }, - }); + fireEvent.click(getPrimaryActionButton()); + + expect(actionCallback).toHaveBeenCalledTimes(1); }); - const secondaryActionButton = getByTestId('do-secondary'); - fireEvent.click(secondaryActionButton); + it('correctly calls secondary action callback', async () => { + const secondaryActionCallback = jest.fn(); - expect(secondaryActionCallback).toHaveBeenCalledTimes(1); - }); + const {getSecondaryActionButton} = renderPrimaryModal( + PrimaryModalType.CONFIRM, + jest.fn(), + secondaryActionCallback, + ); - it('shows close button by default', async () => { - const {getByTestId} = render(); - - act(() => { - PrimaryModal.show(PrimaryModal.type.CONFIRM, { - primaryAction: { - action: () => {}, - text: 'test-text', - }, - secondaryAction: { - action: () => {}, - text: 'secondary-text', - }, - text: { - message: 'test-message', - title: 'test-title', - }, - }); + fireEvent.click(getSecondaryActionButton()); + + expect(secondaryActionCallback).toHaveBeenCalledTimes(1); }); - const closeButton = getByTestId('do-close'); - expect(closeButton).toBeTruthy(); + it('shows close button by default', async () => { + const {getCloseButton} = renderPrimaryModal(PrimaryModalType.CONFIRM); + + expect(getCloseButton()).toBeTruthy(); + }); + + it('hides close button when hideCloseBtn is true', async () => { + const {getCloseButton} = renderPrimaryModal(PrimaryModalType.CONFIRM, jest.fn(), jest.fn(), true); + + expect(getCloseButton()).toBeFalsy(); + }); }); - it('hides close button when hideCloseBtn is true', async () => { - const {queryByTestId} = render(); - - act(() => { - PrimaryModal.show(PrimaryModal.type.CONFIRM, { - primaryAction: { - action: () => {}, - text: 'test-text2', - }, - secondaryAction: { - action: () => {}, - text: 'secondary-text', - }, - text: { - message: 'test-message', - title: 'test-title', - }, - hideCloseBtn: true, - }); + describe('GuestLinkPassword', () => { + const action = jest.fn().mockImplementation(); + + it('should show the active primary button', async () => { + const {getPrimaryActionButton} = renderPrimaryModal(PrimaryModalType.GUEST_LINK_PASSWORD, action); + + expect(getPrimaryActionButton()).toHaveProperty('disabled', false); + }); + + it('should fire validation on submit click', async () => { + const {getErrorMessage, getPrimaryActionButton} = renderPrimaryModal( + PrimaryModalType.GUEST_LINK_PASSWORD, + action, + ); + + fireEvent.click(getPrimaryActionButton()); + + expect(getErrorMessage()).toBeTruthy(); }); - const closeButton = queryByTestId('do-close'); - expect(closeButton).toBeFalsy(); + it('should fill password fields when generate password button clicked', async () => { + const {getGeneratePasswordButton, getConfirmPasswordInput, getPasswordInput} = renderPrimaryModal( + PrimaryModalType.GUEST_LINK_PASSWORD, + action, + ); + + fireEvent.click(getGeneratePasswordButton()); + + const password = (getPasswordInput() as HTMLInputElement).value; + + expect(password).not.toBe(''); + expect(getConfirmPasswordInput()).toHaveProperty('value', password); + }); + + it('should call the action when form submitted successfully', async () => { + const {getGeneratePasswordButton, getPrimaryActionButton, getPasswordInput} = renderPrimaryModal( + PrimaryModalType.GUEST_LINK_PASSWORD, + action, + ); + + const generatePasswordButton = getGeneratePasswordButton(); + + fireEvent.click(generatePasswordButton); + const password = (getPasswordInput() as HTMLInputElement).value; + + act(() => { + fireEvent.click(getPrimaryActionButton()); + }); + + expect(action).toHaveBeenCalledWith(password, false); + }); }); }); + +const renderPrimaryModal = ( + type: PrimaryModalType = PrimaryModalType.CONFIRM, + primaryAction = () => {}, + secondaryAction = () => {}, + hideCloseBtn = false, +) => { + const {getByTestId, queryByTestId} = render(withTheme()); + act(() => { + PrimaryModal.show(type, { + primaryAction: { + action: primaryAction, + text: 'test-text2', + }, + secondaryAction: { + action: secondaryAction, + text: 'secondary-text', + }, + text: { + message: 'test-message', + title: 'test-title', + }, + hideCloseBtn, + copyPassword: true, + closeOnConfirm: true, + preventClose: false, + }); + }); + + return { + getPrimaryActionButton: () => getByTestId('do-action'), + getSecondaryActionButton: () => getByTestId('do-secondary'), + getCloseButton: () => queryByTestId('do-close'), + getErrorMessage: () => getByTestId('primary-modals-error-message'), + getPasswordInput: () => getByTestId('guest-link-password'), + getGeneratePasswordButton: () => getByTestId('do-generate-password'), + getConfirmPasswordInput: () => getByTestId('guest-link-password-confirm'), + }; +}; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModal.tsx b/src/script/components/Modals/PrimaryModal/PrimaryModal.tsx index 77f54b0c546..41640864c59 100644 --- a/src/script/components/Modals/PrimaryModal/PrimaryModal.tsx +++ b/src/script/components/Modals/PrimaryModal/PrimaryModal.tsx @@ -17,27 +17,33 @@ * */ -import {FC, FormEvent, MouseEvent, useState, useRef, ChangeEvent, useEffect} from 'react'; - -import cx from 'classnames'; +import {FC, FormEvent, MouseEvent, useState, useRef, ChangeEvent, useEffect, useMemo} from 'react'; import {ValidationUtil} from '@wireapp/commons'; -import {Checkbox, CheckboxLabel, COLOR, Form, Link, Text, Input, Loading} from '@wireapp/react-ui-kit'; +import {ErrorMessage} from '@wireapp/react-ui-kit'; import {CopyToClipboardButton} from 'Components/CopyToClipboardButton'; import {FadingScrollbar} from 'Components/FadingScrollbar'; -import * as Icon from 'Components/Icon'; -import {ModalComponent} from 'Components/ModalComponent'; -import {PasswordGeneratorButton} from 'Components/PasswordGeneratorButton'; import {Config} from 'src/script/Config'; -import {isEscapeKey} from 'Util/KeyboardUtil'; +import {isEnterKey, isEscapeKey} from 'Util/KeyboardUtil'; import {t} from 'Util/LocalizerUtil'; import {isValidPassword} from 'Util/StringUtil'; +import {CheckboxOption} from './CheckboxOption/CheckboxOption'; import {MessageContent} from './Content/MessageContent'; -import {guestLinkPasswordInputStyles} from './PrimaryModal.styles'; +import {GuestLinkPasswordForm} from './GuestLinkPasswordForm/GuestLinkPasswordForm'; +import {InputForm} from './InputForm/InputForm'; +import {JoinGuestLinkPasswordForm} from './JoinGuestLinkPasswordForm/JoinGuestLinkPasswordForm'; +import {PasswordAdvancedSecurityForm} from './PasswordAdvancedSecurityForm/PasswordAdvancedSecurityForm'; +import {PasswordForm} from './PasswordForm/PasswordForm'; +import {PrimaryButton} from './PrimaryButton/PrimaryButton'; +import {PrimaryModalButtons} from './PrimaryModalButtons/PrimaryModalButtons'; +import {PrimaryModalHeader} from './PrimaryModalHeader/PrimaryModalHeader'; +import {PrimaryModalLoading} from './PrimaryModalLoading/PrimaryModalLoading'; +import {PrimaryModalShell} from './PrimaryModalShell/PrimaryModalShell'; import {usePrimaryModalState, showNextModalInQueue, defaultContent, removeCurrentModal} from './PrimaryModalState'; import {ButtonAction, PrimaryModalType} from './PrimaryModalTypes'; +import {SecondaryButton} from './SecondaryButton/SecondaryButton'; export const PrimaryModalComponent: FC = () => { const [inputValue, updateInputValue] = useState(''); @@ -53,6 +59,10 @@ export const PrimaryModalComponent: FC = () => { const currentId = usePrimaryModalState(state => state.currentModalId); const primaryActionButtonRef = useRef(null); const isModalVisible = currentId !== null; + const passwordValueRef = useRef(null); + const [isFormSubmitted, setIsFormSubmitted] = useState(false); + const isBackupPasswordValid = useMemo(() => passwordInput === '' || isValidPassword(passwordInput), [passwordInput]); + const { checkboxLabel, closeOnConfirm, @@ -96,6 +106,7 @@ export const PrimaryModalComponent: FC = () => { showNextModalInQueue(); setPasswordConfirmationValue(''); setDidCopyPassword(false); + setIsFormSubmitted(false); }; const isPasswordOptional = () => { @@ -105,6 +116,7 @@ export const PrimaryModalComponent: FC = () => { } return passwordRegex.test(passwordInput); }; + const checkGuestLinkPassword = (password: string, passwordConfirm: string): boolean => { if (password !== passwordConfirm) { return false; @@ -118,9 +130,10 @@ export const PrimaryModalComponent: FC = () => { const actionEnabled = isPasswordRequired ? isPasswordOptional() : true; const inputActionEnabled = !isInput || !!inputValue.trim().length; + const areGuestLinkPasswordsValid = checkGuestLinkPassword(passwordValue, passwordConfirmationValue); + const passwordGuestLinkActionEnabled = - (!isGuestLinkPassword || !!passwordValue.trim().length) && - checkGuestLinkPassword(passwordValue, passwordConfirmationValue); + (!isGuestLinkPassword || !!passwordValue.trim().length) && areGuestLinkPasswordsValid; const isPrimaryActionDisabled = (disabled: boolean | undefined) => { if (!!disabled) { @@ -129,10 +142,10 @@ export const PrimaryModalComponent: FC = () => { if (isConfirm) { return false; } - return (!inputActionEnabled || !passwordGuestLinkActionEnabled) && !actionEnabled; + return !inputActionEnabled && !actionEnabled; }; - const doAction = + const performAction = (action?: Function, closeAfter = true, skipValidation = false) => (event: FormEvent | MouseEvent) => { event.preventDefault(); @@ -140,6 +153,18 @@ export const PrimaryModalComponent: FC = () => { if (!skipValidation && !inputActionEnabled) { return; } + + if (hasPasswordWithRules && !isBackupPasswordValid) { + setIsFormSubmitted(true); + return; + } + + // prevent from submit when validation not passed + if (!skipValidation && isGuestLinkPassword && !areGuestLinkPasswordsValid) { + setIsFormSubmitted(true); + return; + } + if (typeof action === 'function') { action(); } @@ -178,23 +203,22 @@ export const PrimaryModalComponent: FC = () => { const secondaryActions = Array.isArray(secondaryAction) ? secondaryAction : [secondaryAction]; - const modalRef = useRef(null); - useEffect(() => { - if (isModalVisible) { - modalRef.current?.focus(); - } - - const handleEscape = (event: KeyboardEvent) => { + const onKeyPress = (event: KeyboardEvent) => { if (isEscapeKey(event) && isModalVisible) { removeCurrentModal(); closeAction(); } + + if (isEnterKey(event) && primaryAction?.runActionOnEnterClick) { + primaryAction?.action?.(); + removeCurrentModal(); + } }; - document.addEventListener('keydown', handleEscape); - return () => document.removeEventListener('keydown', handleEscape); - }, [isModalVisible]); + document.addEventListener('keypress', onKeyPress); + return () => document.removeEventListener('keypress', onKeyPress); + }, [primaryAction, isModalVisible]); const closeAction = () => { if (hasPasswordWithRules) { @@ -206,278 +230,130 @@ export const PrimaryModalComponent: FC = () => { const secondaryButtons = secondaryActions .filter((action): action is ButtonAction => action !== null && !!action.text) .map(action => ( - + )); const primaryButton = !!primaryAction?.text && ( - + ); const buttons = primaryBtnFirst ? [primaryButton, ...secondaryButtons] : [...secondaryButtons, primaryButton]; + const isPasswordFieldValid = isFormSubmitted && !passwordValueRef.current?.validity.valid; + + const backupPasswordHint = t('backupPasswordHint', { + minPasswordLength: Config.getConfig().NEW_PASSWORD_MINIMUM_LENGTH.toString(), + }); return ( - + + ); }; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModalButtons/PrimaryModalButtons.tsx b/src/script/components/Modals/PrimaryModal/PrimaryModalButtons/PrimaryModalButtons.tsx new file mode 100644 index 00000000000..ea75d12b4d2 --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/PrimaryModalButtons/PrimaryModalButtons.tsx @@ -0,0 +1,39 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {ReactNode} from 'react'; + +import cx from 'classnames'; + +interface PrimaryModalButtonsProps { + isColumn: boolean; + children: ReactNode; +} + +export const PrimaryModalButtons = ({isColumn, children}: PrimaryModalButtonsProps) => { + return ( +
    + {children} +
    + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModalHeader/PrimaryModalHeader.tsx b/src/script/components/Modals/PrimaryModal/PrimaryModalHeader/PrimaryModalHeader.tsx new file mode 100644 index 00000000000..5e4fb4df49c --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/PrimaryModalHeader/PrimaryModalHeader.tsx @@ -0,0 +1,53 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import * as Icon from 'Components/Icon'; + +import {removeCurrentModal} from '../PrimaryModalState'; + +interface ModalHeaderProps { + titleText: string; + closeBtnTitle?: string; + hideCloseBtn?: boolean; + closeAction: () => void; +} + +export const PrimaryModalHeader = ({titleText, closeAction, closeBtnTitle, hideCloseBtn}: ModalHeaderProps) => { + return ( +
    + + {!hideCloseBtn && ( + + )} +
    + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModalLoading/PrimaryModalLoading.styles.ts b/src/script/components/Modals/PrimaryModal/PrimaryModalLoading/PrimaryModalLoading.styles.ts new file mode 100644 index 00000000000..3135578f68c --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/PrimaryModalLoading/PrimaryModalLoading.styles.ts @@ -0,0 +1,27 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const wrapperStyles: CSSObject = { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + margin: '1.5rem 0 2rem 0', +}; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModalLoading/PrimaryModalLoading.tsx b/src/script/components/Modals/PrimaryModal/PrimaryModalLoading/PrimaryModalLoading.tsx new file mode 100644 index 00000000000..0747400eb9f --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/PrimaryModalLoading/PrimaryModalLoading.tsx @@ -0,0 +1,30 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {Loading} from '@wireapp/react-ui-kit'; + +import {wrapperStyles} from './PrimaryModalLoading.styles'; + +export const PrimaryModalLoading = () => { + return ( +
    + +
    + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModalShell/PrimaryModalShell.tsx b/src/script/components/Modals/PrimaryModal/PrimaryModalShell/PrimaryModalShell.tsx new file mode 100644 index 00000000000..66a5f6f48e3 --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/PrimaryModalShell/PrimaryModalShell.tsx @@ -0,0 +1,64 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {ReactNode, useEffect, useRef} from 'react'; + +import {ModalComponent} from 'Components/Modals/ModalComponent'; + +interface PrimaryModalShellProps { + title: string; + isShown: boolean; + children: ReactNode; + dataUieName: string; + onClose: () => void; + onBgClick: () => void; +} + +export const PrimaryModalShell = ({ + isShown, + title, + dataUieName, + children, + onClose, + onBgClick, +}: PrimaryModalShellProps) => { + const modalsRef = useRef(null); + + useEffect(() => { + if (isShown) { + modalsRef.current?.focus(); + } + }, [isShown]); + + return ( + + ); +}; diff --git a/src/script/components/Modals/PrimaryModal/PrimaryModalTypes.ts b/src/script/components/Modals/PrimaryModal/PrimaryModalTypes.ts index 287d3a8c2f8..aba2437d35d 100644 --- a/src/script/components/Modals/PrimaryModal/PrimaryModalTypes.ts +++ b/src/script/components/Modals/PrimaryModal/PrimaryModalTypes.ts @@ -23,6 +23,7 @@ import {ClientNotificationData} from '../../../notification/PreferenceNotificati export interface ButtonAction { action?: Function; + runActionOnEnterClick?: Boolean; uieName?: string; text?: React.ReactNode; disabled?: boolean; diff --git a/src/script/components/Modals/PrimaryModal/SecondaryButton/SecondaryButton.tsx b/src/script/components/Modals/PrimaryModal/SecondaryButton/SecondaryButton.tsx new file mode 100644 index 00000000000..34cbeac1503 --- /dev/null +++ b/src/script/components/Modals/PrimaryModal/SecondaryButton/SecondaryButton.tsx @@ -0,0 +1,46 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import type {ReactNode, MouseEvent} from 'react'; + +import cx from 'classnames'; + +interface SecondaryButtonProps { + onClick: (event: MouseEvent) => void; + disabled?: boolean; + children: ReactNode; + fullWidth: boolean; + uieName?: string; +} + +export const SecondaryButton = ({onClick, disabled = false, fullWidth, uieName, children}: SecondaryButtonProps) => { + return ( + + ); +}; diff --git a/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.styles.ts b/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.styles.ts new file mode 100644 index 00000000000..161fffe03f9 --- /dev/null +++ b/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.styles.ts @@ -0,0 +1,76 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const wrapper: CSSObject = { + padding: '32px 24px', +}; + +export const title: CSSObject = { + fontSize: 'var(--line-height-lg)', + marginBottom: '16px', + textAlign: 'center', +}; + +export const description: CSSObject = { + fontSize: 'var(--font-size-base)', + marginBottom: '32px', + textAlign: 'center', +}; + +export const ratingList: CSSObject = { + display: 'flex', + alignItems: 'flex-end', + justifyContent: 'space-between', + gap: '10px', + listStyle: 'none', + margin: '0 0 24px', + padding: 0, +}; + +export const ratingItemHeading: CSSObject = { + color: 'var(--foreground)', + fontSize: 'var(--font-size-small)', + marginBottom: '8px', + textAlign: 'center', +}; + +export const ratingItemBubble: CSSObject = { + display: 'grid', + placeContent: 'center', + height: '56px', + width: '56px', + + borderRadius: '50%', + + fontSize: 'var(--font-size-base)', + fontWeight: 'var(--font-weight-semibold)', +}; + +export const buttonWrapper: CSSObject = { + marginBottom: '32px', +}; + +export const buttonStyle: CSSObject = { + fontSize: 'var(--font-size-base)', + fontWeight: 'var(--font-weight-semibold)', + height: '56px', + width: '100%', +}; diff --git a/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.test.tsx b/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.test.tsx new file mode 100644 index 00000000000..dd1e906a575 --- /dev/null +++ b/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.test.tsx @@ -0,0 +1,137 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {render, fireEvent, act} from '@testing-library/react'; +import {amplify} from 'amplify'; +import {container} from 'tsyringe'; + +import {WebAppEvents} from '@wireapp/webapp-events'; + +import {useCallAlertState} from 'Components/calling/useCallAlertState'; +import {CALL_QUALITY_FEEDBACK_KEY} from 'Components/Modals/QualityFeedbackModal/constants'; +import {RatingListLabel} from 'Components/Modals/QualityFeedbackModal/typings'; + +import {QualityFeedbackModal} from './QualityFeedbackModal'; + +import {withIntl, withTheme} from '../../../auth/util/test/TestUtil'; +import {User} from '../../../entity/User'; +import {EventName} from '../../../tracking/EventName'; +import {Segmentation} from '../../../tracking/Segmentation'; +import {UserState} from '../../../user/UserState'; + +jest.mock('../../../tracking/Countly.helpers', () => ({ + isCountlyEnabledAtCurrentEnvironment: () => true, +})); + +describe('QualityFeedbackModal', () => { + const renderQualityFeedbackModal = () => render(withTheme(withIntl())); + const user = new User('userId', 'domain'); + + beforeEach(() => { + jest.clearAllMocks(); + spyOn(container.resolve(UserState), 'self').and.returnValue(user); + }); + + it('should not render if qualityFeedbackModalShown is false', () => { + renderQualityFeedbackModal(); + + act(() => { + useCallAlertState.getState().setQualityFeedbackModalShown(false); + }); + + expect(useCallAlertState.getState().qualityFeedbackModalShown).toBe(false); + }); + + it('should render correctly when qualityFeedbackModalShown is true', () => { + renderQualityFeedbackModal(); + + act(() => { + useCallAlertState.getState().setQualityFeedbackModalShown(true); + }); + + expect(useCallAlertState.getState().qualityFeedbackModalShown).toBe(true); + }); + + it('should close modal on skip', () => { + const {getByText} = renderQualityFeedbackModal(); + + act(() => { + useCallAlertState.getState().setQualityFeedbackModalShown(true); + }); + + spyOn(amplify, 'publish').and.returnValue({ + eventKey: WebAppEvents.ANALYTICS.EVENT, + type: EventName.CALLING.QUALITY_REVIEW, + value: { + [Segmentation.CALL.QUALITY_REVIEW_LABEL]: RatingListLabel.DISMISSED, + }, + }); + + fireEvent.click(getByText('qualityFeedback.skip')); + + expect(amplify.publish).toHaveBeenCalledWith(WebAppEvents.ANALYTICS.EVENT, EventName.CALLING.QUALITY_REVIEW, { + [Segmentation.CALL.QUALITY_REVIEW_LABEL]: RatingListLabel.DISMISSED, + }); + + expect(useCallAlertState.getState().qualityFeedbackModalShown).toBe(false); + }); + + it('should send quality feedback and close modal on rating click', () => { + const {getByText} = renderQualityFeedbackModal(); + + act(() => { + useCallAlertState.getState().setQualityFeedbackModalShown(true); + }); + + spyOn(amplify, 'publish').and.returnValue({ + eventKey: WebAppEvents.ANALYTICS.EVENT, + type: EventName.CALLING.QUALITY_REVIEW, + value: { + [Segmentation.CALL.SCORE]: 5, + [Segmentation.CALL.QUALITY_REVIEW_LABEL]: RatingListLabel.ANSWERED, + }, + }); + + fireEvent.click(getByText('5')); + + expect(amplify.publish).toHaveBeenCalledWith(WebAppEvents.ANALYTICS.EVENT, EventName.CALLING.QUALITY_REVIEW, { + [Segmentation.CALL.SCORE]: 5, + [Segmentation.CALL.QUALITY_REVIEW_LABEL]: RatingListLabel.ANSWERED, + }); + + expect(useCallAlertState.getState().qualityFeedbackModalShown).toBe(false); + }); + + it('should store the doNotAskAgain state in localStorage', () => { + const {getByText} = renderQualityFeedbackModal(); + + act(() => { + useCallAlertState.getState().setQualityFeedbackModalShown(true); + }); + + const checkbox = getByText('qualityFeedback.doNotAskAgain'); + fireEvent.click(checkbox); + fireEvent.click(getByText('5')); + + act(() => { + const storedData = JSON.parse(localStorage.getItem(CALL_QUALITY_FEEDBACK_KEY) || '{}'); + expect(storedData['userId']).toBeNull(); + }); + }); +}); diff --git a/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.tsx b/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.tsx new file mode 100644 index 00000000000..1605db52acd --- /dev/null +++ b/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.tsx @@ -0,0 +1,158 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import React, {useState} from 'react'; + +import {amplify} from 'amplify'; +import {container} from 'tsyringe'; + +import {Button, ButtonVariant, Checkbox, CheckboxLabel} from '@wireapp/react-ui-kit'; +import {WebAppEvents} from '@wireapp/webapp-events'; + +import {showAppNotification} from 'Components/AppNotification'; +import {useCallAlertState} from 'Components/calling/useCallAlertState'; +import {ModalComponent} from 'Components/Modals/ModalComponent'; +import {RatingListLabel} from 'Components/Modals/QualityFeedbackModal/typings'; +import {useKoSubscribableChildren} from 'Util/ComponentUtil'; +import {t} from 'Util/LocalizerUtil'; +import {getLogger} from 'Util/Logger'; + +import {CALL_QUALITY_FEEDBACK_KEY, CALL_SURVEY_MUTE_INTERVAL, ratingListItems} from './constants'; +import { + buttonStyle, + buttonWrapper, + description, + ratingItemBubble, + ratingItemHeading, + ratingList, + title, + wrapper, +} from './QualityFeedbackModal.styles'; + +import {EventName} from '../../../tracking/EventName'; +import {Segmentation} from '../../../tracking/Segmentation'; +import {UserState} from '../../../user/UserState'; + +const logger = getLogger('CallQualityFeedback'); + +export const QualityFeedbackModal = () => { + const userState = container.resolve(UserState); + + const [isChecked, setIsChecked] = useState(false); + const {setQualityFeedbackModalShown, qualityFeedbackModalShown} = useCallAlertState(); + const {self: selfUser} = useKoSubscribableChildren(userState, ['self']); + + if (!qualityFeedbackModalShown) { + return null; + } + + const handleCloseModal = () => { + if (!selfUser) { + setQualityFeedbackModalShown(false); + return; + } + + try { + const qualityFeedbackStorage = localStorage.getItem(CALL_QUALITY_FEEDBACK_KEY); + const currentStorageData = qualityFeedbackStorage ? JSON.parse(qualityFeedbackStorage) : {}; + const currentDate = new Date(); + const dateUntilShowModal = new Date(currentDate.getTime() + CALL_SURVEY_MUTE_INTERVAL); + + currentStorageData[selfUser.id] = isChecked ? null : dateUntilShowModal.getTime(); + localStorage.setItem(CALL_QUALITY_FEEDBACK_KEY, JSON.stringify(currentStorageData)); + showAppNotification(t('qualityFeedback.notificationSubmitted')); + } catch (error) { + logger.warn(`Can't send feedback: ${(error as Error).message}`); + } finally { + setQualityFeedbackModalShown(false); + } + }; + + const sendQualityFeedback = (score: number) => { + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.CALLING.QUALITY_REVIEW, { + [Segmentation.CALL.SCORE]: score, + [Segmentation.CALL.QUALITY_REVIEW_LABEL]: RatingListLabel.ANSWERED, + }); + + handleCloseModal(); + }; + + const skipQualityFeedback = () => { + amplify.publish(WebAppEvents.ANALYTICS.EVENT, EventName.CALLING.QUALITY_REVIEW, { + [Segmentation.CALL.QUALITY_REVIEW_LABEL]: RatingListLabel.DISMISSED, + }); + + handleCloseModal(); + }; + + return ( + +
    +

    {t('qualityFeedback.heading')}

    + +

    {t('qualityFeedback.description')}

    + +
      + {ratingListItems.map(ratingItem => ( +
    • + {ratingItem?.headingTranslationKey && ( +
      {t(ratingItem.headingTranslationKey)}
      + )} + +
    • + ))} +
    + +
    + +
    + +
    + ) => setIsChecked(event.target.checked)} + > + + {t('qualityFeedback.doNotAskAgain')} + + +
    +
    +
    + ); +}; diff --git a/src/script/components/Modals/QualityFeedbackModal/constants.ts b/src/script/components/Modals/QualityFeedbackModal/constants.ts new file mode 100644 index 00000000000..6e1b355a767 --- /dev/null +++ b/src/script/components/Modals/QualityFeedbackModal/constants.ts @@ -0,0 +1,35 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {TIME_IN_MILLIS} from 'Util/TimeUtil'; + +import {RatingListItem} from './typings'; + +export const ratingListItems: RatingListItem[] = [ + {value: 1, headingTranslationKey: 'qualityFeedback.bad'}, + {value: 2}, + {value: 3, headingTranslationKey: 'qualityFeedback.fair'}, + {value: 4}, + {value: 5, headingTranslationKey: 'qualityFeedback.excellent'}, +]; + +const MUTE_INTERVAL_DAYS = 3; +export const CALL_SURVEY_MUTE_INTERVAL = TIME_IN_MILLIS.DAY * MUTE_INTERVAL_DAYS; + +export const CALL_QUALITY_FEEDBACK_KEY = 'CALL_QUALITY_FEEDBACK'; diff --git a/src/script/components/Modals/QualityFeedbackModal/index.ts b/src/script/components/Modals/QualityFeedbackModal/index.ts new file mode 100644 index 00000000000..285eceb0b26 --- /dev/null +++ b/src/script/components/Modals/QualityFeedbackModal/index.ts @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './QualityFeedbackModal'; diff --git a/src/script/components/Modals/QualityFeedbackModal/typings.tsx b/src/script/components/Modals/QualityFeedbackModal/typings.tsx new file mode 100644 index 00000000000..f0d99499322 --- /dev/null +++ b/src/script/components/Modals/QualityFeedbackModal/typings.tsx @@ -0,0 +1,30 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {StringIdentifer} from 'Util/LocalizerUtil'; + +export type RatingListItem = { + value: number; + headingTranslationKey?: StringIdentifer; +}; + +export enum RatingListLabel { + ANSWERED = 'answered', + DISMISSED = 'dismissed', +} diff --git a/src/script/components/Modals/ServiceModal/ServiceModal.tsx b/src/script/components/Modals/ServiceModal/ServiceModal.tsx index 7ee33fc3236..421f27cc575 100644 --- a/src/script/components/Modals/ServiceModal/ServiceModal.tsx +++ b/src/script/components/Modals/ServiceModal/ServiceModal.tsx @@ -21,7 +21,7 @@ import React from 'react'; import {Avatar, AVATAR_SIZE} from 'Components/Avatar'; import * as Icon from 'Components/Icon'; -import {ModalComponent} from 'Components/ModalComponent'; +import {ModalComponent} from 'Components/Modals/ModalComponent'; import {SidebarTabs, useSidebarStore} from 'src/script/page/LeftSidebar/panels/Conversations/useSidebarStore'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {t} from 'Util/LocalizerUtil'; diff --git a/src/script/components/Modals/UserModal/UserModal.tsx b/src/script/components/Modals/UserModal/UserModal.tsx index 6af97306fc7..d485a54ae76 100644 --- a/src/script/components/Modals/UserModal/UserModal.tsx +++ b/src/script/components/Modals/UserModal/UserModal.tsx @@ -27,7 +27,7 @@ import {Link, LinkVariant} from '@wireapp/react-ui-kit'; import {FadingScrollbar} from 'Components/FadingScrollbar'; import * as Icon from 'Components/Icon'; -import {ModalComponent} from 'Components/ModalComponent'; +import {ModalComponent} from 'Components/Modals/ModalComponent'; import {EnrichedFields} from 'Components/panel/EnrichedFields'; import {UserActions} from 'Components/panel/UserActions'; import {UserDetails} from 'Components/panel/UserDetails'; diff --git a/src/script/components/ParticipantItemContent/ParticipantItem.styles.ts b/src/script/components/ParticipantItemContent/ParticipantItem.styles.ts index 063b3cce28d..58cfe3c99cb 100644 --- a/src/script/components/ParticipantItemContent/ParticipantItem.styles.ts +++ b/src/script/components/ParticipantItemContent/ParticipantItem.styles.ts @@ -23,6 +23,7 @@ export const listWrapper = ({isHighlighted = false, noUnderline = false, noInter position: 'relative', display: 'block', margin: '1px', + alignItems: 'center', '&:hover, &:focus, &:focus-visible': { background: 'var(--app-bg-secondary)', diff --git a/src/script/components/ParticipantItemContent/ParticipantItemContent.tsx b/src/script/components/ParticipantItemContent/ParticipantItemContent.tsx index 14b0abfb67a..9d8dc4e9379 100644 --- a/src/script/components/ParticipantItemContent/ParticipantItemContent.tsx +++ b/src/script/components/ParticipantItemContent/ParticipantItemContent.tsx @@ -21,10 +21,9 @@ import React from 'react'; import ko from 'knockout'; +import {UserBlockedBadge, UserVerificationBadges} from 'Components/Badge'; import * as Icon from 'Components/Icon'; -import {UserBlockedBadge} from 'Components/UserBlockedBadge/UserBlockedBadge'; import {UserInfo} from 'Components/UserInfo'; -import {UserVerificationBadges} from 'Components/VerificationBadge'; import {User} from 'src/script/entity/User'; import {ServiceEntity} from 'src/script/integration/ServiceEntity'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; diff --git a/src/script/components/PasswordGeneratorButton.test.tsx b/src/script/components/PasswordGeneratorButton/PasswordGeneratorButton.test.tsx similarity index 98% rename from src/script/components/PasswordGeneratorButton.test.tsx rename to src/script/components/PasswordGeneratorButton/PasswordGeneratorButton.test.tsx index e9f8783cacc..f285704205a 100644 --- a/src/script/components/PasswordGeneratorButton.test.tsx +++ b/src/script/components/PasswordGeneratorButton/PasswordGeneratorButton.test.tsx @@ -22,7 +22,7 @@ import {act} from 'react-dom/test-utils'; import {PasswordGeneratorButton} from './PasswordGeneratorButton'; -import {withTheme} from '../auth/util/test/TestUtil'; +import {withTheme} from '../../auth/util/test/TestUtil'; describe('PasswordGeneratorButton', () => { it('calls onGeneratePassword prop with a random password when clicked', () => { diff --git a/src/script/components/PasswordGeneratorButton.tsx b/src/script/components/PasswordGeneratorButton/PasswordGeneratorButton.tsx similarity index 88% rename from src/script/components/PasswordGeneratorButton.tsx rename to src/script/components/PasswordGeneratorButton/PasswordGeneratorButton.tsx index 06e436a48d7..4a8b24d4daf 100644 --- a/src/script/components/PasswordGeneratorButton.tsx +++ b/src/script/components/PasswordGeneratorButton/PasswordGeneratorButton.tsx @@ -17,8 +17,6 @@ * */ -import React from 'react'; - import {Button, ButtonVariant} from '@wireapp/react-ui-kit'; import * as Icon from 'Components/Icon'; @@ -30,7 +28,7 @@ interface PasswordGeneratorButtonProps { onGeneratePassword: (password: string) => void; } -const PasswordGeneratorButton: React.FC = ({passwordLength = 8, onGeneratePassword}) => { +export const PasswordGeneratorButton = ({passwordLength = 8, onGeneratePassword}: PasswordGeneratorButtonProps) => { const generatePassword = () => { const password = generateRandomPassword(passwordLength); onGeneratePassword(password); @@ -43,5 +41,3 @@ const PasswordGeneratorButton: React.FC = ({passwo ); }; - -export {PasswordGeneratorButton}; diff --git a/src/script/components/PasswordGeneratorButton/index.tsx b/src/script/components/PasswordGeneratorButton/index.tsx new file mode 100644 index 00000000000..b760c03373f --- /dev/null +++ b/src/script/components/PasswordGeneratorButton/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './PasswordGeneratorButton'; diff --git a/src/script/components/RichTextEditor/plugins/InlineEmojiReplacementPlugin/inlineReplacements.ts b/src/script/components/RichTextEditor/plugins/InlineEmojiReplacementPlugin/inlineReplacements.ts index 117e196352a..5919f04806f 100644 --- a/src/script/components/RichTextEditor/plugins/InlineEmojiReplacementPlugin/inlineReplacements.ts +++ b/src/script/components/RichTextEditor/plugins/InlineEmojiReplacementPlugin/inlineReplacements.ts @@ -90,10 +90,10 @@ export const inlineReplacements: Emoticon[] = [ ], }, { - name: 'frowning', - emoji: '😦', + name: 'slight frown', + emoji: '🙁', tags: [], - description: 'frowning face with open mouth', + description: 'slightly frowning face', emoticons: [':(', ':[', ':-(', ':-[', '=(', '=[', '=-(', '=-['], }, { diff --git a/src/script/components/SearchInput.tsx b/src/script/components/SearchInput/SearchInput.tsx similarity index 94% rename from src/script/components/SearchInput.tsx rename to src/script/components/SearchInput/SearchInput.tsx index 52f9628b480..c0635b92fd6 100644 --- a/src/script/components/SearchInput.tsx +++ b/src/script/components/SearchInput/SearchInput.tsx @@ -24,10 +24,9 @@ import cx from 'classnames'; import {isEnterKey} from 'Util/KeyboardUtil'; import {t} from 'Util/LocalizerUtil'; -import * as Icon from './Icon'; - -import type {User} from '../entity/User'; -import {MAX_HANDLE_LENGTH} from '../user/UserHandleGenerator'; +import type {User} from '../../entity/User'; +import {MAX_HANDLE_LENGTH} from '../../user/UserHandleGenerator'; +import * as Icon from '../Icon'; interface SearchInputProps { onEnter?: (event: React.KeyboardEvent) => void | Promise; @@ -39,7 +38,7 @@ interface SearchInputProps { setInput: (input: string) => void; } -export const SearchInput: React.FC = ({ +export const SearchInput = ({ onEnter, input, selectedUsers = [], diff --git a/src/script/components/SearchInput/index.tsx b/src/script/components/SearchInput/index.tsx new file mode 100644 index 00000000000..f94bfb6a34e --- /dev/null +++ b/src/script/components/SearchInput/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './SearchInput'; diff --git a/src/script/components/SelectText.test.tsx b/src/script/components/SelectText/SelectText.test.tsx similarity index 94% rename from src/script/components/SelectText.test.tsx rename to src/script/components/SelectText/SelectText.test.tsx index bcda79d3c27..4e7a54393ef 100644 --- a/src/script/components/SelectText.test.tsx +++ b/src/script/components/SelectText/SelectText.test.tsx @@ -17,8 +17,7 @@ * */ -import {render} from '@testing-library/react'; -import {act} from 'react-dom/test-utils'; +import {act, render} from '@testing-library/react'; import {SelectText} from './SelectText'; diff --git a/src/script/components/SelectText.tsx b/src/script/components/SelectText/SelectText.tsx similarity index 92% rename from src/script/components/SelectText.tsx rename to src/script/components/SelectText/SelectText.tsx index 805b4eff6f6..f780da8b589 100644 --- a/src/script/components/SelectText.tsx +++ b/src/script/components/SelectText/SelectText.tsx @@ -30,7 +30,7 @@ export interface SelectTextProps { dataUieName?: string; } -const SelectText: React.FC = ({text, className = '', dataUieName = 'select-text'}) => { +export const SelectText = ({text, className = '', dataUieName = 'select-text'}: SelectTextProps) => { const onClick = ({currentTarget}: React.UIEvent) => { if (window.getSelection) { const selectionRange = document.createRange(); @@ -58,5 +58,3 @@ const SelectText: React.FC = ({text, className = '', dataUieNam ); }; - -export {SelectText}; diff --git a/src/script/components/SelectText/index.tsx b/src/script/components/SelectText/index.tsx new file mode 100644 index 00000000000..1f4cf9c33bc --- /dev/null +++ b/src/script/components/SelectText/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './SelectText'; diff --git a/src/script/components/TitleBar/TitleBar.tsx b/src/script/components/TitleBar/TitleBar.tsx index ab62488b2f2..74aa8a4391e 100644 --- a/src/script/components/TitleBar/TitleBar.tsx +++ b/src/script/components/TitleBar/TitleBar.tsx @@ -27,10 +27,10 @@ import {container} from 'tsyringe'; import {IconButton, IconButtonVariant, useMatchMedia} from '@wireapp/react-ui-kit'; import {WebAppEvents} from '@wireapp/webapp-events'; +import {ConversationVerificationBadges} from 'Components/Badge'; import {useCallAlertState} from 'Components/calling/useCallAlertState'; import * as Icon from 'Components/Icon'; import {LegalHoldDot} from 'Components/LegalHoldDot'; -import {ConversationVerificationBadges} from 'Components/VerificationBadge'; import {User} from 'src/script/entity/User'; import {useAppMainState, ViewType} from 'src/script/page/state'; import {ContentState} from 'src/script/page/useAppState'; diff --git a/src/script/components/UserDevices.tsx b/src/script/components/UserDevices/UserDevices.tsx similarity index 72% rename from src/script/components/UserDevices.tsx rename to src/script/components/UserDevices/UserDevices.tsx index 26ea277a512..1f4bced2b31 100644 --- a/src/script/components/UserDevices.tsx +++ b/src/script/components/UserDevices/UserDevices.tsx @@ -17,7 +17,7 @@ * */ -import React, {useEffect, useMemo, useState} from 'react'; +import {useEffect, useMemo, useState} from 'react'; import {ClientClassification} from '@wireapp/api-client/lib/client/'; @@ -26,50 +26,23 @@ import {t} from 'Util/LocalizerUtil'; import {getLogger} from 'Util/Logger'; import {capitalizeFirstChar} from 'Util/StringUtil'; -import {DeviceDetails} from './userDevices/DeviceDetails'; -import {DeviceList} from './userDevices/DeviceList'; -import {NoDevicesFound} from './userDevices/NoDevicesFound'; -import {SelfFingerprint} from './userDevices/SelfFingerprint'; - -import {ClientRepository, ClientEntity} from '../client'; -import {MessageRepository} from '../conversation/MessageRepository'; -import {CryptographyRepository} from '../cryptography/CryptographyRepository'; -import {User} from '../entity/User'; -import {useUserIdentity} from '../hooks/useDeviceIdentities'; - -enum FIND_MODE { - FOUND = 'UserDevices.MODE.FOUND', - NOT_FOUND = 'UserDevices.MODE.NOT_FOUND', - REQUESTING = 'UserDevices.MODE.REQUESTING', -} +import {DeviceDetails} from './components/DeviceDetails'; +import {DeviceList} from './components/DeviceList'; +import {NoDevicesFound} from './components/NoDevicesFound'; +import {SelfFingerprint} from './components/SelfFingerprint'; +import {FIND_MODE, UserDevicesState} from './UserDevices.types'; -export enum UserDevicesState { - DEVICE_DETAILS = 'UserDevices.DEVICE_DETAILS', - DEVICE_LIST = 'UserDevices.DEVICE_LIST', - SELF_FINGERPRINT = 'UserDevices.SELF_FINGERPRINT', -} +import {ClientRepository, ClientEntity} from '../../client'; +import {MessageRepository} from '../../conversation/MessageRepository'; +import {CryptographyRepository} from '../../cryptography/CryptographyRepository'; +import {User} from '../../entity/User'; +import {useUserIdentity} from '../../hooks/useDeviceIdentities'; export interface UserDevicesHistoryEntry { headline: string; state: UserDevicesState; } -export const useUserDevicesHistory = () => { - const [history, setHistory] = useState([ - {headline: '', state: UserDevicesState.DEVICE_LIST}, - ]); - const current = useMemo(() => history[history.length - 1], [history]); - return { - current, - goBack: () => { - setHistory(history.slice(0, -1)); - }, - goTo: (state: UserDevicesState, headline: string) => { - setHistory([...history, {headline, state}]); - }, - }; -}; - const sortUserDevices = (devices: ClientEntity[]): ClientEntity[] => { const [legalholdDevices, otherDevices] = partition( devices, @@ -89,7 +62,7 @@ interface UserDevicesProps { groupId?: string; } -const UserDevices: React.FC = ({ +export const UserDevices = ({ noPadding = false, current, user, @@ -98,7 +71,7 @@ const UserDevices: React.FC = ({ messageRepository, cryptographyRepository, groupId, -}) => { +}: UserDevicesProps) => { const [selectedClient, setSelectedClient] = useState(); const {getDeviceIdentity} = useUserIdentity(user.qualifiedId, groupId); const [deviceMode, setDeviceMode] = useState(FIND_MODE.REQUESTING); @@ -118,7 +91,7 @@ const UserDevices: React.FC = ({ } } } catch (error) { - logger.error(`Unable to retrieve clients for user '${user.id}': ${error.message || error}`); + logger.error(`Unable to retrieve clients for user '${user.id}': ${(error as Error).message || error}`); } })(); }, [user]); @@ -165,5 +138,3 @@ const UserDevices: React.FC = ({ ); }; - -export {UserDevices}; diff --git a/src/script/components/UserDevices/UserDevices.types.tsx b/src/script/components/UserDevices/UserDevices.types.tsx new file mode 100644 index 00000000000..95e71b43792 --- /dev/null +++ b/src/script/components/UserDevices/UserDevices.types.tsx @@ -0,0 +1,30 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export enum FIND_MODE { + FOUND = 'UserDevices.MODE.FOUND', + NOT_FOUND = 'UserDevices.MODE.NOT_FOUND', + REQUESTING = 'UserDevices.MODE.REQUESTING', +} + +export enum UserDevicesState { + DEVICE_DETAILS = 'UserDevices.DEVICE_DETAILS', + DEVICE_LIST = 'UserDevices.DEVICE_LIST', + SELF_FINGERPRINT = 'UserDevices.SELF_FINGERPRINT', +} diff --git a/src/script/components/userDevices/DeviceCard.test.tsx b/src/script/components/UserDevices/components/DeviceCard/DeviceCard.test.tsx similarity index 96% rename from src/script/components/userDevices/DeviceCard.test.tsx rename to src/script/components/UserDevices/components/DeviceCard/DeviceCard.test.tsx index 75ab4d0265b..09bbb087049 100644 --- a/src/script/components/userDevices/DeviceCard.test.tsx +++ b/src/script/components/UserDevices/components/DeviceCard/DeviceCard.test.tsx @@ -23,7 +23,7 @@ import ko from 'knockout'; import {DeviceCard} from './DeviceCard'; -import type {ClientEntity} from '../../client/ClientEntity'; +import type {ClientEntity} from '../../../../client/ClientEntity'; function createClientEntity(clientEntity: Partial): ClientEntity { const device: Partial = { diff --git a/src/script/components/userDevices/DeviceCard.tsx b/src/script/components/UserDevices/components/DeviceCard/DeviceCard.tsx similarity index 91% rename from src/script/components/userDevices/DeviceCard.tsx rename to src/script/components/UserDevices/components/DeviceCard/DeviceCard.tsx index d2b1898a562..e700ca53bff 100644 --- a/src/script/components/userDevices/DeviceCard.tsx +++ b/src/script/components/UserDevices/components/DeviceCard/DeviceCard.tsx @@ -20,17 +20,17 @@ import {ClientClassification} from '@wireapp/api-client/lib/client'; import cx from 'classnames'; +import {DeviceVerificationBadges} from 'Components/Badge'; +import {LegalHoldDot} from 'Components/LegalHoldDot'; import {useMessageFocusedTabIndex} from 'Components/MessagesList/Message/util'; -import {DeviceVerificationBadges} from 'Components/VerificationBadge'; import {WireIdentity} from 'src/script/E2EIdentity'; import {handleKeyDown} from 'Util/KeyboardUtil'; import {t} from 'Util/LocalizerUtil'; import {splitFingerprint} from 'Util/StringUtil'; -import {type ClientEntity} from '../../client/ClientEntity'; -import {FormattedId} from '../../page/MainContent/panels/preferences/DevicesPreferences/components/FormattedId'; -import * as Icon from '../Icon'; -import {LegalHoldDot} from '../LegalHoldDot'; +import {type ClientEntity} from '../../../../client/ClientEntity'; +import {FormattedId} from '../../../../page/MainContent/panels/preferences/DevicesPreferences/components/FormattedId'; +import * as Icon from '../../../Icon'; export interface DeviceCardProps { click?: (device: ClientEntity) => void; diff --git a/src/script/components/UserDevices/components/DeviceCard/index.tsx b/src/script/components/UserDevices/components/DeviceCard/index.tsx new file mode 100644 index 00000000000..8c2040396b2 --- /dev/null +++ b/src/script/components/UserDevices/components/DeviceCard/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './DeviceCard'; diff --git a/src/script/components/userDevices/DeviceDetails.tsx b/src/script/components/UserDevices/components/DeviceDetails/DeviceDetails.tsx similarity index 91% rename from src/script/components/userDevices/DeviceDetails.tsx rename to src/script/components/UserDevices/components/DeviceDetails/DeviceDetails.tsx index b32fb4cf47f..6c5bf7fb1a5 100644 --- a/src/script/components/userDevices/DeviceDetails.tsx +++ b/src/script/components/UserDevices/components/DeviceDetails/DeviceDetails.tsx @@ -33,14 +33,14 @@ import {t} from 'Util/LocalizerUtil'; import type {Logger} from 'Util/Logger'; import {splitFingerprint} from 'Util/StringUtil'; -import type {ClientRepository, ClientEntity} from '../../client'; -import {Config} from '../../Config'; -import {ConversationState} from '../../conversation/ConversationState'; -import type {MessageRepository} from '../../conversation/MessageRepository'; -import type {CryptographyRepository} from '../../cryptography/CryptographyRepository'; -import type {User} from '../../entity/User'; -import {MotionDuration} from '../../motion/MotionDuration'; -import {FormattedId} from '../../page/MainContent/panels/preferences/DevicesPreferences/components/FormattedId'; +import type {ClientRepository, ClientEntity} from '../../../../client'; +import {Config} from '../../../../Config'; +import {ConversationState} from '../../../../conversation/ConversationState'; +import type {MessageRepository} from '../../../../conversation/MessageRepository'; +import type {CryptographyRepository} from '../../../../cryptography/CryptographyRepository'; +import type {User} from '../../../../entity/User'; +import {MotionDuration} from '../../../../motion/MotionDuration'; +import {FormattedId} from '../../../../page/MainContent/panels/preferences/DevicesPreferences/components/FormattedId'; interface DeviceDetailsProps { clickToShowSelfFingerprint: () => void; diff --git a/src/script/components/UserDevices/components/DeviceDetails/index.tsx b/src/script/components/UserDevices/components/DeviceDetails/index.tsx new file mode 100644 index 00000000000..b945c2fb7a6 --- /dev/null +++ b/src/script/components/UserDevices/components/DeviceDetails/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './DeviceDetails'; diff --git a/src/script/components/userDevices/DeviceList.tsx b/src/script/components/UserDevices/components/DeviceList/DeviceList.tsx similarity index 87% rename from src/script/components/userDevices/DeviceList.tsx rename to src/script/components/UserDevices/components/DeviceList/DeviceList.tsx index a0dc7c891ce..166e4799ba6 100644 --- a/src/script/components/userDevices/DeviceList.tsx +++ b/src/script/components/UserDevices/components/DeviceList/DeviceList.tsx @@ -17,19 +17,16 @@ * */ -import React from 'react'; - import cx from 'classnames'; import {WireIdentity} from 'src/script/E2EIdentity'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {t} from 'Util/LocalizerUtil'; -import {DeviceCard} from './DeviceCard'; - -import type {ClientEntity} from '../../client/ClientEntity'; -import {Config} from '../../Config'; -import type {User} from '../../entity/User'; +import type {ClientEntity} from '../../../../client/ClientEntity'; +import {Config} from '../../../../Config'; +import type {User} from '../../../../entity/User'; +import {DeviceCard} from '../DeviceCard'; interface DeviceListProps { clickOnDevice: (client: ClientEntity) => void; @@ -39,7 +36,7 @@ interface DeviceListProps { user: User; } -const DeviceList: React.FC = ({user, getDeviceIdentity, noPadding, clients, clickOnDevice}) => { +export const DeviceList = ({user, getDeviceIdentity, noPadding, clients, clickOnDevice}: DeviceListProps) => { const {name: userName} = useKoSubscribableChildren(user, ['name']); return ( @@ -80,5 +77,3 @@ const DeviceList: React.FC = ({user, getDeviceIdentity, noPaddi ); }; - -export {DeviceList}; diff --git a/src/script/components/UserDevices/components/DeviceList/index.tsx b/src/script/components/UserDevices/components/DeviceList/index.tsx new file mode 100644 index 00000000000..27edf8b1a28 --- /dev/null +++ b/src/script/components/UserDevices/components/DeviceList/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './DeviceList'; diff --git a/src/script/components/userDevices/NoDevicesFound.tsx b/src/script/components/UserDevices/components/NoDevicesFound/NoDevicesFound.tsx similarity index 88% rename from src/script/components/userDevices/NoDevicesFound.tsx rename to src/script/components/UserDevices/components/NoDevicesFound/NoDevicesFound.tsx index 662407c3348..ca13024aa5a 100644 --- a/src/script/components/userDevices/NoDevicesFound.tsx +++ b/src/script/components/UserDevices/components/NoDevicesFound/NoDevicesFound.tsx @@ -17,21 +17,19 @@ * */ -import React from 'react'; - import cx from 'classnames'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {t} from 'Util/LocalizerUtil'; -import type {User} from '../../entity/User'; +import type {User} from '../../../../entity/User'; interface NoDevicesFoundProps { noPadding: boolean; user: User; } -const NoDevicesFound: React.FC = ({user, noPadding}) => { +export const NoDevicesFound = ({user, noPadding}: NoDevicesFoundProps) => { const {name: userName} = useKoSubscribableChildren(user, ['name']); return ( @@ -46,5 +44,3 @@ const NoDevicesFound: React.FC = ({user, noPadding}) => { ); }; - -export {NoDevicesFound}; diff --git a/src/script/components/UserDevices/components/NoDevicesFound/index.tsx b/src/script/components/UserDevices/components/NoDevicesFound/index.tsx new file mode 100644 index 00000000000..c922c4d1127 --- /dev/null +++ b/src/script/components/UserDevices/components/NoDevicesFound/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './NoDevicesFound'; diff --git a/src/script/components/userDevices/SelfFingerprint.tsx b/src/script/components/UserDevices/components/SelfFingerprint/SelfFingerprint.tsx similarity index 83% rename from src/script/components/userDevices/SelfFingerprint.tsx rename to src/script/components/UserDevices/components/SelfFingerprint/SelfFingerprint.tsx index b0cec4224c6..4402177ddaf 100644 --- a/src/script/components/userDevices/SelfFingerprint.tsx +++ b/src/script/components/UserDevices/components/SelfFingerprint/SelfFingerprint.tsx @@ -17,7 +17,7 @@ * */ -import React, {useEffect, useState} from 'react'; +import {useEffect, useState} from 'react'; import {amplify} from 'amplify'; import cx from 'classnames'; @@ -28,11 +28,10 @@ import {WebAppEvents} from '@wireapp/webapp-events'; import {t} from 'Util/LocalizerUtil'; import {splitFingerprint} from 'Util/StringUtil'; -import {DeviceCard} from './DeviceCard'; - -import {ClientState} from '../../client/ClientState'; -import type {CryptographyRepository} from '../../cryptography/CryptographyRepository'; -import {FormattedId} from '../../page/MainContent/panels/preferences/DevicesPreferences/components/FormattedId'; +import {ClientState} from '../../../../client/ClientState'; +import type {CryptographyRepository} from '../../../../cryptography/CryptographyRepository'; +import {FormattedId} from '../../../../page/MainContent/panels/preferences/DevicesPreferences/components/FormattedId'; +import {DeviceCard} from '../DeviceCard'; interface SelfFingerprintProps { clientState?: ClientState; @@ -40,11 +39,11 @@ interface SelfFingerprintProps { noPadding: boolean; } -export const SelfFingerprint: React.FC = ({ +export const SelfFingerprint = ({ cryptographyRepository, noPadding, clientState = container.resolve(ClientState), -}) => { +}: SelfFingerprintProps) => { const [localFingerprint, setLocalFingerprint] = useState(''); useEffect(() => { cryptographyRepository.getLocalFingerprint().then(setLocalFingerprint); diff --git a/src/script/components/UserDevices/components/SelfFingerprint/index.tsx b/src/script/components/UserDevices/components/SelfFingerprint/index.tsx new file mode 100644 index 00000000000..d25adb76675 --- /dev/null +++ b/src/script/components/UserDevices/components/SelfFingerprint/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './SelfFingerprint'; diff --git a/src/script/components/UserDevices/index.tsx b/src/script/components/UserDevices/index.tsx new file mode 100644 index 00000000000..fceb2679c74 --- /dev/null +++ b/src/script/components/UserDevices/index.tsx @@ -0,0 +1,21 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './UserDevices'; +export * from './UserDevices.types'; diff --git a/src/script/components/UserInfo.tsx b/src/script/components/UserInfo/UserInfo.tsx similarity index 97% rename from src/script/components/UserInfo.tsx rename to src/script/components/UserInfo/UserInfo.tsx index 9acb7d0d0d1..0a8f511e64a 100644 --- a/src/script/components/UserInfo.tsx +++ b/src/script/components/UserInfo/UserInfo.tsx @@ -23,12 +23,11 @@ import {CSSObject} from '@emotion/react'; import cx from 'classnames'; import {selfIndicator} from 'Components/ParticipantItemContent/ParticipantItem.styles'; +import {UserName} from 'Components/UserName'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {KEY} from 'Util/KeyboardUtil'; -import {UserName} from './UserName'; - -import {User} from '../entity/User'; +import {User} from '../../entity/User'; interface UserInfoProps { user: User; diff --git a/src/script/components/Checkbox/index.ts b/src/script/components/UserInfo/index.tsx similarity index 90% rename from src/script/components/Checkbox/index.ts rename to src/script/components/UserInfo/index.tsx index ddc633025b6..2b17c75022d 100644 --- a/src/script/components/Checkbox/index.ts +++ b/src/script/components/UserInfo/index.tsx @@ -1,6 +1,6 @@ /* * Wire - * Copyright (C) 2022 Wire Swiss GmbH + * Copyright (C) 2024 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,4 +17,4 @@ * */ -export * from './Checkbox'; +export * from './UserInfo'; diff --git a/src/script/components/UserList/UserList.tsx b/src/script/components/UserList/UserList.tsx index 25269f79852..764afb6188d 100644 --- a/src/script/components/UserList/UserList.tsx +++ b/src/script/components/UserList/UserList.tsx @@ -23,6 +23,7 @@ import cx from 'classnames'; import {container} from 'tsyringe'; import * as Icon from 'Components/Icon'; +import {InViewport} from 'Components/InViewport'; import {collapseButton, collapseIcon} from 'Components/UserList/UserList.styles'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {isEnterKey, isSpaceKey} from 'Util/KeyboardUtil'; @@ -35,7 +36,6 @@ import {ConversationState} from '../../conversation/ConversationState'; import type {Conversation} from '../../entity/Conversation'; import type {User} from '../../entity/User'; import {TeamState} from '../../team/TeamState'; -import {InViewport} from '../utils/InViewport'; export enum UserlistMode { COMPACT = 'UserlistMode.COMPACT', diff --git a/src/script/components/UserList/components/UserListItem/UserListItem.tsx b/src/script/components/UserList/components/UserListItem/UserListItem.tsx index c215a6a1d9c..7f5996a51e7 100644 --- a/src/script/components/UserList/components/UserListItem/UserListItem.tsx +++ b/src/script/components/UserList/components/UserListItem/UserListItem.tsx @@ -24,9 +24,9 @@ import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; import {Checkbox, CheckboxLabel} from '@wireapp/react-ui-kit'; import {Avatar, AVATAR_SIZE} from 'Components/Avatar'; +import {UserStatusBadges} from 'Components/Badge'; import {ParticipantItemContent} from 'Components/ParticipantItemContent'; import {listItem, listWrapper} from 'Components/ParticipantItemContent/ParticipantItem.styles'; -import {UserStatusBadges} from 'Components/UserBadges'; import {UserlistMode} from 'Components/UserList'; import {useUserName} from 'Components/UserName'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; diff --git a/src/script/components/UserName.tsx b/src/script/components/UserName/UserName.tsx similarity index 97% rename from src/script/components/UserName.tsx rename to src/script/components/UserName/UserName.tsx index d1dac0d4725..bedd39b006b 100644 --- a/src/script/components/UserName.tsx +++ b/src/script/components/UserName/UserName.tsx @@ -20,7 +20,7 @@ import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {t} from 'Util/LocalizerUtil'; -import {User} from '../entity/User'; +import {User} from '../../entity/User'; interface UserNameProps { user: User; diff --git a/src/script/components/UserName/index.tsx b/src/script/components/UserName/index.tsx new file mode 100644 index 00000000000..4afb261c950 --- /dev/null +++ b/src/script/components/UserName/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './UserName'; diff --git a/src/script/components/UserSearchableList.tsx b/src/script/components/UserSearchableList/UserSearchableList.tsx similarity index 86% rename from src/script/components/UserSearchableList.tsx rename to src/script/components/UserSearchableList/UserSearchableList.tsx index c960d5182fe..3439a9734ce 100644 --- a/src/script/components/UserSearchableList.tsx +++ b/src/script/components/UserSearchableList/UserSearchableList.tsx @@ -23,18 +23,17 @@ import {QualifiedId} from '@wireapp/api-client/lib/user'; import {container} from 'tsyringe'; import {debounce} from 'underscore'; +import {UserList} from 'Components/UserList'; import {partition} from 'Util/ArrayUtil'; import {t} from 'Util/LocalizerUtil'; import {matchQualifiedIds} from 'Util/QualifiedId'; import {sortByPriority} from 'Util/StringUtil'; -import {UserList} from './UserList'; - -import {ConversationState} from '../conversation/ConversationState'; -import type {User} from '../entity/User'; -import {SearchRepository} from '../search/SearchRepository'; -import type {TeamRepository} from '../team/TeamRepository'; -import {TeamState} from '../team/TeamState'; +import {ConversationState} from '../../conversation/ConversationState'; +import type {User} from '../../entity/User'; +import {SearchRepository} from '../../search/SearchRepository'; +import type {TeamRepository} from '../../team/TeamRepository'; +import {TeamState} from '../../team/TeamState'; export type UserListProps = React.ComponentProps & { conversationState?: ConversationState; @@ -54,10 +53,12 @@ export type UserListProps = React.ComponentProps & { excludeUsers?: QualifiedId[]; /** will do an extra request to the server when user types in (otherwise will only lookup given local users) */ allowRemoteSearch?: boolean; + filterRemoteTeamUsers?: boolean; }; -export const UserSearchableList: React.FC = ({ +export const UserSearchableList = ({ onUpdateSelectedUsers, + filterRemoteTeamUsers = false, dataUieName = '', filter = '', highlightedUsers, @@ -67,7 +68,7 @@ export const UserSearchableList: React.FC = ({ users, teamState = container.resolve(TeamState), ...props -}) => { +}: UserListProps) => { const {searchRepository, teamRepository, selfFirst, ...userListProps} = props; const {conversationState = container.resolve(ConversationState)} = props; @@ -92,7 +93,9 @@ export const UserSearchableList: React.FC = ({ // We shouldn't show any members that have the 'external' role and are not already locally known. const nonExternalMembers = await teamRepository.filterExternals(uniqueMembers); - setRemoteTeamMembers(nonExternalMembers); + setRemoteTeamMembers( + filterRemoteTeamUsers ? teamRepository.filterRemoteDomainUsers(nonExternalMembers) : nonExternalMembers, + ); }, 300), [], ); @@ -116,13 +119,15 @@ export const UserSearchableList: React.FC = ({ } if (!selfFirst) { - setFilteredUsers(results); + setFilteredUsers(filterRemoteTeamUsers ? teamRepository.filterRemoteDomainUsers(results) : results); return; } // make sure the self user is the first one in the list const [selfUser, otherUsers] = partition(results, user => user.isMe); - setFilteredUsers(selfUser.concat(otherUsers)); + + const concatUsers = selfUser.concat(otherUsers); + setFilteredUsers(filterRemoteTeamUsers ? teamRepository.filterRemoteDomainUsers(concatUsers) : concatUsers); }, [filter, users.length]); const foundUserEntities = () => { diff --git a/src/script/components/UserSearchableList/index.tsx b/src/script/components/UserSearchableList/index.tsx new file mode 100644 index 00000000000..1b97138a672 --- /dev/null +++ b/src/script/components/UserSearchableList/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './UserSearchableList'; diff --git a/src/script/components/VerifiedIcon.tsx b/src/script/components/VerificationIcon/VerificationIcon.tsx similarity index 82% rename from src/script/components/VerifiedIcon.tsx rename to src/script/components/VerificationIcon/VerificationIcon.tsx index 9d41e284aef..f855d89b7bb 100644 --- a/src/script/components/VerifiedIcon.tsx +++ b/src/script/components/VerificationIcon/VerificationIcon.tsx @@ -17,17 +17,15 @@ * */ -import React from 'react'; +import {VerifiedIcon, NotVerifiedIcon} from '../Icon'; -import {VerifiedIcon, NotVerifiedIcon} from './Icon'; - -export interface VerifiedIconProps { +export interface VerificationIconProps { className?: string; dataUieName?: string; isVerified: boolean; } -export const VerificationIcon: React.FC = ({isVerified, dataUieName, className}) => { +export const VerificationIcon = ({isVerified, dataUieName, className}: VerificationIconProps) => { return isVerified ? ( ) : ( diff --git a/src/script/components/VerificationIcon/index.tsx b/src/script/components/VerificationIcon/index.tsx new file mode 100644 index 00000000000..e7e8bd88a2a --- /dev/null +++ b/src/script/components/VerificationIcon/index.tsx @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './VerificationIcon'; diff --git a/src/script/components/calling/CallParticipantsListItem/CallParticipantItemContent/CallParticipantItemContent.styles.ts b/src/script/components/calling/CallParticipantsListItem/CallParticipantItemContent/CallParticipantItemContent.styles.ts index c21460b60b0..23600012121 100644 --- a/src/script/components/calling/CallParticipantsListItem/CallParticipantItemContent/CallParticipantItemContent.styles.ts +++ b/src/script/components/calling/CallParticipantsListItem/CallParticipantItemContent/CallParticipantItemContent.styles.ts @@ -29,7 +29,7 @@ export const chevronIcon: CSSObject = { justifyContent: 'center', opacity: '0', transition: 'opacity 0.25s ease-in-out', - width: '16px', + minWidth: '16px', svg: { width: '8px', path: { diff --git a/src/script/components/calling/CallParticipantsListItem/CallParticipantItemContent/CallParticipantItemContent.tsx b/src/script/components/calling/CallParticipantsListItem/CallParticipantItemContent/CallParticipantItemContent.tsx index 8ca26c8701d..d90b1dc7ad8 100644 --- a/src/script/components/calling/CallParticipantsListItem/CallParticipantItemContent/CallParticipantItemContent.tsx +++ b/src/script/components/calling/CallParticipantsListItem/CallParticipantItemContent/CallParticipantItemContent.tsx @@ -60,21 +60,20 @@ export const CallParticipantItemContent = ({ {isSelf &&
    {selfString}
    } + {isAudioEstablished && showContextMenu && ( + + )} - - {isAudioEstablished && showContextMenu && ( - - )} ); }; diff --git a/src/script/components/calling/CallParticipantsListItem/CallParticipantsListItem.styles.ts b/src/script/components/calling/CallParticipantsListItem/CallParticipantsListItem.styles.ts index 7083bc6c757..a05cc0b6ea4 100644 --- a/src/script/components/calling/CallParticipantsListItem/CallParticipantsListItem.styles.ts +++ b/src/script/components/calling/CallParticipantsListItem/CallParticipantsListItem.styles.ts @@ -26,10 +26,7 @@ export const callParticipantListItemWrapper = (isLast = false): CSSObject => ({ '&:hover, &:focus, &:focus-visible': { backgroundColor: 'var(--disabled-call-button-bg)', }, - - ...(isLast && { - borderRadius: '0 0 8px 8px', - }), + borderBottom: isLast ? 'none' : '1px solid var(--border-color)', }); const commonIconStyles = { diff --git a/src/script/components/calling/CallParticipantsListItem/CallParticipantsListItem.tsx b/src/script/components/calling/CallParticipantsListItem/CallParticipantsListItem.tsx index 15e8e2b638d..b58a8468a01 100644 --- a/src/script/components/calling/CallParticipantsListItem/CallParticipantsListItem.tsx +++ b/src/script/components/calling/CallParticipantsListItem/CallParticipantsListItem.tsx @@ -22,7 +22,7 @@ import React from 'react'; import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; import {Avatar, AVATAR_SIZE} from 'Components/Avatar'; -import {UserStatusBadges} from 'Components/UserBadges'; +import {UserStatusBadges} from 'Components/Badge'; import {Participant} from 'src/script/calling/Participant'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {handleKeyDown} from 'Util/KeyboardUtil'; diff --git a/src/script/components/calling/CallingCell.tsx b/src/script/components/calling/CallingCell.tsx deleted file mode 100644 index 226532c9921..00000000000 --- a/src/script/components/calling/CallingCell.tsx +++ /dev/null @@ -1,654 +0,0 @@ -/* - * Wire - * Copyright (C) 2022 Wire Swiss GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * - */ - -import React, {useCallback, useEffect, useState} from 'react'; - -import {DefaultConversationRoleName} from '@wireapp/api-client/lib/conversation/'; -import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; -import cx from 'classnames'; -import {container} from 'tsyringe'; - -import {CALL_TYPE, REASON as CALL_REASON, STATE as CALL_STATE} from '@wireapp/avs'; - -import {Avatar, AVATAR_SIZE, GroupAvatar} from 'Components/Avatar'; -import {Duration} from 'Components/calling/Duration'; -import {GroupVideoGrid} from 'Components/calling/GroupVideoGrid'; -import {useCallAlertState} from 'Components/calling/useCallAlertState'; -import {FadingScrollbar} from 'Components/FadingScrollbar'; -import * as Icon from 'Components/Icon'; -import {ConversationClassifiedBar} from 'Components/input/ClassifiedBar'; -import {usePushToTalk} from 'src/script/hooks/usePushToTalk/usePushToTalk'; -import {useAppMainState, ViewType} from 'src/script/page/state'; -import {useKoSubscribableChildren} from 'Util/ComponentUtil'; -import {isEnterKey, isSpaceOrEnterKey} from 'Util/KeyboardUtil'; -import {t} from 'Util/LocalizerUtil'; -import {sortUsersByPriority} from 'Util/StringUtil'; - -import {CallParticipantsListItem} from './CallParticipantsListItem'; -import {useDetachedCallingFeatureState} from './DetachedCallingCell/DetachedCallingFeature.state'; - -import type {Call} from '../../calling/Call'; -import type {CallingRepository} from '../../calling/CallingRepository'; -import {CallingViewMode, CallState, MuteState} from '../../calling/CallState'; -import type {Participant} from '../../calling/Participant'; -import {useVideoGrid} from '../../calling/videoGridHandler'; -import {generateConversationUrl} from '../../router/routeGenerator'; -import {createNavigate, createNavigateKeyboard} from '../../router/routerBindings'; -import {TeamState} from '../../team/TeamState'; -import {ContextMenuEntry, showContextMenu} from '../../ui/ContextMenu'; -import {CallActions, CallViewTab} from '../../view_model/CallingViewModel'; - -interface VideoCallProps { - hasAccessToCamera?: boolean; - isSelfVerified?: boolean; - teamState?: TeamState; -} - -interface AnsweringControlsProps { - call: Call; - callActions: CallActions; - callingRepository: Pick; - pushToTalkKey: string | null; - isFullUi?: boolean; - callState?: CallState; - classifiedDomains?: string[]; - isTemporaryUser?: boolean; - setMaximizedParticipant?: (participant: Participant | null) => void; -} - -export type CallingCellProps = VideoCallProps & AnsweringControlsProps; - -type labels = {dataUieName: string; text: string}; - -const CallingCell: React.FC = ({ - classifiedDomains, - isTemporaryUser, - call, - callActions, - isFullUi = false, - hasAccessToCamera, - isSelfVerified, - callingRepository, - pushToTalkKey, - setMaximizedParticipant, - teamState = container.resolve(TeamState), - callState = container.resolve(CallState), -}) => { - const {conversation} = call; - const {reason, state, isCbrEnabled, startedAt, participants, maximizedParticipant, muteState} = - useKoSubscribableChildren(call, [ - 'reason', - 'state', - 'isCbrEnabled', - 'startedAt', - 'participants', - 'maximizedParticipant', - 'pages', - 'currentPage', - 'muteState', - ]); - - const { - isGroup, - participating_user_ets: userEts, - selfUser, - display_name: conversationName, - roles, - } = useKoSubscribableChildren(conversation, [ - 'isGroup', - 'participating_user_ets', - 'selfUser', - 'display_name', - 'roles', - ]); - - const {viewMode} = useKoSubscribableChildren(callState, ['viewMode']); - const isFullScreenGrid = viewMode === CallingViewMode.FULL_SCREEN_GRID; - const isDetachedWindow = viewMode === CallingViewMode.DETACHED_WINDOW; - - const {isVideoCallingEnabled} = useKoSubscribableChildren(teamState, ['isVideoCallingEnabled']); - - const {activeCallViewTab} = useKoSubscribableChildren(callState, ['activeCallViewTab']); - const isMuted = muteState !== MuteState.NOT_MUTED; - - const isDeclined = !!reason && [CALL_REASON.STILL_ONGOING, CALL_REASON.ANSWERED_ELSEWHERE].includes(reason); - - const isOutgoing = state === CALL_STATE.OUTGOING; - const isIncoming = state === CALL_STATE.INCOMING; - const isConnecting = state === CALL_STATE.ANSWERED; - const isOngoing = state === CALL_STATE.MEDIA_ESTAB; - - const callStatus: Partial> = { - [CALL_STATE.OUTGOING]: { - dataUieName: 'call-label-outgoing', - text: t('callStateOutgoing'), - }, - [CALL_STATE.INCOMING]: { - dataUieName: 'call-label-incoming', - text: t('callStateIncoming'), - }, - [CALL_STATE.ANSWERED]: { - dataUieName: 'call-label-connecting', - text: t('callStateConnecting'), - }, - }; - - const currentCallStatus = callStatus[state]; - - const isVideoCall = call.initialType === CALL_TYPE.VIDEO; - - const showNoCameraPreview = !hasAccessToCamera && isVideoCall && !isOngoing; - const showVideoButton = isVideoCallingEnabled && (isVideoCall || isOngoing); - const showParticipantsButton = isOngoing && isGroup; - - const videoGrid = useVideoGrid(call); - - const conversationParticipants = conversation && (selfUser ? userEts.concat(selfUser) : userEts); - const conversationUrl = generateConversationUrl(conversation.qualifiedId); - const selfParticipant = call?.getSelfParticipant(); - - const { - sharesScreen: selfSharesScreen, - sharesCamera: selfSharesCamera, - hasActiveVideo: selfHasActiveVideo, - } = useKoSubscribableChildren(selfParticipant, ['sharesScreen', 'sharesCamera', 'hasActiveVideo']); - - const {activeSpeakers} = useKoSubscribableChildren(call, ['activeSpeakers']); - - const isOutgoingVideoCall = isOutgoing && selfSharesCamera; - const isVideoUnsupported = !selfSharesCamera && !conversation?.supportsVideoCall(call.isConference); - const disableVideoButton = isOutgoingVideoCall || isVideoUnsupported; - const disableScreenButton = !callingRepository.supportsScreenSharing; - - const [showParticipants, setShowParticipants] = useState(false); - const isModerator = selfUser && roles[selfUser.id] === DefaultConversationRoleName.WIRE_ADMIN; - - const toggleMute = useCallback( - (shouldMute: boolean) => callActions.toggleMute(call, shouldMute), - [call, callActions], - ); - - const isCurrentlyMuted = useCallback(() => { - const isMuted = call.muteState() === MuteState.SELF_MUTED; - return isMuted; - }, [call]); - - usePushToTalk({ - key: pushToTalkKey, - toggleMute, - isMuted: isCurrentlyMuted, - }); - - const getParticipantContext = (event: React.MouseEvent, participant: Participant) => { - event.preventDefault(); - - const muteParticipant: ContextMenuEntry = { - click: () => callingRepository.sendModeratorMute(conversation.qualifiedId, [participant]), - icon: Icon.MicOffIcon, - identifier: `moderator-mute-participant`, - isDisabled: participant.isMuted(), - label: t('moderatorMenuEntryMute'), - }; - - const muteOthers: ContextMenuEntry = { - click: () => { - callingRepository.sendModeratorMute( - conversation.qualifiedId, - participants.filter(p => p !== participant), - ); - }, - icon: Icon.MicOffIcon, - identifier: 'moderator-mute-others', - label: t('moderatorMenuEntryMuteAllOthers'), - }; - - const entries: ContextMenuEntry[] = [muteOthers].concat(!participant.user.isMe ? muteParticipant : []); - showContextMenu(event, entries, 'participant-moderator-menu'); - }; - - const handleMaximizeKeydown = useCallback( - (event: React.KeyboardEvent) => { - if (!isOngoing || isDetachedWindow) { - return; - } - if (isSpaceOrEnterKey(event.key)) { - callState.viewMode(CallingViewMode.FULL_SCREEN_GRID); - } - }, - [isOngoing, callState], - ); - - const handleMaximizeClick = useCallback(() => { - if (!isOngoing || isDetachedWindow) { - return; - } - callState.viewMode(CallingViewMode.FULL_SCREEN_GRID); - }, [isOngoing, callState]); - - const {setCurrentView} = useAppMainState(state => state.responsiveView); - const {showAlert, clearShowAlert} = useCallAlertState(); - - const answerCall = () => { - callActions.answer(call); - setCurrentView(ViewType.MOBILE_LEFT_SIDEBAR); - }; - - const answerOrRejectCall = useCallback( - (event: KeyboardEvent) => { - const answerCallShortcut = !event.shiftKey && event.ctrlKey && isEnterKey(event); - const hangUpCallShortcut = event.ctrlKey && event.shiftKey && isEnterKey(event); - - const removeEventListener = () => window.removeEventListener('keydown', answerOrRejectCall); - - if (answerCallShortcut || hangUpCallShortcut) { - event.preventDefault(); - event.stopPropagation(); - } - - if (answerCallShortcut) { - answerCall(); - removeEventListener(); - } - - if (hangUpCallShortcut) { - callActions.reject(call); - removeEventListener(); - } - }, - [call, callActions], - ); - - useEffect(() => { - if (isIncoming) { - // Capture will be dispatched to registered element before being dispatched to any EventTarget beneath it in the DOM Tree. - // It's needed because when someone is calling we need to change order of shortcuts to the top of keyboard usage. - // If we didn't pass this prop other Event Listeners will be dispatched in same time. - document.addEventListener('keydown', answerOrRejectCall, {capture: true}); - - return () => { - document.removeEventListener('keydown', answerOrRejectCall, {capture: true}); - }; - } - - return () => { - clearShowAlert(); - }; - }, [answerOrRejectCall, isIncoming]); - - const call1To1StartedAlert = t(isOutgoingVideoCall ? 'startedVideoCallingAlert' : 'startedAudioCallingAlert', { - conversationName, - cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), - }); - - const onGoingCallAlert = t(isOutgoingVideoCall ? 'ongoingVideoCall' : 'ongoingAudioCall', { - conversationName, - cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), - }); - - const callGroupStartedAlert = t(isOutgoingVideoCall ? 'startedVideoGroupCallingAlert' : 'startedGroupCallingAlert', { - conversationName, - cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), - }); - - const onGoingGroupCallAlert = t(isOutgoingVideoCall ? 'ongoingGroupVideoCall' : 'ongoingGroupAudioCall', { - conversationName, - cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), - }); - - const callStartedAlert = isGroup ? callGroupStartedAlert : call1To1StartedAlert; - const ongoingCallAlert = isGroup ? onGoingGroupCallAlert : onGoingCallAlert; - - const toggleDetachedWindow = () => { - if (isDetachedWindow) { - callState.viewMode(CallingViewMode.MINIMIZED); - } else { - callState.viewMode(CallingViewMode.DETACHED_WINDOW); - } - }; - - const isDetachedCallingFeatureEnabled = useDetachedCallingFeatureState(state => state.isSupported()); - - return ( -
    - {isIncoming && ( -

    - {t('callConversationAcceptOrDecline', conversationName)} -

    - )} - - {conversation && (!isDeclined || isTemporaryUser) && ( -
    - {muteState === MuteState.REMOTE_MUTED && isFullUi && ( -
    {t('muteStateRemoteMute')}
    - )} - -
    -
    { - if ((isGroup || isOngoing) && showAlert && !isVideoCall) { - element?.focus(); - } - }} - className="conversation-list-cell conversation-list-cell-button" - onClick={createNavigate(conversationUrl)} - onBlur={() => { - if (isGroup || isOngoing) { - clearShowAlert(); - } - }} - onKeyDown={createNavigateKeyboard(conversationUrl)} - tabIndex={TabIndex.FOCUSABLE} - role="button" - aria-label={ - showAlert - ? callStartedAlert - : `${isOngoing ? `${ongoingCallAlert} ` : ''}${t('accessibility.openConversation', conversationName)}` - } - > - {!isTemporaryUser && ( -
    - {isGroup && } - {!isGroup && !!conversationParticipants.length && ( - - )} -
    - )} - -

    - {conversationName} - - {currentCallStatus && ( - - {currentCallStatus.text} - - )} - - {isOngoing && startedAt && ( -
    - - - - - {isCbrEnabled && ( - - CBR - - )} -
    - )} -

    -
    - -
    - {isOngoing && isDetachedCallingFeatureEnabled && ( - - )} - - {(isConnecting || isOngoing) && ( - - )} -
    -
    - - {(isOngoing || selfHasActiveVideo) && !isFullScreenGrid && !!videoGrid?.grid?.length && isFullUi ? ( -
    - - - {isOngoing && !isDetachedWindow && ( -
    - -
    - )} -
    - ) : ( - showNoCameraPreview && - isFullUi && ( -
    - {t('callNoCameraAccess')} -
    - ) - )} - - {classifiedDomains && ( - - )} - -
    -
      - {isFullUi && ( - <> -
    • - -
    • - - {showVideoButton && ( -
    • - -
    • - )} - - {isOngoing && ( -
    • - -
    • - )} - - )} -
    - -
      - {showParticipantsButton && isFullUi && ( -
    • - -
    • - )} - - {(isIncoming || isOutgoing) && !isDeclined && ( -
    • - -
    • - )} - - {isIncoming && ( -
    • - {isDeclined ? ( - - ) : ( - - )} -
    • - )} -
    -
    - - {isFullUi && ( -
    - -
      - {participants - .slice() - .sort((participantA, participantB) => sortUsersByPriority(participantA.user, participantB.user)) - .map((participant, index, participantsArray) => ( -
    • - getParticipantContext(event, participant)} - isLast={participantsArray.length === index} - /> -
    • - ))} -
    -
    -
    - )} -
    - )} -
    - ); -}; - -export {CallingCell}; diff --git a/src/script/components/calling/CallingCell/CallIngParticipantList/CallingParticipantList.styles.ts b/src/script/components/calling/CallingCell/CallIngParticipantList/CallingParticipantList.styles.ts new file mode 100644 index 00000000000..7f81989fe88 --- /dev/null +++ b/src/script/components/calling/CallingCell/CallIngParticipantList/CallingParticipantList.styles.ts @@ -0,0 +1,25 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const labelStyles: CSSObject = { + padding: '12px 10px', + fontWeight: 'var(--font-weight-semibold)', +}; diff --git a/src/script/components/calling/CallingCell/CallIngParticipantList/CallingParticipantList.tsx b/src/script/components/calling/CallingCell/CallIngParticipantList/CallingParticipantList.tsx new file mode 100644 index 00000000000..9819b42e0c1 --- /dev/null +++ b/src/script/components/calling/CallingCell/CallIngParticipantList/CallingParticipantList.tsx @@ -0,0 +1,109 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import React from 'react'; + +import cx from 'classnames'; + +import {CallParticipantsListItem} from 'Components/calling/CallParticipantsListItem'; +import {FadingScrollbar} from 'Components/FadingScrollbar'; +import * as Icon from 'Components/Icon'; +import {t} from 'Util/LocalizerUtil'; +import {sortUsersByPriority} from 'Util/StringUtil'; + +import {labelStyles} from './CallingParticipantList.styles'; + +import {CallingRepository} from '../../../../calling/CallingRepository'; +import {Participant} from '../../../../calling/Participant'; +import {Conversation} from '../../../../entity/Conversation'; +import {ContextMenuEntry, showContextMenu} from '../../../../ui/ContextMenu'; + +interface CallingParticipantListProps { + callingRepository: Pick; + conversation: Conversation; + isModerator?: boolean; + isSelfVerified?: boolean; + participants: Participant[]; + showParticipants?: boolean; +} + +export const CallingParticipantList = ({ + callingRepository, + conversation, + isModerator, + isSelfVerified, + participants, + showParticipants, +}: CallingParticipantListProps) => { + const getParticipantContext = (event: React.MouseEvent, participant: Participant) => { + event.preventDefault(); + + const muteParticipant = { + click: () => callingRepository.sendModeratorMute(conversation.qualifiedId, [participant]), + icon: Icon.MicOffIcon, + identifier: `moderator-mute-participant`, + isDisabled: participant.isMuted(), + label: t('moderatorMenuEntryMute'), + }; + + const muteOthers: ContextMenuEntry = { + click: () => { + callingRepository.sendModeratorMute( + conversation.qualifiedId, + participants.filter(p => p !== participant), + ); + }, + icon: Icon.MicOffIcon, + identifier: 'moderator-mute-others', + label: t('moderatorMenuEntryMuteAllOthers'), + }; + + const entries: ContextMenuEntry[] = [muteOthers].concat(!participant.user.isMe ? muteParticipant : []); + showContextMenu({event, entries, identifier: 'participant-moderator-menu'}); + }; + + return ( +
    +

    {t('videoCallOverlayParticipantsListLabel', participants.length)}

    + +
      + {participants + .slice() + .sort((participantA, participantB) => sortUsersByPriority(participantA.user, participantB.user)) + .map((participant, index, participantsArray) => ( +
    • + getParticipantContext(event, participant)} + isLast={participantsArray.length === index} + /> +
    • + ))} +
    +
    +
    + ); +}; diff --git a/src/script/components/calling/CallingCell/CallIngParticipantList/index.ts b/src/script/components/calling/CallingCell/CallIngParticipantList/index.ts new file mode 100644 index 00000000000..9d3a12c8f61 --- /dev/null +++ b/src/script/components/calling/CallingCell/CallIngParticipantList/index.ts @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './CallingParticipantList'; diff --git a/src/script/components/calling/CallingCell/CallingCell.styles.ts b/src/script/components/calling/CallingCell/CallingCell.styles.ts new file mode 100644 index 00000000000..b15ceaa63fb --- /dev/null +++ b/src/script/components/calling/CallingCell/CallingCell.styles.ts @@ -0,0 +1,49 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const callingCellWrapper: CSSObject = { + backgroundColor: 'var(--app-bg-secondary)', + border: '1px solid 1px solid var(--border-color)', + borderRadius: '8px', + display: 'flex', + flexDirection: 'column', + padding: '12px 12px 16px', +}; + +export const callingContainer: CSSObject = { + position: 'relative', + display: 'flex', + flexDirection: 'column', + flexShrink: '0', + padding: '10px 12px 20px', + animation: 'show-call-ui var(--animation-timing-fast) ease-in-out 0s 1', +}; + +export const infoBar: CSSObject = { + backgroundColor: 'var(--accent-color)', + borderRadius: '8px', + color: 'var(--app-bg-secondary)', + fontSize: 'var(--line-height-xs)', + fontWeight: 'var(--font-weight-medium)', + margin: '8px 8px 0', + padding: '4px', + textAlign: 'center', +}; diff --git a/src/script/components/calling/CallingCell.test.tsx b/src/script/components/calling/CallingCell/CallingCell.test.tsx similarity index 97% rename from src/script/components/calling/CallingCell.test.tsx rename to src/script/components/calling/CallingCell/CallingCell.test.tsx index ba35584f200..9192e0f47e8 100644 --- a/src/script/components/calling/CallingCell.test.tsx +++ b/src/script/components/calling/CallingCell/CallingCell.test.tsx @@ -35,7 +35,7 @@ import {createUuid} from 'Util/uuid'; import {CallingCell, CallingCellProps} from './CallingCell'; -jest.mock('Components/utils/InViewport', () => ({ +jest.mock('Components/InViewport', () => ({ InViewport: ({onVisible, children}: {onVisible: () => void; children: any}) => { setTimeout(onVisible); return
    {children}
    ; @@ -68,11 +68,10 @@ const createProps = async () => { return { call: createCall(CALL_STATE.MEDIA_ESTAB), callActions: {} as CallActions, - callingRepository: mockCallingRepository, + callingRepository: mockCallingRepository as CallingRepository, pushToTalkKey: null, conversation, hasAccessToCamera: true, - isSelfVerified: true, teamState: mockTeamState, videoGrid: {grid: [], thumbnail: undefined}, } as CallingCellProps; diff --git a/src/script/components/calling/CallingCell/CallingCell.tsx b/src/script/components/calling/CallingCell/CallingCell.tsx new file mode 100644 index 00000000000..ea1fd4f4b71 --- /dev/null +++ b/src/script/components/calling/CallingCell/CallingCell.tsx @@ -0,0 +1,364 @@ +/* + * Wire + * Copyright (C) 2022 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import React, {useCallback, useEffect} from 'react'; + +import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; +import {container} from 'tsyringe'; + +import {CALL_TYPE, REASON as CALL_REASON, STATE as CALL_STATE} from '@wireapp/avs'; + +import {callingContainer} from 'Components/calling/CallingCell/CallingCell.styles'; +import {CallingControls} from 'Components/calling/CallingCell/CallingControls'; +import {CallingHeader} from 'Components/calling/CallingCell/CallingHeader'; +import {GroupVideoGrid} from 'Components/calling/GroupVideoGrid'; +import {useCallAlertState} from 'Components/calling/useCallAlertState'; +import {ConversationClassifiedBar} from 'Components/ClassifiedBar/ClassifiedBar'; +import * as Icon from 'Components/Icon'; +import {usePushToTalk} from 'src/script/hooks/usePushToTalk/usePushToTalk'; +import {useAppMainState, ViewType} from 'src/script/page/state'; +import {useKoSubscribableChildren} from 'Util/ComponentUtil'; +import {isEnterKey, isSpaceOrEnterKey} from 'Util/KeyboardUtil'; +import {t} from 'Util/LocalizerUtil'; + +import type {Call} from '../../../calling/Call'; +import type {CallingRepository} from '../../../calling/CallingRepository'; +import {CallingViewMode, CallState, MuteState} from '../../../calling/CallState'; +import type {Participant} from '../../../calling/Participant'; +import {useVideoGrid} from '../../../calling/videoGridHandler'; +import {generateConversationUrl} from '../../../router/routeGenerator'; +import {TeamState} from '../../../team/TeamState'; +import {CallActions, CallViewTab} from '../../../view_model/CallingViewModel'; + +interface VideoCallProps { + hasAccessToCamera?: boolean; + teamState?: TeamState; +} + +interface AnsweringControlsProps { + call: Call; + callActions: CallActions; + callingRepository: CallingRepository; + pushToTalkKey: string | null; + isFullUi?: boolean; + callState?: CallState; + classifiedDomains?: string[]; + isTemporaryUser?: boolean; + setMaximizedParticipant?: (participant: Participant | null) => void; +} + +export type CallingCellProps = VideoCallProps & AnsweringControlsProps; + +export type CallLabel = {dataUieName: string; text: string}; + +export const CallingCell = ({ + classifiedDomains, + isTemporaryUser, + call, + callActions, + isFullUi = false, + hasAccessToCamera, + callingRepository, + pushToTalkKey, + setMaximizedParticipant, + teamState = container.resolve(TeamState), + callState = container.resolve(CallState), +}: CallingCellProps) => { + const {conversation} = call; + const {reason, state, isCbrEnabled, startedAt, maximizedParticipant, muteState} = useKoSubscribableChildren(call, [ + 'reason', + 'state', + 'isCbrEnabled', + 'startedAt', + 'maximizedParticipant', + 'pages', + 'currentPage', + 'muteState', + ]); + + const { + isGroup, + participating_user_ets: userEts, + selfUser, + display_name: conversationName, + } = useKoSubscribableChildren(conversation, ['isGroup', 'participating_user_ets', 'selfUser', 'display_name']); + const {activeCallViewTab, viewMode} = useKoSubscribableChildren(callState, ['activeCallViewTab', 'viewMode']); + + const selfParticipant = call.getSelfParticipant(); + + const {sharesCamera: selfSharesCamera, hasActiveVideo: selfHasActiveVideo} = useKoSubscribableChildren( + selfParticipant, + ['sharesCamera', 'hasActiveVideo'], + ); + + const {activeSpeakers} = useKoSubscribableChildren(call, ['activeSpeakers']); + + const isVideoCall = call.initialType === CALL_TYPE.VIDEO; + const isDetachedWindow = viewMode === CallingViewMode.DETACHED_WINDOW; + + const isMuted = muteState !== MuteState.NOT_MUTED; + const isCurrentlyMuted = useCallback(() => muteState === MuteState.SELF_MUTED, [muteState]); + + const isDeclined = !!reason && [CALL_REASON.STILL_ONGOING, CALL_REASON.ANSWERED_ELSEWHERE].includes(reason); + + const isOutgoing = state === CALL_STATE.OUTGOING; + const isIncoming = state === CALL_STATE.INCOMING; + const isConnecting = state === CALL_STATE.ANSWERED; + const isOngoing = state === CALL_STATE.MEDIA_ESTAB; + + const callStatus: Partial> = { + [CALL_STATE.OUTGOING]: { + dataUieName: 'call-label-outgoing', + text: t('callStateOutgoing'), + }, + [CALL_STATE.INCOMING]: { + dataUieName: 'call-label-incoming', + text: t('callStateIncoming'), + }, + [CALL_STATE.ANSWERED]: { + dataUieName: 'call-label-connecting', + text: t('callStateConnecting'), + }, + }; + + const currentCallStatus = callStatus[state]; + + const showNoCameraPreview = !hasAccessToCamera && isVideoCall && !isOngoing; + + const videoGrid = useVideoGrid(call); + + const conversationParticipants = selfUser ? userEts.concat(selfUser) : userEts; + const conversationUrl = generateConversationUrl(conversation.qualifiedId); + + const isOutgoingVideoCall = isOutgoing && selfSharesCamera; + + const toggleMute = useCallback( + (shouldMute: boolean) => callActions.toggleMute(call, shouldMute), + [call, callActions], + ); + + usePushToTalk({ + key: pushToTalkKey, + toggleMute, + isMuted: isCurrentlyMuted, + }); + + const handleMaximizeKeydown = useCallback( + (event: React.KeyboardEvent) => { + if (!isOngoing) { + return; + } + if (isSpaceOrEnterKey(event.key)) { + void callingRepository.setViewModeFullScreen(); + } + }, + [isOngoing, callingRepository], + ); + + const handleMaximizeClick = useCallback(() => { + if (!isOngoing) { + return; + } + void callingRepository.setViewModeFullScreen(); + }, [isOngoing, callingRepository]); + + const {setCurrentView} = useAppMainState(state => state.responsiveView); + const {showAlert, clearShowAlert} = useCallAlertState(); + + const answerCall = () => { + callActions.answer(call); + setCurrentView(ViewType.MOBILE_LEFT_SIDEBAR); + }; + + const answerOrRejectCall = useCallback( + (event: KeyboardEvent) => { + const answerCallShortcut = !event.shiftKey && event.ctrlKey && isEnterKey(event); + const hangUpCallShortcut = event.ctrlKey && event.shiftKey && isEnterKey(event); + + const removeEventListener = () => window.removeEventListener('keydown', answerOrRejectCall); + + if (answerCallShortcut || hangUpCallShortcut) { + event.preventDefault(); + event.stopPropagation(); + } + + if (answerCallShortcut) { + answerCall(); + removeEventListener(); + } + + if (hangUpCallShortcut) { + callActions.reject(call); + removeEventListener(); + } + }, + [call, callActions], + ); + + useEffect(() => { + if (isIncoming) { + // Capture will be dispatched to registered element before being dispatched to any EventTarget beneath it in the DOM Tree. + // It's needed because when someone is calling we need to change order of shortcuts to the top of keyboard usage. + // If we didn't pass this prop other Event Listeners will be dispatched in same time. + document.addEventListener('keydown', answerOrRejectCall, {capture: true}); + + return () => { + document.removeEventListener('keydown', answerOrRejectCall, {capture: true}); + }; + } + + return () => { + clearShowAlert(); + }; + }, [answerOrRejectCall, isIncoming]); + + const call1To1StartedAlert = t(isOutgoingVideoCall ? 'startedVideoCallingAlert' : 'startedAudioCallingAlert', { + conversationName, + cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), + }); + + const onGoingCallAlert = t(isOutgoingVideoCall ? 'ongoingVideoCall' : 'ongoingAudioCall', { + conversationName, + cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), + }); + + const callGroupStartedAlert = t(isOutgoingVideoCall ? 'startedVideoGroupCallingAlert' : 'startedGroupCallingAlert', { + conversationName, + cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), + }); + + const onGoingGroupCallAlert = t(isOutgoingVideoCall ? 'ongoingGroupVideoCall' : 'ongoingGroupAudioCall', { + conversationName, + cameraStatus: t(selfSharesCamera ? 'cameraStatusOn' : 'cameraStatusOff'), + }); + + const toggleDetachedWindow = () => { + if (isDetachedWindow) { + void callingRepository.setViewModeMinimized(); + return; + } + void callingRepository.setViewModeDetached(); + }; + + return ( +
    + {isIncoming && ( +

    + {t('callConversationAcceptOrDecline', conversationName)} +

    + )} + + {(!isDeclined || isTemporaryUser) && ( +
    + {muteState === MuteState.REMOTE_MUTED && isFullUi && ( +
    {t('muteStateRemoteMute')}
    + )} + + + + {(isOngoing || selfHasActiveVideo) && !isDetachedWindow && !!videoGrid?.grid?.length && isFullUi ? ( + <> + {!isDetachedWindow && ( +
    + + + {isOngoing && ( +
    + +
    + )} +
    + )} + + ) : ( + showNoCameraPreview && + isFullUi && ( +
    + {t('callNoCameraAccess')} +
    + ) + )} + + {classifiedDomains && ( + + )} + + +
    + )} +
    + ); +}; diff --git a/src/script/components/calling/CallingCell/CallingControls/CallingControls.styles.ts b/src/script/components/calling/CallingCell/CallingControls/CallingControls.styles.ts new file mode 100644 index 00000000000..f4d85962667 --- /dev/null +++ b/src/script/components/calling/CallingCell/CallingControls/CallingControls.styles.ts @@ -0,0 +1,35 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const cellControlsWrapper: CSSObject = { + alignItems: 'center', + display: 'flex', + justifyContent: 'space-between', + width: '100%', +}; + +export const cellControlsList: CSSObject = { + display: 'flex', + gap: '8px', + listStyleType: 'none', + margin: 0, + padding: 0, +}; diff --git a/src/script/components/calling/CallingCell/CallingControls/CallingControls.tsx b/src/script/components/calling/CallingCell/CallingControls/CallingControls.tsx new file mode 100644 index 00000000000..f4e97079dbc --- /dev/null +++ b/src/script/components/calling/CallingCell/CallingControls/CallingControls.tsx @@ -0,0 +1,227 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import cx from 'classnames'; +import {container} from 'tsyringe'; + +import { + cellControlsList, + cellControlsWrapper, +} from 'Components/calling/CallingCell/CallingControls/CallingControls.styles'; +import {useCallAlertState} from 'Components/calling/useCallAlertState'; +import * as Icon from 'Components/Icon'; +import {DesktopScreenShareMenu} from 'src/script/calling/CallState'; +import {useKoSubscribableChildren} from 'Util/ComponentUtil'; +import {t} from 'Util/LocalizerUtil'; + +import {Call} from '../../../../calling/Call'; +import {Participant} from '../../../../calling/Participant'; +import {TeamState} from '../../../../team/TeamState'; +import {CallActions} from '../../../../view_model/CallingViewModel'; + +interface CallingControlsProps { + answerCall: () => void; + call: Call; + callActions: CallActions; + call1To1StartedAlert: string; + isDetachedWindow: boolean; + isFullUi?: boolean; + isMuted?: boolean; + isIncoming: boolean; + isOutgoing: boolean; + isOngoing: boolean; + isDeclined: boolean; + isGroup: boolean; + isVideoCall: boolean; + isConnecting?: boolean; + selfParticipant: Participant; + disableScreenButton: boolean; + teamState: TeamState; + supportsVideoCall: boolean; +} + +export const CallingControls = ({ + answerCall, + call, + callActions, + call1To1StartedAlert, + isFullUi, + isMuted, + isConnecting, + isDetachedWindow, + isIncoming, + isOutgoing, + isDeclined, + disableScreenButton, + isVideoCall, + isOngoing, + isGroup, + selfParticipant, + teamState = container.resolve(TeamState), + supportsVideoCall, +}: CallingControlsProps) => { + const {isVideoCallingEnabled} = useKoSubscribableChildren(teamState, ['isVideoCallingEnabled']); + const {sharesScreen: selfSharesScreen, sharesCamera: selfSharesCamera} = useKoSubscribableChildren(selfParticipant, [ + 'sharesScreen', + 'sharesCamera', + ]); + + const {showAlert, clearShowAlert} = useCallAlertState(); + + const isVideoUnsupported = !selfSharesCamera && !supportsVideoCall; + const showVideoButton = isVideoCallingEnabled && (isVideoCall || isOngoing); + const disableVideoButton = (isOutgoing && selfSharesCamera) || isVideoUnsupported; + + return ( +
    +
      + {isFullUi && ( + <> +
    • + +
    • + + {showVideoButton && ( +
    • + +
    • + )} + + {isOngoing && ( +
    • + +
    • + )} + + )} +
    + +
      + {(isIncoming || isOutgoing) && !isDeclined && ( +
    • + +
    • + )} + + {isIncoming && ( +
    • + {isDeclined ? ( + + ) : ( + + )} +
    • + )} + + {(isConnecting || isOngoing) && ( +
    • + +
    • + )} +
    +
    + ); +}; diff --git a/src/script/components/calling/CallingCell/CallingControls/index.ts b/src/script/components/calling/CallingCell/CallingControls/index.ts new file mode 100644 index 00000000000..6fcd6a5624a --- /dev/null +++ b/src/script/components/calling/CallingCell/CallingControls/index.ts @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './CallingControls'; diff --git a/src/script/components/calling/CallingCell/CallingHeader/CallingHeader.styles.ts b/src/script/components/calling/CallingCell/CallingHeader/CallingHeader.styles.ts new file mode 100644 index 00000000000..6ceaa2059bc --- /dev/null +++ b/src/script/components/calling/CallingCell/CallingHeader/CallingHeader.styles.ts @@ -0,0 +1,88 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const callingHeaderContainer: CSSObject = { + alignItems: 'center', + borderRadius: '8px 8px 0 0', + cursor: 'pointer', + display: 'flex', + fontWeight: 'var(--font-weight-regular)', + marginBottom: '8px', + position: 'relative', + width: '100%', +}; + +export const callingHeaderWrapper: CSSObject = { + alignItems: 'center', + display: 'flex', + gap: '12px', + width: '100%', + overflow: 'hidden', +}; + +export const callAvatar: CSSObject = { + alignItems: 'center', + display: 'flex', +}; + +export const conversationCallName: CSSObject = { + fontSize: 'var(--font-size-medium)', + fontWeight: 'var(--font-weight-medium)', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', +}; + +export const callDescription: CSSObject = { + color: 'var(--background)', + fontSize: 'var(--font-size-small)', + fontWeight: 'var(--font-weight-regular)', +}; + +export const callDetails: CSSObject = { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + paddingRight: '12px', + width: '100%', +}; + +export const cbrCallState: CSSObject = { + fontWeight: 'var(--font-weight-semibold)', + marginLeft: '6px', +}; + +export const detachedWindowButton: CSSObject = { + alignItems: 'center', + background: 'transparent', + border: 'none', + display: 'flex', + justifyContent: 'center', + padding: '8px 12px', + + '& svg': { + fill: 'var(--text-color)', + }, + + '& svg path': { + fill: 'var(--text-color)', + }, +}; diff --git a/src/script/components/calling/CallingCell/CallingHeader/CallingHeader.tsx b/src/script/components/calling/CallingCell/CallingHeader/CallingHeader.tsx new file mode 100644 index 00000000000..92c3bbd6743 --- /dev/null +++ b/src/script/components/calling/CallingCell/CallingHeader/CallingHeader.tsx @@ -0,0 +1,165 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums'; + +import {IconButton, IconButtonVariant} from '@wireapp/react-ui-kit'; + +import {Avatar, AVATAR_SIZE, GroupAvatar} from 'Components/Avatar'; +import {Duration} from 'Components/calling/Duration'; +import * as Icon from 'Components/Icon'; +import {isDetachedCallingFeatureEnabled} from 'Util/isDetachedCallingFeatureEnabled'; +import {t} from 'Util/LocalizerUtil'; + +import { + callAvatar, + callDescription, + callDetails, + callingHeaderContainer, + callingHeaderWrapper, + cbrCallState, + conversationCallName, + detachedWindowButton, +} from './CallingHeader.styles'; + +import {User} from '../../../../entity/User'; +import {createNavigate, createNavigateKeyboard} from '../../../../router/routerBindings'; + +interface CallingHeaderProps { + isOngoing: boolean; + isGroup: boolean; + + showAlert: boolean; + isVideoCall: boolean; + clearShowAlert: () => void; + conversationUrl: string; + callStartedAlert: string; + ongoingCallAlert: string; + isTemporaryUser: boolean; + conversationParticipants: User[]; + conversationName: string; + currentCallStatus: any; + startedAt?: number; + isCbrEnabled: boolean; + toggleDetachedWindow: () => void; + isDetachedWindow: boolean; +} + +export const CallingHeader = ({ + isGroup, + isOngoing, + showAlert, + isVideoCall, + clearShowAlert, + conversationUrl, + callStartedAlert, + ongoingCallAlert, + isTemporaryUser, + conversationParticipants, + conversationName, + currentCallStatus, + startedAt, + isCbrEnabled, + toggleDetachedWindow, + isDetachedWindow, +}: CallingHeaderProps) => { + return ( +
    +
    { + if ((isGroup || isOngoing) && showAlert && !isVideoCall) { + element?.focus(); + } + }} + css={callingHeaderWrapper} + onClick={createNavigate(conversationUrl)} + onBlur={() => { + if (isGroup || isOngoing) { + clearShowAlert(); + } + }} + onKeyDown={createNavigateKeyboard(conversationUrl)} + tabIndex={TabIndex.FOCUSABLE} + role="button" + aria-label={ + showAlert + ? callStartedAlert + : `${isOngoing ? `${ongoingCallAlert} ` : ''}${t('accessibility.openConversation', conversationName)}` + } + > + {isDetachedWindow && !isTemporaryUser && ( +
    + {isGroup && } + {!isGroup && !!conversationParticipants.length && ( + + )} +
    + )} + +

    +
    {conversationName}
    + + {currentCallStatus && ( +
    + {currentCallStatus.text} +
    + )} + + {isOngoing && startedAt && ( +
    + {isDetachedWindow ? ( + + {t('viewingInAnotherWindow')} + + ) : ( + + + + )} + + {isCbrEnabled && ( + + CBR + + )} +
    + )} +

    +
    + + {isDetachedCallingFeatureEnabled() && isOngoing && ( +
    + + {isDetachedWindow ? : } + +
    + )} +
    + ); +}; diff --git a/src/script/components/calling/CallingCell/CallingHeader/index.ts b/src/script/components/calling/CallingCell/CallingHeader/index.ts new file mode 100644 index 00000000000..7d3a24f71a9 --- /dev/null +++ b/src/script/components/calling/CallingCell/CallingHeader/index.ts @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './CallingHeader'; diff --git a/src/script/components/calling/CallingCell/index.ts b/src/script/components/calling/CallingCell/index.ts new file mode 100644 index 00000000000..bdc2f2b8760 --- /dev/null +++ b/src/script/components/calling/CallingCell/index.ts @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +export * from './CallingCell'; diff --git a/src/script/components/calling/CallingOverlayContainer.tsx b/src/script/components/calling/CallingOverlayContainer.tsx index 13f38a77e51..ad3c7add5ab 100644 --- a/src/script/components/calling/CallingOverlayContainer.tsx +++ b/src/script/components/calling/CallingOverlayContainer.tsx @@ -21,17 +21,15 @@ import React, {Fragment, useEffect} from 'react'; import {container} from 'tsyringe'; -import {CALL_TYPE, STATE as CALL_STATE} from '@wireapp/avs'; - import {useCallAlertState} from 'Components/calling/useCallAlertState'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; -import {ChooseScreen, Screen} from './ChooseScreen'; +import {ChooseScreen} from './ChooseScreen'; import {FullscreenVideoCall} from './FullscreenVideoCall'; import {Call} from '../../calling/Call'; import {CallingRepository} from '../../calling/CallingRepository'; -import {CallingViewMode, CallState, MuteState} from '../../calling/CallState'; +import {CallingViewMode, CallState, DesktopScreenShareMenu, MuteState} from '../../calling/CallState'; import {LEAVE_CALL_REASON} from '../../calling/enum/LeaveCallReason'; import {Participant} from '../../calling/Participant'; import {useVideoGrid} from '../../calling/videoGridHandler'; @@ -42,7 +40,7 @@ export interface CallingContainerProps { readonly callingRepository: CallingRepository; readonly mediaRepository: MediaRepository; readonly callState?: CallState; - readonly toggleScreenshare: (call: Call) => void; + readonly toggleScreenshare: (call: Call, desktopScreenShareMenu: DesktopScreenShareMenu) => void; } const CallingContainer: React.FC = ({ @@ -52,19 +50,17 @@ const CallingContainer: React.FC = ({ toggleScreenshare, }) => { const {devicesHandler: mediaDevicesHandler} = mediaRepository; - const {viewMode} = useKoSubscribableChildren(callState, ['viewMode']); - const isFullScreenGrid = viewMode === CallingViewMode.FULL_SCREEN_GRID; - const isDetachedWindow = viewMode === CallingViewMode.DETACHED_WINDOW; - - const {activeCallViewTab, joinedCall, selectableScreens, selectableWindows, isChoosingScreen} = + const {activeCallViewTab, joinedCall, hasAvailableScreensToShare, desktopScreenShareMenu, viewMode} = useKoSubscribableChildren(callState, [ 'activeCallViewTab', 'joinedCall', - 'selectableScreens', - 'selectableWindows', - 'isChoosingScreen', + 'hasAvailableScreensToShare', + 'desktopScreenShareMenu', + 'viewMode', ]); + const isFullScreenOrDetached = [CallingViewMode.DETACHED_WINDOW, CallingViewMode.FULL_SCREEN].includes(viewMode); + const { maximizedParticipant, state: currentCallState, @@ -74,26 +70,19 @@ const CallingContainer: React.FC = ({ const isMuted = muteState !== MuteState.NOT_MUTED; useEffect(() => { - if (currentCallState === CALL_STATE.MEDIA_ESTAB && joinedCall?.initialType === CALL_TYPE.VIDEO) { - callState.viewMode(CallingViewMode.FULL_SCREEN_GRID); - } if (currentCallState === undefined) { - callState.viewMode(CallingViewMode.MINIMIZED); + void callingRepository.setViewModeMinimized(); } }, [currentCallState]); const videoGrid = useVideoGrid(joinedCall!); - const onCancelScreenSelection = () => { - callState.selectableScreens([]); - callState.selectableWindows([]); - }; - const changePage = (newPage: number, call: Call) => callingRepository.changeCallPage(call, newPage); const {clearShowAlert} = useCallAlertState(); const leave = (call: Call) => { + callingRepository.setViewModeMinimized(); callingRepository.leaveCall(call.conversation.qualifiedId, LEAVE_CALL_REASON.MANUAL_LEAVE_BY_UI_CLICK); callState.activeCallViewTab(CallViewTab.ALL); call.maximizedParticipant(null); @@ -135,26 +124,34 @@ const CallingContainer: React.FC = ({ const conversation = joinedCall?.conversation; - if (isDetachedWindow || !joinedCall || !conversation || conversation.isSelfUserRemoved()) { + if (!joinedCall || !conversation || conversation.isSelfUserRemoved()) { return null; } + const toggleScreenShare = (call: Call) => { + toggleScreenshare(call, DesktopScreenShareMenu.DETACHED_WINDOW); + }; + + const isScreenshareActive = + hasAvailableScreensToShare && desktopScreenShareMenu === DesktopScreenShareMenu.DETACHED_WINDOW; + return ( - {isFullScreenGrid && !!videoGrid?.grid.length && ( + {isFullScreenOrDetached && !!videoGrid?.grid.length && ( = ({ setActiveCallViewTab={setActiveCallViewTab} toggleMute={toggleMute} toggleCamera={toggleCamera} - toggleScreenshare={toggleScreenshare} + toggleScreenshare={toggleScreenShare} leave={leave} changePage={changePage} /> )} - {isChoosingScreen && ( - - )} + {isScreenshareActive && } ); }; diff --git a/src/script/components/calling/ChooseScreen.tsx b/src/script/components/calling/ChooseScreen.tsx index 8f8ef659e66..8f5a136b494 100644 --- a/src/script/components/calling/ChooseScreen.tsx +++ b/src/script/components/calling/ChooseScreen.tsx @@ -17,23 +17,31 @@ * */ -import React, {Fragment, useEffect} from 'react'; +import {Fragment, useCallback, useEffect} from 'react'; -import {t} from 'Util/LocalizerUtil'; +import {container} from 'tsyringe'; -export interface Screen { - id: string; - thumbnail: HTMLCanvasElement; -} +import {CallState} from 'src/script/calling/CallState'; +import {ElectronDesktopCapturerSource} from 'src/script/media/MediaDevicesHandler'; +import {useKoSubscribableChildren} from 'Util/ComponentUtil'; +import {t} from 'Util/LocalizerUtil'; export interface ChooseScreenProps { - cancel: () => void; choose: (screenId: string) => void; - screens: Screen[]; - windows: Screen[]; + callState?: CallState; } -const ChooseScreen: React.FC = ({cancel, choose, screens = [], windows = []}: ChooseScreenProps) => { +function ChooseScreen({choose, callState = container.resolve(CallState)}: ChooseScreenProps) { + const {selectableScreens, selectableWindows} = useKoSubscribableChildren(callState, [ + 'selectableScreens', + 'selectableWindows', + ]); + + const cancel = useCallback(() => { + callState.selectableScreens([]); + callState.selectableWindows([]); + }, [callState]); + useEffect(() => { const closeOnEsc = ({key}: KeyboardEvent): void => { if (key === 'Escape') { @@ -47,7 +55,7 @@ const ChooseScreen: React.FC = ({cancel, choose, screens = [] }; }, [cancel]); - const renderPreviews = (list: Screen[], uieName: string) => + const renderPreviews = (list: ElectronDesktopCapturerSource[], uieName: string) => list.map(({id, thumbnail}) => ( - )} - {currentPage !== 0 && ( - + {t('muteStateRemoteMute')} + )} - {!verticalBreakpoint && ( -
    +
    +
    + {!maximizedParticipant && activeCallViewTab === CallViewTab.ALL && totalPages > 1 && ( +
    + + changePage(newPage, call)} /> -
    - )} - - )} -
    - - {!isChoosingScreen && ( -
    - {showEmojisBar && ( -
    - {EMOJIS_LIST.map(emoji => { - const isDisabled = disabledEmojis.includes(emoji); - return ( - - ); - })} -
    - )} - - {emojis.map(({id, emoji, left, from}) => ( -
    - - -
    - ))} - -
      - {!horizontalSmBreakpoint && ( -
    • -
    • +
    )} -
    -
  • - + {isDetachedCallingFeatureEnabled() && viewMode !== CallingViewMode.DETACHED_WINDOW && ( + + )} +
  • + - {showSwitchMicrophone && ( +
    + setMaximizedParticipant(call, participant)} + /> + {classifiedDomains && ( + + )} +
    + + {!isChoosingScreen && ( +
    + {emojis.map(({id, emoji, left, from}) => ( +
    + + +
    + ))} + +
      + {!horizontalSmBreakpoint && ( +
    • + )} +
    • + + {showToggleVideo && ( +
    • + + +
    • - )} - -
    • - -
    • - {Config.getConfig().FEATURE.ENABLE_IN_CALL_REACTIONS && (
    • +
    + + {!horizontalXsBreakpoint && ( +
    + {participants.length > 2 && ( +
  • +