diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 07a128960..3a6655aef 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -24,20 +24,11 @@ jobs: with: flutter-version: '3.7.3' channel: 'stable' - - - name: Setup node (ㅠ﹏ㅠ) - uses: actions/setup-node@v3 - with: - node-version: 18 - name: Setup Tools ᕙ|” ◉ ◡ ◉ ”|ᕗ run: | sudo apt-get update -y - sudo apt-get install -y ninja-build libgtk-3-dev libolm3 libmpv-dev mpv - - cd $PROJECT_PATH/integration_test/subprocess - npm ci - npm install -g ts-node + sudo apt-get install -y ninja-build libgtk-3-dev libolm3 libmpv-dev mpv ffmpeg - name: Start Synapse [◯ ‸ ◯] run: | @@ -67,4 +58,16 @@ jobs: cd $PROJECT_PATH export DISPLAY=:99 sudo Xvfb -ac :99 -screen 0 1920x1080x24 > /dev/null 2>&1 & + ffmpeg -f x11grab -video_size 1920x1080 -framerate 15 -i :99 -vcodec libx264 -preset ultrafast -qp 0 -nostdin -loglevel quiet -pix_fmt yuv444p video.mkv & scripts/integration-test.sh + INTEGRATION_TEST_EXIT_CODE=$? + sleep 1 + kill $(pgrep ffmpeg) + exit $INTEGRATION_TEST_EXIT_CODE + + - name: Upload Artifact + if: always() + uses: actions/upload-artifact@v3 + with: + name: result.mkv + path: commet/video.mkv \ No newline at end of file diff --git a/commet/.vscode/launch.json b/commet/.vscode/launch.json index 12358186e..cc76b20af 100644 --- a/commet/.vscode/launch.json +++ b/commet/.vscode/launch.json @@ -9,7 +9,8 @@ "request": "launch", "type": "dart", "toolArgs": [ - "--dart-define", "PLATFORM=desktop" + "--dart-define", "PLATFORM=desktop", + "--dart-define", "BUILD_MODE=debug" ], }, { @@ -17,7 +18,8 @@ "request": "launch", "type": "dart", "toolArgs": [ - "--dart-define", "PLATFORM=mobile" + "--dart-define", "PLATFORM=mobile", + "--dart-define", "BUILD_MODE=debug" ], }, { @@ -25,7 +27,8 @@ "request": "launch", "type": "dart", "toolArgs": [ - "--dart-define", "PLATFORM=android" + "--dart-define", "PLATFORM=android", + "--dart-define", "BUILD_MODE=debug" ], }, { diff --git a/commet/integration_test/extensions/common_flows.dart b/commet/integration_test/extensions/common_flows.dart index c5879155f..a77aa177d 100644 --- a/commet/integration_test/extensions/common_flows.dart +++ b/commet/integration_test/extensions/common_flows.dart @@ -7,6 +7,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:commet/main.dart'; import 'package:hive/hive.dart'; +import 'package:matrix/encryption/utils/key_verification.dart'; +import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'wait_for.dart'; @@ -64,4 +66,32 @@ extension CommonFlows on WidgetTester { await waitFor(() => app.clientManager.isLoggedIn(), timeout: const Duration(seconds: 5), skipPumpAndSettle: true); expect(app.clientManager.isLoggedIn(), equals(true)); } + + Future createTestClient() async { + var hs = const String.fromEnvironment('HOMESERVER', defaultValue: "localhost"); + + var username = const String.fromEnvironment('USER1_NAME', defaultValue: "alice"); + + var password = const String.fromEnvironment('USER1_PW', defaultValue: "AliceInWonderland"); + + var otherClient = Client( + "Commet Integration Tester", + verificationMethods: {KeyVerificationMethod.emoji, KeyVerificationMethod.numbers}, + nativeImplementations: MatrixClient.nativeImplementations, + logLevel: Level.verbose, + databaseBuilder: (client) async { + print(await AppConfig.getDatabasePath()); + final db = HiveCollectionsDatabase(client.clientName, await AppConfig.getDatabasePath()); + await db.open(); + return db; + }, + ); + + await otherClient.checkHomeserver(Uri.http(hs)); + + await otherClient.login(LoginType.mLoginPassword, + identifier: AuthenticationUserIdentifier(user: username), password: password); + + return otherClient; + } } diff --git a/commet/integration_test/matrix/key_verification_test.dart b/commet/integration_test/matrix/key_verification_test.dart index fd34b7a04..ed32ddf11 100644 --- a/commet/integration_test/matrix/key_verification_test.dart +++ b/commet/integration_test/matrix/key_verification_test.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:commet/client/matrix/matrix_client.dart'; +import 'package:commet/config/app_config.dart'; import 'package:commet/generated/l10n.dart'; import 'package:commet/ui/pages/matrix/verification/matrix_verification_page.dart'; import 'package:flutter/material.dart'; @@ -21,8 +22,6 @@ void main() { var username = const String.fromEnvironment('USER1_NAME', defaultValue: "alice"); var password = const String.fromEnvironment('USER1_PW', defaultValue: "AliceInWonderland"); - // Adding a bunch of delays to not trigger M_LIMIT_EXCEEDED: Too Many Requests - // Also helps avoid some errors with lock files when cleaning user data; await tester.clearUserData(); var app = App(); @@ -31,9 +30,16 @@ void main() { var matrixClient = (app.clientManager.getClients()[0] as MatrixClient); - var proc = await IntegrationTestSubprocess.verifyMeWithEmoji(matrixClient.getMatrixClient().deviceID!); + var otherClient = await tester.createTestClient(); - await Future.delayed(const Duration(seconds: 1)); + var client = matrixClient.getMatrixClient(); + + var devices = await otherClient.getDevices(); + var device = devices!.firstWhere((element) => element.deviceId == client.deviceID); + expect(device, isNotNull); + + var verification = otherClient.userDeviceKeys[otherClient.userID]!.deviceKeys[device.deviceId]!.startVerification(); + verification.onUpdate = () {}; await tester.waitFor(() => find.byType(MatrixVerificationPage).evaluate().isNotEmpty); @@ -51,14 +57,19 @@ void main() { await tester.tap(button); + await verification.acceptSas(); + await tester .waitFor(() => find.widgetWithText(ElevatedButton, T.current.sasVerificationDone).evaluate().isNotEmpty); await tester.pumpAndSettle(); - var client = matrixClient.getMatrixClient(); + expect(verification.isDone, equals(true)); + expect(verification.state, equals(KeyVerificationState.done)); - proc.kill(); await tester.clean(); + + expect(client.userDeviceKeys[client.userID]!.deviceKeys[otherClient.deviceID!], isNotNull); + expect(otherClient.userDeviceKeys[otherClient.userID]!.deviceKeys[client.deviceID!], isNotNull); }); } diff --git a/commet/integration_test/subprocess/.gitignore b/commet/integration_test/subprocess/.gitignore deleted file mode 100644 index c6bba5913..000000000 --- a/commet/integration_test/subprocess/.gitignore +++ /dev/null @@ -1,130 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* diff --git a/commet/integration_test/subprocess/.npmrc b/commet/integration_test/subprocess/.npmrc deleted file mode 100644 index 45c69bbe0..000000000 --- a/commet/integration_test/subprocess/.npmrc +++ /dev/null @@ -1 +0,0 @@ -@matrix-org:registry=https://gitlab.matrix.org/api/v4/packages/npm/ diff --git a/commet/integration_test/subprocess/README.md b/commet/integration_test/subprocess/README.md deleted file mode 100644 index c3ee818fb..000000000 --- a/commet/integration_test/subprocess/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Integration Testing Subprocess - -This provides a second process to act as a matrix client to interact with during unit testing. Using `matrix-js-sdk` and node. - -## Running -To set this up for running, make sure `node` is installed and available on your system PATH - -We use Node version 18. - -Also install `ts-node` such that it is also available on your system path. - -``` -npm install -g ts-node -``` - -Then install dependencies - -``` -npm ci -``` diff --git a/commet/integration_test/subprocess/package-lock.json b/commet/integration_test/subprocess/package-lock.json deleted file mode 100644 index 06ad728ef..000000000 --- a/commet/integration_test/subprocess/package-lock.json +++ /dev/null @@ -1,1280 +0,0 @@ -{ - "name": "external_device", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "@matrix-org/olm": "^3.2.14", - "commander": "^10.0.0", - "matrix-js-sdk": "^23.5.0", - "typescript": "^4.9.5" - }, - "devDependencies": { - "@types/node": "^18.15.3", - "nodemon": "^2.0.21", - "ts-node": "^10.9.1" - } - }, - "node_modules/@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@matrix-org/matrix-sdk-crypto-js": { - "version": "0.1.0-alpha.4", - "resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.4.tgz", - "integrity": "sha512-mdaDKrw3P5ZVCpq0ioW0pV6ihviDEbS8ZH36kpt9stLKHwwDSopPogE6CkQhi0B1jn1yBUtOYi32mBV/zcOR7g==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/@matrix-org/olm": { - "version": "3.2.14", - "resolved": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz", - "integrity": "sha1-rNlsAKiB0PRi4fl6Vsc3QsjbyYQ=" - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "node_modules/@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" - }, - "node_modules/@types/node": { - "version": "18.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==", - "dev": true - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/another-json": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/another-json/-/another-json-0.2.0.tgz", - "integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base-x": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", - "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bs58": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", - "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", - "dependencies": { - "base-x": "^4.0.0" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/commander": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", - "integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==", - "engines": { - "node": ">=14" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/loglevel": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", - "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", - "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/matrix-events-sdk": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz", - "integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==" - }, - "node_modules/matrix-js-sdk": { - "version": "23.5.0", - "resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-23.5.0.tgz", - "integrity": "sha512-jkHJBxXcLqzz0aZ4+Hjbx7hvgryIy+DZPOxvNfM2jJM0sc803Yyu4JMZLEdx/JLwFG1KE7bFZGiXP26g5yu6Mw==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.3", - "another-json": "^0.2.0", - "bs58": "^5.0.0", - "content-type": "^1.0.4", - "loglevel": "^1.7.1", - "matrix-events-sdk": "0.0.1", - "matrix-widget-api": "^1.0.0", - "p-retry": "4", - "sdp-transform": "^2.14.1", - "unhomoglyph": "^1.0.6", - "uuid": "9" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/matrix-widget-api": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.2.0.tgz", - "integrity": "sha512-BkBTREtXjCUM3Kx4UBgDmKoz39w7AXfIjBIC/jISBdcJkg8upFUhpIy+zUrSCIrmfO2Ke8LOsSoFoQkOyhqGxQ==", - "dependencies": { - "@types/events": "^3.0.0", - "events": "^3.2.0" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nodemon": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.21.tgz", - "integrity": "sha512-djN/n2549DUtY33S7o1djRCd7dEm0kBnj9c7S9XVXqRUbuggN1MZH/Nqa+5RFQr63Fbefq37nFXAE9VU86yL1A==", - "dev": true, - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/sdp-transform": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.14.1.tgz", - "integrity": "sha512-RjZyX3nVwJyCuTo5tGPx+PZWkDMCg7oOLpSlhjDdZfwUoNqG1mM8nyj31IGHyaPWXhjbP7cdK3qZ2bmkJ1GzRw==", - "bin": { - "sdp-verify": "checker.js" - } - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, - "dependencies": { - "semver": "~7.0.0" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, - "node_modules/unhomoglyph": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz", - "integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==" - }, - "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - } - }, - "dependencies": { - "@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@matrix-org/matrix-sdk-crypto-js": { - "version": "0.1.0-alpha.4", - "resolved": "https://registry.npmjs.org/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.4.tgz", - "integrity": "sha512-mdaDKrw3P5ZVCpq0ioW0pV6ihviDEbS8ZH36kpt9stLKHwwDSopPogE6CkQhi0B1jn1yBUtOYi32mBV/zcOR7g==" - }, - "@matrix-org/olm": { - "version": "3.2.14", - "resolved": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz", - "integrity": "sha1-rNlsAKiB0PRi4fl6Vsc3QsjbyYQ=" - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" - }, - "@types/node": { - "version": "18.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==", - "dev": true - }, - "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "another-json": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/another-json/-/another-json-0.2.0.tgz", - "integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==" - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base-x": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", - "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "bs58": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", - "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", - "requires": { - "base-x": "^4.0.0" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "commander": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", - "integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "loglevel": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", - "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==" - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "matrix-events-sdk": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz", - "integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==" - }, - "matrix-js-sdk": { - "version": "23.5.0", - "resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-23.5.0.tgz", - "integrity": "sha512-jkHJBxXcLqzz0aZ4+Hjbx7hvgryIy+DZPOxvNfM2jJM0sc803Yyu4JMZLEdx/JLwFG1KE7bFZGiXP26g5yu6Mw==", - "requires": { - "@babel/runtime": "^7.12.5", - "@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.3", - "another-json": "^0.2.0", - "bs58": "^5.0.0", - "content-type": "^1.0.4", - "loglevel": "^1.7.1", - "matrix-events-sdk": "0.0.1", - "matrix-widget-api": "^1.0.0", - "p-retry": "4", - "sdp-transform": "^2.14.1", - "unhomoglyph": "^1.0.6", - "uuid": "9" - } - }, - "matrix-widget-api": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.2.0.tgz", - "integrity": "sha512-BkBTREtXjCUM3Kx4UBgDmKoz39w7AXfIjBIC/jISBdcJkg8upFUhpIy+zUrSCIrmfO2Ke8LOsSoFoQkOyhqGxQ==", - "requires": { - "@types/events": "^3.0.0", - "events": "^3.2.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nodemon": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.21.tgz", - "integrity": "sha512-djN/n2549DUtY33S7o1djRCd7dEm0kBnj9c7S9XVXqRUbuggN1MZH/Nqa+5RFQr63Fbefq37nFXAE9VU86yL1A==", - "dev": true, - "requires": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "requires": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - } - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" - }, - "sdp-transform": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.14.1.tgz", - "integrity": "sha512-RjZyX3nVwJyCuTo5tGPx+PZWkDMCg7oOLpSlhjDdZfwUoNqG1mM8nyj31IGHyaPWXhjbP7cdK3qZ2bmkJ1GzRw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, - "requires": { - "semver": "~7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - } - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" - }, - "undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, - "unhomoglyph": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz", - "integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==" - }, - "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } - } -} diff --git a/commet/integration_test/subprocess/package.json b/commet/integration_test/subprocess/package.json deleted file mode 100644 index b3b2bea6f..000000000 --- a/commet/integration_test/subprocess/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "dependencies": { - "@matrix-org/olm": "^3.2.14", - "commander": "^10.0.0", - "matrix-js-sdk": "^23.5.0", - "typescript": "^4.9.5" - }, - "devDependencies": { - "@types/node": "^18.15.3", - "nodemon": "^2.0.21", - "ts-node": "^10.9.1" - } -} diff --git a/commet/integration_test/subprocess/src/cases/verify_me_with_emoji.ts b/commet/integration_test/subprocess/src/cases/verify_me_with_emoji.ts deleted file mode 100644 index 585aa2120..000000000 --- a/commet/integration_test/subprocess/src/cases/verify_me_with_emoji.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { MatrixClient } from "matrix-js-sdk"; -import { Phase, VerificationRequest, VerificationRequestEvent } from "matrix-js-sdk/lib/crypto/verification/request/VerificationRequest"; -function delay(ms: number) { - return new Promise( resolve => setTimeout(resolve, ms) ); -} - -export async function verify_me_with_emoji(client: MatrixClient, deviceID: string): Promise { - console.log("Running test case"); - var request = await client.requestVerification(client.getUserId()!, [deviceID]); - console.log("Request sent"); - request.on(VerificationRequestEvent.Change, async () => { - console.log("Request changed"); - console.log(request.phase); - - if(request.done || request.cancelled){ - return; - } - - await request.accept(); - - if(request.ready){ - console.log("Request ready"); - const verifier = request.beginKeyVerification('m.sas.v1', {userId: client.getUserId()!, deviceId: deviceID}); - console.log("Created verifier") - verifier.on('show_sas', async (sas_data : any)=>{ - console.log("Show sas, confirming"); - await sas_data.confirm(); - }) - - console.log("Verifying") - await verifier.verify(); - } - }); -} - \ No newline at end of file diff --git a/commet/integration_test/subprocess/src/main.ts b/commet/integration_test/subprocess/src/main.ts deleted file mode 100644 index 04c424d03..000000000 --- a/commet/integration_test/subprocess/src/main.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as sdk from 'matrix-js-sdk'; -import { isConditionalExpression } from 'typescript'; -import { verify_me_with_emoji } from './cases/verify_me_with_emoji'; - - -var olm = require('@matrix-org/olm'); -var commander = require('commander'); - -global.Olm = olm; - -commander - .version('1.0.0', '-v, --version') - .usage('[OPTIONS]...') - .option('-h, --homeserver ', 'Homeserver to use for testing') - .option('-u, --username ', 'Username to use for testing') - .option('-p, --password ', 'password to use for testing') - .option('-t, --test_case ', 'Named test case to use') - .option('-d, --device_id ', 'ID of the device to be verified') - .parse(process.argv); - -const options = commander.opts(); - -async function login(){ - const loginClient = sdk.createClient({ baseUrl: options.homeserver }); - - var result = await loginClient.login("m.login.password", {"user": options.username, "password": options.password}); - console.log(result); - - var client = sdk.createClient({ baseUrl: options.homeserver, userId: result.user_id, accessToken: result.access_token, deviceId: result.device_id }) - await client.initCrypto(); - client.startClient(); - return client; -} - -async function main(){ - if(!options.homeserver) { - console.log("Please provide a homeserver") - return; - } - - if(!options.username) { - console.log("Please provide a username"); - return; - } - - if(!options.password){ - console.log("Please provide a password"); - return - } - - if(!options.test_case){ - console.log("No test case provided"); - return - } - else{ - console.log("Received test case: " + options.test_case); - } - - const client = await login(); - - switch(options.test_case){ - case "verify_me_with_emoji": - return await verify_me_with_emoji(client, options.device_id); - } -} - - -main(); -console.log("DONE"); \ No newline at end of file diff --git a/commet/integration_test/subprocess/src/olm.ts b/commet/integration_test/subprocess/src/olm.ts deleted file mode 100644 index 53cd06b57..000000000 --- a/commet/integration_test/subprocess/src/olm.ts +++ /dev/null @@ -1 +0,0 @@ -declare module '@matrix-org/olm'; \ No newline at end of file diff --git a/commet/integration_test/subprocess/tsconfig.json b/commet/integration_test/subprocess/tsconfig.json deleted file mode 100644 index 7cd06bb9f..000000000 --- a/commet/integration_test/subprocess/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "lib": [ - "es6" - ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - "module": "commonjs", /* Specify what module code is generated. */ - "rootDir": "src", /* Specify the root folder within your source files. */ - "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - "outDir": "build", /* Specify an output folder for all emitted files. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - "strict": false, /* Enable all strict type-checking options. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} \ No newline at end of file diff --git a/commet/lib/client/matrix/matrix_timeline.dart b/commet/lib/client/matrix/matrix_timeline.dart index 9818eb1fc..848b2cf2c 100644 --- a/commet/lib/client/matrix/matrix_timeline.dart +++ b/commet/lib/client/matrix/matrix_timeline.dart @@ -3,11 +3,11 @@ import 'package:commet/cache/cache_file_provider.dart'; import 'package:commet/client/attachment.dart'; import 'package:commet/cache/file_image.dart'; import 'package:commet/client/matrix/matrix_client_extensions.dart'; +import 'package:commet/ui/atoms/rich_text/matrix_html_parser.dart'; import 'package:commet/utils/emoji/emoji_matcher.dart'; import 'package:commet/utils/text_utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_matrix_html/flutter_html.dart'; -import 'package:flutter_matrix_html/image_properties.dart'; +import 'package:path/path.dart'; import 'package:tiamat/tiamat.dart' as tiamat; import '../../ui/atoms/pill.dart'; @@ -126,35 +126,12 @@ class MatrixTimeline extends Timeline { e.bodyFormat = format; e.formattedBody = matrixEvent.formattedText; - var document = htmlParser.parseFragment( - e.formattedBody, - ); - - double emoteSize = 64; - - if (document.nodes.any((element) => !element.attributes.containsKey("data-mx-emoticon"))) { - emoteSize = 24; - } - - e.formattedContent = Html( - emoteSize: emoteSize, - imageProperties: ImageProperties(filterQuality: FilterQuality.high), - getMxcImage: (mxc, width, height, {animated}) { - return _matrixRoom.client.getMxcImage(mxc); - }, - pillBuilder: (identifier) { - return Pill( - identifier: identifier, - displayText: identifier, - url: identifier, - ); - }, - data: e.formattedBody!, - ); + if (format == "org.matrix.custom.html") e.formattedContent = MatrixHtmlParser.parse(e.formattedBody!); } else { e.bodyFormat = "chat.commet.default"; - e.formattedContent = Text.rich(TextSpan(children: TextUtils.formatString(matrixEvent.body, allowBigEmoji: true))); + e.formattedContent = + TextUtils.manageRtlSpan(matrixEvent.body, TextUtils.formatString(matrixEvent.body, allowBigEmoji: true)); } } diff --git a/commet/lib/config/app_config.dart b/commet/lib/config/app_config.dart index eb0127e00..93c034b0b 100644 --- a/commet/lib/config/app_config.dart +++ b/commet/lib/config/app_config.dart @@ -26,7 +26,6 @@ double getUiScale() { class AppConfig { static Future getDatabasePath() async { final dir = await getApplicationSupportDirectory(); - var path = join(dir.path, "hive"); - return path; + return join(dir.path, "hive"); } } diff --git a/commet/lib/config/build_config.dart b/commet/lib/config/build_config.dart index d1f679a0f..1a64cca4d 100644 --- a/commet/lib/config/build_config.dart +++ b/commet/lib/config/build_config.dart @@ -1,9 +1,9 @@ // ignore_for_file: constant_identifier_names class BuildConfig { - static const String _buildMode = String.fromEnvironment('BUILD_MODE', defaultValue: "debug"); + static const String _buildMode = String.fromEnvironment('BUILD_MODE', defaultValue: _Constants.release); - static const String _platform = String.fromEnvironment('PLATFORM', defaultValue: "desktop"); + static const String _platform = String.fromEnvironment('PLATFORM', defaultValue: _Constants._desktop); static const bool DEBUG = _buildMode == _Constants._debug; diff --git a/commet/lib/l10n/intl_en.arb b/commet/lib/l10n/intl_en.arb index 3ab64cf79..1661825fb 100644 --- a/commet/lib/l10n/intl_en.arb +++ b/commet/lib/l10n/intl_en.arb @@ -31,5 +31,6 @@ "spaceTopicPrompt": "Topic (Optional)", "joinSpacePrompt": "Join Space!", "spaceAddressPrompt": "Space Address:", - "couldNotLoadRoomPreview": "Could not load room preview" + "couldNotLoadRoomPreview": "Could not load room preview", + "sendAMessagePrompt": "Send a message..." } \ No newline at end of file diff --git a/commet/lib/ui/atoms/code_block.dart b/commet/lib/ui/atoms/code_block.dart new file mode 100644 index 000000000..46614e90b --- /dev/null +++ b/commet/lib/ui/atoms/code_block.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_highlighter/flutter_highlighter.dart'; +import 'package:flutter_highlighter/themes/atom-one-dark.dart'; +import 'package:flutter_highlighter/themes/atom-one-light.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:matrix/matrix.dart'; +import 'package:tiamat/config/style/theme_extensions.dart'; + +const _darkTheme = { + 'root': TextStyle(color: Color(0xffabb2bf), backgroundColor: Colors.transparent), + 'comment': TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic), + 'quote': TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic), + 'doctag': TextStyle(color: Color(0xffc678dd)), + 'keyword': TextStyle(color: Color(0xffc678dd)), + 'formula': TextStyle(color: Color(0xffc678dd)), + 'section': TextStyle(color: Color(0xffe06c75)), + 'name': TextStyle(color: Color(0xffe06c75)), + 'selector-tag': TextStyle(color: Color(0xffe06c75)), + 'deletion': TextStyle(color: Color(0xffe06c75)), + 'subst': TextStyle(color: Color(0xffe06c75)), + 'literal': TextStyle(color: Color(0xff56b6c2)), + 'string': TextStyle(color: Color(0xff98c379)), + 'regexp': TextStyle(color: Color(0xff98c379)), + 'addition': TextStyle(color: Color(0xff98c379)), + 'attribute': TextStyle(color: Color(0xff98c379)), + 'meta-string': TextStyle(color: Color(0xff98c379)), + 'built_in': TextStyle(color: Color(0xffe6c07b)), + 'attr': TextStyle(color: Color(0xffd19a66)), + 'variable': TextStyle(color: Color(0xffd19a66)), + 'template-variable': TextStyle(color: Color(0xffd19a66)), + 'type': TextStyle(color: Color(0xffd19a66)), + 'selector-class': TextStyle(color: Color(0xffd19a66)), + 'selector-attr': TextStyle(color: Color(0xffd19a66)), + 'selector-pseudo': TextStyle(color: Color(0xffd19a66)), + 'number': TextStyle(color: Color(0xffd19a66)), + 'symbol': TextStyle(color: Color(0xff61aeee)), + 'bullet': TextStyle(color: Color(0xff61aeee)), + 'link': TextStyle(color: Color(0xff61aeee)), + 'meta': TextStyle(color: Color(0xff61aeee)), + 'selector-id': TextStyle(color: Color(0xff61aeee)), + 'title': TextStyle(color: Color(0xff61aeee)), + 'emphasis': TextStyle(fontStyle: FontStyle.italic), + 'strong': TextStyle(fontWeight: FontWeight.bold), +}; + +const _lightTheme = { + 'root': TextStyle(color: Color(0xff383a42), backgroundColor: Colors.transparent), + 'comment': TextStyle(color: Color(0xffa0a1a7), fontStyle: FontStyle.italic), + 'quote': TextStyle(color: Color(0xffa0a1a7), fontStyle: FontStyle.italic), + 'doctag': TextStyle(color: Color(0xffa626a4)), + 'keyword': TextStyle(color: Color(0xffa626a4)), + 'formula': TextStyle(color: Color(0xffa626a4)), + 'section': TextStyle(color: Color(0xffe45649)), + 'name': TextStyle(color: Color(0xffe45649)), + 'selector-tag': TextStyle(color: Color(0xffe45649)), + 'deletion': TextStyle(color: Color(0xffe45649)), + 'subst': TextStyle(color: Color(0xffe45649)), + 'literal': TextStyle(color: Color(0xff0184bb)), + 'string': TextStyle(color: Color(0xff50a14f)), + 'regexp': TextStyle(color: Color(0xff50a14f)), + 'addition': TextStyle(color: Color(0xff50a14f)), + 'attribute': TextStyle(color: Color(0xff50a14f)), + 'meta-string': TextStyle(color: Color(0xff50a14f)), + 'built_in': TextStyle(color: Color(0xffc18401)), + 'attr': TextStyle(color: Color(0xff986801)), + 'variable': TextStyle(color: Color(0xff986801)), + 'template-variable': TextStyle(color: Color(0xff986801)), + 'type': TextStyle(color: Color(0xff986801)), + 'selector-class': TextStyle(color: Color(0xff986801)), + 'selector-attr': TextStyle(color: Color(0xff986801)), + 'selector-pseudo': TextStyle(color: Color(0xff986801)), + 'number': TextStyle(color: Color(0xff986801)), + 'symbol': TextStyle(color: Color(0xff4078f2)), + 'bullet': TextStyle(color: Color(0xff4078f2)), + 'link': TextStyle(color: Color(0xff4078f2)), + 'meta': TextStyle(color: Color(0xff4078f2)), + 'selector-id': TextStyle(color: Color(0xff4078f2)), + 'title': TextStyle(color: Color(0xff4078f2)), + 'emphasis': TextStyle(fontStyle: FontStyle.italic), + 'strong': TextStyle(fontWeight: FontWeight.bold), +}; + +class Codeblock extends StatelessWidget { + const Codeblock({required this.text, this.language, super.key}); + + final String text; + final String? language; + + @override + Widget build(BuildContext context) { + var theme = Theme.of(context).brightness == Brightness.dark ? _darkTheme : _lightTheme; + + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Theme.of(context).extension()!.surfaceLow2, + border: Border.all(color: Theme.of(context).extension()!.surfaceHigh1, width: 2)), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: language != null + ? HighlightView( + text.trim(), + language: language, + textStyle: GoogleFonts.robotoMono(), + theme: theme, + ) + : Text( + text.trim(), + style: GoogleFonts.robotoMono(), + ), + )); + } +} diff --git a/commet/lib/ui/atoms/emoji_widget.dart b/commet/lib/ui/atoms/emoji_widget.dart index 69794e937..148835ef9 100644 --- a/commet/lib/ui/atoms/emoji_widget.dart +++ b/commet/lib/ui/atoms/emoji_widget.dart @@ -41,6 +41,8 @@ class EmojiWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Image( + filterQuality: FilterQuality.high, + isAntiAlias: true, width: height, height: height, image: AssetImage("assets/twemoji/assets/72x72/$unicode.png"), diff --git a/commet/lib/ui/atoms/rich_text/matrix_html_parser.dart b/commet/lib/ui/atoms/rich_text/matrix_html_parser.dart new file mode 100644 index 000000000..16a2eccb0 --- /dev/null +++ b/commet/lib/ui/atoms/rich_text/matrix_html_parser.dart @@ -0,0 +1,105 @@ +import 'package:commet/ui/atoms/code_block.dart'; +import 'package:commet/ui/atoms/rich_text/spans/link.dart'; +import 'package:commet/utils/text_utils.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:html/parser.dart' as htmlParser; +import 'package:html/dom.dart' as dom; +import 'package:tiamat/atoms/button.dart'; +import 'package:tiamat/config/style/theme_dark.dart'; +import 'package:tiamat/tiamat.dart' as tiamat; + +class MatrixHtmlParser { + static Widget parse(String text) { + var fragment = htmlParser.parseFragment(text); + List spans = List.empty(growable: true); + print(text); + print(fragment); + fragment.nodes.forEach( + (element) { + print(element); + spans.addAll(_parseChild(element, TextStyle())); + }, + ); + + return TextUtils.manageRtlSpan(text, spans, isHtml: true); + } + + static List _parseChild(dom.Node element, TextStyle currentStyle) { + TextStyle theme = currentStyle; + if (element is dom.Text) { + return TextUtils.formatString(element.data, allowBigEmoji: true, style: theme); + } + + List parsedText = List.empty(growable: true); + + if (element is dom.Element) { + theme = updateStyle(theme, element.localName!); + var span = parseSpecial(element, theme); + + if (span != null) { + return [span]; + } + } + + for (var child in element.nodes) { + parsedText.addAll(_parseChild(child, theme)); + } + + return parsedText; + } + + static InlineSpan? parseSpecial(dom.Element element, TextStyle style) { + switch (element.localName) { + case "a": + return LinkSpan.create((element.nodes.first as dom.Text).data, + destination: element.attributes.containsKey('href') ? Uri.tryParse(element.attributes['href']!) : null, + style: style); + case "pre": + if (element.children.isEmpty) break; + if (element.children.first.localName != "code") break; + + var child = element.children.first; + String? langauge; + + if (child.attributes.containsKey('class')) { + var className = child.attributes['class']!; + if (className.startsWith('language-')) { + langauge = className.replaceAll('language-', ''); + } + } + + return WidgetSpan( + child: Codeblock( + language: langauge, + text: child.innerHtml, + )); + } + } + + static TextStyle updateStyle(TextStyle style, String type) { + switch (type) { + case "h1": + return style.copyWith(fontSize: 56, fontWeight: FontWeight.w600); + case "h2": + return style.copyWith(fontSize: 52, fontWeight: FontWeight.w600); + case "h3": + return style.copyWith(fontSize: 48, fontWeight: FontWeight.w600); + case "h4": + return style.copyWith(fontSize: 42, fontWeight: FontWeight.w600); + case "h5": + return style.copyWith(fontSize: 36, fontWeight: FontWeight.w600); + case "em": + return style.copyWith(fontStyle: FontStyle.italic); + case "strong": + return style.copyWith(fontWeight: FontWeight.bold); + case "code": + return style.copyWith( + fontFamily: GoogleFonts.robotoMono().fontFamily, backgroundColor: Colors.black.withAlpha(60)); + default: + } + + return style; + } +} diff --git a/commet/lib/ui/atoms/rich_text/spans/link.dart b/commet/lib/ui/atoms/rich_text/spans/link.dart new file mode 100644 index 000000000..f7c401ee4 --- /dev/null +++ b/commet/lib/ui/atoms/rich_text/spans/link.dart @@ -0,0 +1,16 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:tiamat/config/style/theme_dark.dart'; + +class LinkSpan { + static InlineSpan create(String text, {Uri? destination, TextStyle? style}) { + return TextSpan( + text: text, + style: + (style ?? TextStyle()).copyWith(color: ThemeDarkColors.primary, decorationColor: ThemeDarkColors.primary), + recognizer: TapGestureRecognizer() + ..onTap = () { + print("Link Tapped: ${destination.toString()}"); + }); + } +} diff --git a/commet/lib/ui/molecules/message.dart b/commet/lib/ui/molecules/message.dart index b03f86ca8..067ac68c4 100644 --- a/commet/lib/ui/molecules/message.dart +++ b/commet/lib/ui/molecules/message.dart @@ -23,6 +23,8 @@ class Message extends StatefulWidget { } class _MessageState extends State { + bool hovered = false; + @override void initState() { super.initState(); @@ -37,6 +39,32 @@ class _MessageState extends State { Widget build(BuildContext context) { return material.Material( color: material.Colors.transparent, + child: BuildConfig.MOBILE + ? material.InkWell( + child: buildContent(context), + onLongPress: () { + print("Message was long pressed"); + }, + ) + : MouseRegion( + child: buildContent(context), + onEnter: (_) { + setState(() { + hovered = true; + }); + }, + onExit: (_) { + setState(() { + hovered = false; + }); + }, + ), + ); + } + + Widget buildContent(BuildContext context) { + return Container( + color: hovered ? material.Theme.of(context).hoverColor : material.Colors.transparent, child: Padding( padding: EdgeInsets.fromLTRB(s(15), widget.showSender ? s(20) : s(4), 8, 4), child: Stack( @@ -56,41 +84,7 @@ class _MessageState extends State { image: null, isPadding: true, ), - Flexible( - child: Padding( - padding: EdgeInsets.fromLTRB(s(16), 0, s(0), 0), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.showSender) senderName(context), - if (widget.event.status == TimelineEventStatus.removed) - tiamat.Text.error(T.of(context).messageDeleted) - else if (widget.event.bodyFormat != null) - formattedBody() - else if (widget.event.body != null) - tiamat.Text.body( - widget.event.body!, - ), - if (widget.event.attachments != null) - Wrap( - children: widget.event.attachments! - .map((e) => Padding( - padding: EdgeInsets.fromLTRB(0, s(8), s(8), s(8)), - child: MessageAttachment(e), - )) - .toList(), - ), - if (widget.event.status == TimelineEventStatus.error) - Padding( - padding: const EdgeInsets.fromLTRB(0, 4, 0, 0), - child: tiamat.Text.error(T.of(context).messageFailedToSend), - ), - if (BuildConfig.DEBUG) debugInfo() - ], - ), - ), - ) + messageBody(context, selectableText: !BuildConfig.MOBILE) ], ), ), @@ -100,9 +94,43 @@ class _MessageState extends State { ); } - Widget formattedBody() { - return material.SelectionArea( - child: widget.event.formattedContent!, + Widget messageBody(BuildContext context, {bool selectableText = true}) { + return Flexible( + child: Padding( + padding: EdgeInsets.fromLTRB(s(16), 0, s(0), 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.showSender) senderName(context), + if (widget.event.status == TimelineEventStatus.removed) + tiamat.Text.error(T.of(context).messageDeleted) + else if (widget.event.bodyFormat != null) + selectableText + ? material.SelectionArea(child: widget.event.formattedContent!) + : widget.event.formattedContent! + else if (widget.event.body != null) + selectableText + ? material.SelectionArea(child: tiamat.Text.body(widget.event.body!)) + : tiamat.Text.body(widget.event.body!), + if (widget.event.attachments != null) + Wrap( + children: widget.event.attachments! + .map((e) => Padding( + padding: EdgeInsets.fromLTRB(0, s(8), s(8), s(8)), + child: MessageAttachment(e), + )) + .toList(), + ), + if (widget.event.status == TimelineEventStatus.error) + Padding( + padding: const EdgeInsets.fromLTRB(0, 4, 0, 0), + child: tiamat.Text.error(T.of(context).messageFailedToSend), + ), + if (BuildConfig.DEBUG) debugInfo() + ], + ), + ), ); } diff --git a/commet/lib/ui/molecules/message_input.dart b/commet/lib/ui/molecules/message_input.dart index 3e81c9552..71c0a4730 100644 --- a/commet/lib/ui/molecules/message_input.dart +++ b/commet/lib/ui/molecules/message_input.dart @@ -5,15 +5,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:tiamat/config/config.dart'; import 'package:tiamat/tiamat.dart'; - +import 'package:tiamat/tiamat.dart' as tiamat; +import '../../generated/l10n.dart'; import '../atoms/icon_button.dart' as i; enum MessageInputSendResult { clearText, unhandled } class MessageInput extends StatefulWidget { - const MessageInput({super.key, this.maxHeight = 200, this.onSendMessage}); + const MessageInput({super.key, this.maxHeight = 200, this.onSendMessage, this.onFocusChanged}); final double maxHeight; final MessageInputSendResult Function(String message)? onSendMessage; + final void Function(bool focused)? onFocusChanged; @override State createState() => MessageInputState(); @@ -52,9 +54,9 @@ class MessageInputState extends State { void sendMessage() { if (controller.text.isEmpty) return; + if (controller.text.trim().isEmpty) return; - MessageInputSendResult? result = - widget.onSendMessage?.call(controller.text); + MessageInputSendResult? result = widget.onSendMessage?.call(controller.text.trim()); if (result == MessageInputSendResult.clearText) { controller.clear(); } @@ -67,21 +69,18 @@ class MessageInputState extends State { padding: EdgeInsets.all(s(8.0)), child: Tile( decoration: BoxDecoration( - boxShadow: [ - BoxShadow(color: Colors.black.withAlpha(50), blurRadius: 20) - ], - color: Theme.of(context).extension()!.surfaceHigh1, - borderRadius: BorderRadius.circular(s(5))), + //boxShadow: [BoxShadow(color: Colors.black.withAlpha(50), blurRadius: 20)], + color: Theme.of(context).extension()!.surfaceLow2, + borderRadius: BorderRadius.circular(8)), child: Padding( padding: const EdgeInsets.all(0.0), child: Row( children: [ Flexible( child: ConstrainedBox( - constraints: BoxConstraints.loose( - Size.fromHeight(widget.maxHeight)), + constraints: BoxConstraints.loose(Size.fromHeight(widget.maxHeight)), child: Padding( - padding: EdgeInsets.fromLTRB(s(8), s(9), s(8), s(9)), + padding: EdgeInsets.fromLTRB(8, 9, 8, 9), child: Material( color: Colors.transparent, child: Row( @@ -89,27 +88,41 @@ class MessageInputState extends State { Expanded( child: RawKeyboardListener( focusNode: textFocus, - child: TextField( - controller: controller, - decoration: null, - maxLines: null, - cursorColor: - Theme.of(context).colorScheme.onPrimary, - cursorWidth: 1, - onChanged: (value) { - setState(() {}); - }, + child: Stack( + children: [ + Focus( + onFocusChange: (value) { + widget.onFocusChanged?.call(value); + }, + child: TextField( + controller: controller, + decoration: null, + maxLines: null, + cursorColor: Theme.of(context).colorScheme.onPrimary, + cursorWidth: 1, + onChanged: (value) { + setState(() {}); + }, + ), + ), + if (controller.text.isEmpty) + IgnorePointer( + ignoring: true, + child: Padding( + padding: const EdgeInsets.all(2.0), + child: tiamat.Text(T.of(context).sendAMessagePrompt, + type: TextType.label, color: Theme.of(context).iconTheme.color), + ), + ) + ], ), ), ), Row(children: [ - i.IconButton(size: s(24), icon: Icons.face), + const i.IconButton(size: 24, icon: Icons.face), Padding( - padding: EdgeInsets.fromLTRB(s(10), 0, 0, 0), - child: i.IconButton( - onPressed: sendMessage, - size: s(24), - icon: Icons.send), + padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), + child: i.IconButton(onPressed: sendMessage, size: 24, icon: Icons.send), ), ]) ], diff --git a/commet/lib/ui/molecules/timeline_viewer.dart b/commet/lib/ui/molecules/timeline_viewer.dart index c23799646..012243342 100644 --- a/commet/lib/ui/molecules/timeline_viewer.dart +++ b/commet/lib/ui/molecules/timeline_viewer.dart @@ -42,8 +42,7 @@ class TimelineViewerState extends State { controller .animateTo(controller.position.maxScrollExtent, - duration: const Duration(milliseconds: 500), - curve: Curves.easeOutExpo) + duration: const Duration(milliseconds: 500), curve: Curves.easeOutExpo) .then((value) { TimelineEvent? latest = split.recent.isNotEmpty ? split.recent[0] : null; if (latest == lastEvent) { @@ -70,8 +69,7 @@ class TimelineViewerState extends State { void forceToBottom() { controller.jumpTo(controller.position.maxScrollExtent); WidgetsBinding.instance.addPostFrameCallback((_) { - if (!(controller.position.pixels >= controller.position.maxScrollExtent)) - forceToBottom(); + if (!(controller.position.pixels >= controller.position.maxScrollExtent)) forceToBottom(); }); } @@ -125,8 +123,7 @@ class TimelineViewerState extends State { void handleBottomAttached() { setState(() { - attachedToBottom = - controller.position.pixels >= controller.position.maxScrollExtent; + attachedToBottom = controller.position.pixels >= controller.position.maxScrollExtent - 20; }); } @@ -139,15 +136,12 @@ class TimelineViewerState extends State { slivers: [ SliverList( delegate: SliverChildBuilderDelegate((context, index) { - int actualIndex = split.getTimelineIndex( - split.getHistoryDisplayIndex(index), - SplitTimelinePart.historical); + int actualIndex = split.getTimelineIndex(split.getHistoryDisplayIndex(index), SplitTimelinePart.historical); return TimelineEventView( event: split.historical[split.getHistoryDisplayIndex(index)], - showSender: shouldShowSender(split.getTimelineIndex( - split.getHistoryDisplayIndex(index), - SplitTimelinePart.historical)), + showSender: shouldShowSender( + split.getTimelineIndex(split.getHistoryDisplayIndex(index), SplitTimelinePart.historical)), debugInfo: "Split Part: ${split.whichList(actualIndex)} history index: $index, actual index: $actualIndex, actual index id: ${widget.timeline.events[actualIndex].eventId}", onDelete: () { @@ -158,18 +152,15 @@ class TimelineViewerState extends State { SliverList( key: newEventsListKey, delegate: SliverChildBuilderDelegate((context, index) { - int actualIndex = split.getTimelineIndex( - split.getRecentDisplayIndex(index), SplitTimelinePart.recent); + int actualIndex = split.getTimelineIndex(split.getRecentDisplayIndex(index), SplitTimelinePart.recent); return TimelineEventView( - showSender: shouldShowSender(split.getTimelineIndex( - split.getRecentDisplayIndex(index), - SplitTimelinePart.recent)), + showSender: shouldShowSender( + split.getTimelineIndex(split.getRecentDisplayIndex(index), SplitTimelinePart.recent)), event: split.recent[split.getRecentDisplayIndex(index)], debugInfo: "Split Part: ${split.whichList(actualIndex)} history index: $index, actual index: $actualIndex, actual index id: ${widget.timeline.events[actualIndex].eventId}", onDelete: () { - widget.timeline.deleteEventByIndex( - split.getTimelineIndex(index, SplitTimelinePart.recent)); + widget.timeline.deleteEventByIndex(split.getTimelineIndex(index, SplitTimelinePart.recent)); }, ); }, childCount: split.recent.length)), @@ -187,7 +178,6 @@ class TimelineViewerState extends State { .inMinutes > 1) return true; - return widget.timeline.events[index].sender != - widget.timeline.events[index + 1].sender; + return widget.timeline.events[index].sender != widget.timeline.events[index + 1].sender; } } diff --git a/commet/lib/ui/molecules/user_list.dart b/commet/lib/ui/molecules/user_list.dart index 59bf6d0a4..a76d0066e 100644 --- a/commet/lib/ui/molecules/user_list.dart +++ b/commet/lib/ui/molecules/user_list.dart @@ -26,7 +26,6 @@ class _PeerListState extends State { @override Widget build(BuildContext context) { return Tile.low1( - borderLeft: true, child: Padding( padding: const EdgeInsets.fromLTRB(8, 4, 8, 4), child: AnimatedList( diff --git a/commet/lib/ui/pages/chat/chat_page.dart b/commet/lib/ui/pages/chat/chat_page.dart index 3aa255b3c..0715fd0ed 100644 --- a/commet/lib/ui/pages/chat/chat_page.dart +++ b/commet/lib/ui/pages/chat/chat_page.dart @@ -22,9 +22,9 @@ class ChatPageState extends State { late Space? selectedSpace = null; late Room? selectedRoom = null; late bool homePageSelected = false; - late GlobalKey timelineKey = - GlobalKey(); + late GlobalKey timelineKey = GlobalKey(); late Map> timelines = {}; + double height = -1; void selectHomePage() { homePageSelected = true; @@ -34,8 +34,7 @@ class ChatPageState extends State { if (kDebugMode) { // Weird hacky work around mentioned in #2 timelines[selectedRoom?.identifier]?.currentState!.prepareForDisposal(); - WidgetsBinding.instance - .addPostFrameCallback((_) => _setSelectedSpace(space)); + WidgetsBinding.instance.addPostFrameCallback((_) => _setSelectedSpace(space)); } else { _setSelectedSpace(space); } @@ -45,8 +44,7 @@ class ChatPageState extends State { if (kDebugMode) { // Weird hacky work around mentioned in #2 timelines[selectedRoom?.identifier]?.currentState!.prepareForDisposal(); - WidgetsBinding.instance - .addPostFrameCallback((_) => _clearRoomSelection()); + WidgetsBinding.instance.addPostFrameCallback((_) => _clearRoomSelection()); } else { _clearRoomSelection(); } @@ -68,8 +66,7 @@ class ChatPageState extends State { if (kDebugMode) { // Weird hacky work around mentioned in #2 timelines[selectedRoom?.identifier]?.currentState!.prepareForDisposal(); - WidgetsBinding.instance - .addPostFrameCallback((_) => _setSelectedRoom(room)); + WidgetsBinding.instance.addPostFrameCallback((_) => _setSelectedRoom(room)); } else { _setSelectedRoom(room); } @@ -110,7 +107,24 @@ class ChatPageState extends State { onWillPop: () async { return false; }, - child: pickChatView()); + child: NotificationListener( + onNotification: (SizeChangedLayoutNotification notification) { + print("Size Changed"); + var prevHeight = height; + height = MediaQuery.of(context).size.height; + if (prevHeight == -1) return true; + + var diff = prevHeight - height; + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + var state = timelines[selectedRoom?.identifier]?.currentState; + if (state != null) { + state.controller.jumpTo(state.controller.offset + diff); + } + }); + + return true; + }, + child: SizeChangedLayoutNotifier(child: pickChatView()))); } Widget pickChatView() { diff --git a/commet/lib/ui/pages/chat/desktop_chat_page.dart b/commet/lib/ui/pages/chat/desktop_chat_page.dart index b2a40b16f..2feb56f26 100644 --- a/commet/lib/ui/pages/chat/desktop_chat_page.dart +++ b/commet/lib/ui/pages/chat/desktop_chat_page.dart @@ -36,8 +36,7 @@ class _DesktopChatPageViewState extends State { Tile.low4( child: SideNavigationBar( onSpaceSelected: (index) { - widget.state - .selectSpace(widget.state.clientManager.spaces[index]); + widget.state.selectSpace(widget.state.clientManager.spaces[index]); }, onHomeSelected: () { widget.state.selectHome(); @@ -45,16 +44,10 @@ class _DesktopChatPageViewState extends State { ), ), if (widget.state.homePageSelected) homePageView(), - if (!widget.state.homePageSelected && - widget.state.selectedSpace != null) - spaceRoomSelector(), + if (!widget.state.homePageSelected && widget.state.selectedSpace != null) spaceRoomSelector(), if (widget.state.selectedRoom != null) roomChatView(), - if (widget.state.selectedSpace != null && - widget.state.selectedRoom == null) - Expanded( - child: SpaceSummary( - key: widget.state.selectedSpace!.key, - space: widget.state.selectedSpace!)), + if (widget.state.selectedSpace != null && widget.state.selectedRoom == null) + Expanded(child: SpaceSummary(key: widget.state.selectedSpace!.key, space: widget.state.selectedSpace!)), ], ), if (widget.state.selectedRoom != null) @@ -77,8 +70,7 @@ class _DesktopChatPageViewState extends State { directMessages: widget.state.clientManager.directMessages, onSelected: (index) { setState(() { - widget.state - .selectRoom(widget.state.clientManager.directMessages[index]); + widget.state.selectRoom(widget.state.clientManager.directMessages[index]); }); }, ), @@ -90,11 +82,9 @@ class _DesktopChatPageViewState extends State { return Flexible( child: Tile( borderLeft: true, - borderRight: true, child: Column( children: [ - SizedBox( - height: s(50), child: RoomHeader(widget.state.selectedRoom!)), + SizedBox(height: s(50), child: RoomHeader(widget.state.selectedRoom!)), Flexible( child: Row( children: [ @@ -104,8 +94,7 @@ class _DesktopChatPageViewState extends State { children: [ Expanded( child: TimelineViewer( - key: widget.state - .timelines[widget.state.selectedRoom!.identifier], + key: widget.state.timelines[widget.state.selectedRoom!.identifier], timeline: widget.state.selectedRoom!.timeline!, )), Tile( @@ -147,9 +136,7 @@ class _DesktopChatPageViewState extends State { child: SpaceHeader( widget.state.selectedSpace!, onTap: widget.state.clearRoomSelection, - backgroundColor: Theme.of(context) - .extension()! - .surfaceLow1, + backgroundColor: Theme.of(context).extension()!.surfaceLow1, ), )), Expanded( @@ -158,8 +145,7 @@ class _DesktopChatPageViewState extends State { key: widget.state.selectedSpace!.key, onRoomInsert: widget.state.selectedSpace!.onRoomAdded.stream, onRoomSelected: (index) { - widget.state - .selectRoom(widget.state.selectedSpace!.rooms[index]); + widget.state.selectRoom(widget.state.selectedSpace!.rooms[index]); }, )), Tile.low2( @@ -168,8 +154,7 @@ class _DesktopChatPageViewState extends State { child: Padding( padding: const EdgeInsets.all(3.0), child: UserPanel( - displayName: - widget.state.selectedSpace!.client.user!.displayName, + displayName: widget.state.selectedSpace!.client.user!.displayName, avatar: widget.state.selectedSpace!.client.user!.avatar, detail: widget.state.selectedSpace!.client.user!.detail, color: widget.state.selectedSpace!.client.user!.color, diff --git a/commet/lib/ui/pages/chat/mobile_chat_page.dart b/commet/lib/ui/pages/chat/mobile_chat_page.dart index b607663a6..0d11d30de 100644 --- a/commet/lib/ui/pages/chat/mobile_chat_page.dart +++ b/commet/lib/ui/pages/chat/mobile_chat_page.dart @@ -37,6 +37,7 @@ class _MobileChatPageViewState extends State { late GlobalKey panelsKey; late GlobalKey messageInput = GlobalKey(); bool shouldMainIgnoreInput = false; + double height = -1; @override void initState() { @@ -74,22 +75,18 @@ class _MobileChatPageViewState extends State { widget.state.selectHome(); }, onSpaceSelected: (index) { - widget.state - .selectSpace(widget.state.clientManager.spaces[index]); + widget.state.selectSpace(widget.state.clientManager.spaces[index]); }, ), ), if (widget.state.homePageSelected) homePageView(), - if (widget.state.homePageSelected == false && - widget.state.selectedSpace != null) - spaceRoomSelector(newContext), + if (widget.state.homePageSelected == false && widget.state.selectedSpace != null) spaceRoomSelector(newContext), ], ); } Widget mainPanel() { - if (widget.state.selectedSpace != null && - widget.state.selectedRoom == null) { + if (widget.state.selectedSpace != null && widget.state.selectedRoom == null) { return SpaceSummary(space: widget.state.selectedSpace!); } @@ -153,9 +150,7 @@ class _MobileChatPageViewState extends State { height: 100.1, child: SpaceHeader( widget.state.selectedSpace!, - backgroundColor: material.Theme.of(context) - .extension()! - .surfaceLow1, + backgroundColor: material.Theme.of(context).extension()!.surfaceLow1, onTap: clearSelectedRoom, ), ), @@ -175,8 +170,7 @@ class _MobileChatPageViewState extends State { child: SizedBox( height: s(70), child: UserPanel( - displayName: - widget.state.selectedSpace!.client.user!.displayName, + displayName: widget.state.selectedSpace!.client.user!.displayName, avatar: widget.state.selectedSpace!.client.user!.avatar, detail: widget.state.selectedSpace!.client.user!.detail, color: widget.state.selectedSpace!.client.user!.color, @@ -196,29 +190,48 @@ class _MobileChatPageViewState extends State { body: SafeArea( child: Column( children: [ - SizedBox( - height: s(50), child: RoomHeader(widget.state.selectedRoom!)), + SizedBox(height: s(50), child: RoomHeader(widget.state.selectedRoom!)), Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: TimelineViewer( - key: widget.state - .timelines[widget.state.selectedRoom!.identifier], - timeline: widget.state.selectedRoom!.timeline!, - )), - Padding( - padding: EdgeInsets.fromLTRB(0, 0, 0, s(8)), - child: MessageInput( - key: messageInput, - onSendMessage: (message) { - widget.state.selectedRoom!.sendMessage(message); - return MessageInputSendResult.clearText; - }, - ), - ) - ], + child: NotificationListener( + onNotification: (notification) { + var prevHeight = height; + height = MediaQuery.of(context).viewInsets.bottom; + if (prevHeight == -1) return true; + + var diff = height - prevHeight; + if (diff <= 0) return true; + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + var state = widget.state.timelines[widget.state.selectedRoom?.identifier]?.currentState; + if (state != null) { + state.controller.jumpTo(state.controller.offset + diff); + } + }); + + return true; + }, + child: SizeChangedLayoutNotifier( + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: TimelineViewer( + key: widget.state.timelines[widget.state.selectedRoom!.identifier], + timeline: widget.state.selectedRoom!.timeline!, + )), + Padding( + padding: EdgeInsets.fromLTRB(0, 0, 0, s(8)), + child: MessageInput( + key: messageInput, + onSendMessage: (message) { + widget.state.selectedRoom!.sendMessage(message); + return MessageInputSendResult.clearText; + }, + ), + ) + ], + ), + ), ), ), ], diff --git a/commet/lib/utils/emoji/emoji.dart b/commet/lib/utils/emoji/emoji.dart index 1ceefb00b..30bb2147f 100644 --- a/commet/lib/utils/emoji/emoji.dart +++ b/commet/lib/utils/emoji/emoji.dart @@ -4,6 +4,8 @@ import 'package:commet/ui/atoms/emoji_widget.dart'; import 'package:commet/utils/emoji/emoji_matcher.dart'; import 'package:flutter/material.dart'; +import '../text_utils.dart'; + class Emoji { late ImageProvider image; String? shortcode; @@ -11,58 +13,19 @@ class Emoji { Emoji(this.image, {this.shortcode, this.unicode}); - static List emojify(List spans) { - for (int i = 0; i < spans.length; i++) { - var span = spans[i]; - if (span is! TextSpan) continue; - if (span.text == null) continue; - } - - return spans; + static List emojify(List span, {double? emojiHeight}) { + return TextUtils.formatSpan(span, (text, style) => emojifyString(text, emojiHeight: emojiHeight, style: style)); } - static List emojifyString(String text, {double? emojiHeight}) { + static List emojifyString(String text, {double? emojiHeight, TextStyle? style}) { var emojis = EmojiMatcher.find(text); - if (emojis.isEmpty) return [TextSpan(text: text)]; - - List span = List.empty(growable: true); - for (int i = 0; i < emojis.length; i++) { - var emojiMatch = emojis.elementAt(i); - - String? pre; - - if (i == 0 && emojiMatch.start > 0) { - pre = text.substring(0, emojiMatch.start); - } else if (i > 0) { - var start = emojiMatch.start; - var end = emojis.elementAt(i - 1).end; - - if (start != end) { - var previous = emojis.elementAt(i - 1); - pre = text.substring(previous.end, emojiMatch.start); - } - } - - if (pre != null) { - span.add(TextSpan(text: pre)); - } - - span.add(WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: Padding( - padding: const EdgeInsets.fromLTRB(1, 0, 1, 0), - child: EmojiWidget( - text.substring(emojiMatch.start, emojiMatch.end), - height: emojiHeight, - ), - ))); - } - - if (emojis.last.end != text.length) { - span.add(TextSpan(text: text.substring(emojis.last.end))); - } - - return span; + return TextUtils.formatMatches(emojis, text, style: style, builder: ((matchedText, style) { + return WidgetSpan( + child: EmojiWidget( + matchedText, + height: style != null && style.fontSize != null ? style.fontSize : emojiHeight, + )); + })); } } diff --git a/commet/lib/utils/text_utils.dart b/commet/lib/utils/text_utils.dart index 65403c49d..ab2b8b8d2 100644 --- a/commet/lib/utils/text_utils.dart +++ b/commet/lib/utils/text_utils.dart @@ -1,12 +1,109 @@ import 'package:commet/utils/emoji/emoji.dart'; import 'package:flutter/material.dart'; +import 'package:tiamat/tiamat.dart'; +import '../ui/atoms/rich_text/spans/link.dart'; import 'emoji/emoji_matcher.dart'; +import 'package:intl/intl.dart' as intl; +import 'package:flutter/material.dart' as material; + +final _urlRegex = RegExp( + r'([\w+]+\:\/\/)?([\w\d-]+\.)*[\w-]+[\.\:]\w+([\/\?\=\&\#.]?[\w-]+)*\/?', + caseSensitive: false, + dotAll: true, +); class TextUtils { - static List formatString(String text, {bool allowBigEmoji = false}) { + static List formatString(String text, {bool allowBigEmoji = false, TextStyle? style}) { bool bigEmoji = allowBigEmoji && shouldDoBigEmoji(text); - List span = Emoji.emojifyString(text, emojiHeight: bigEmoji ? 48 : 20); + List span = Emoji.emojifyString(text, emojiHeight: bigEmoji ? 48 : 20, style: style); + span = linkifySpan(span, style); + + return span; + } + + static List formatRichText(List spans, {TextStyle? style}) { + spans = Emoji.emojify(spans); + spans = linkifySpan(spans, style); + return spans; + } + + static List linkifySpan(List span, TextStyle? style) { + return formatSpan(span, (text, _) => linkifyString(text, style)); + } + + static bool isRtl(String text, {bool isHtml = false}) { + return intl.Bidi.detectRtlDirectionality(text, isHtml: isHtml); + } + + static Widget manageRtlSpan(String text, List spans, {bool isHtml = false}) { + bool rtl = isRtl(text, isHtml: isHtml); + return Container( + width: double.infinity, + child: material.Text.rich( + material.TextSpan(children: spans), + textAlign: rtl ? TextAlign.right : TextAlign.left, + textDirection: rtl ? TextDirection.rtl : TextDirection.ltr, + )); + } + + static List linkifyString(String text, TextStyle? style) { + var matches = _urlRegex.allMatches(text); + return formatMatches( + matches, + text, + style: style, + builder: (matchedText, _) { + return LinkSpan.create(matchedText, destination: Uri.tryParse(matchedText), style: style); + }, + ); + } + + static List formatSpan( + List span, List Function(String text, TextStyle? style) formatter) { + for (int i = span.length - 1; i >= 0; i--) { + var item = span[i]; + if (item is TextSpan) { + var spans = formatter(item.text!, item.style); + span.removeAt(i); + span.insertAll(i, spans); + } + } + return span; + } + + static List formatMatches(Iterable matches, String text, + {required InlineSpan Function(String matchedText, TextStyle? theme) builder, TextStyle? style}) { + if (matches.isEmpty) return [TextSpan(text: text, style: style)]; + + List span = List.empty(growable: true); + for (int i = 0; i < matches.length; i++) { + var match = matches.elementAt(i); + + String? pre; + + if (i == 0 && match.start > 0) { + pre = text.substring(0, match.start); + } else if (i > 0) { + var start = match.start; + var end = matches.elementAt(i - 1).end; + + if (start != end) { + var previous = matches.elementAt(i - 1); + pre = text.substring(previous.end, match.start); + } + } + + if (pre != null) { + span.add(TextSpan(text: pre, style: style)); + } + + span.add(builder(text.substring(match.start, match.end), style)); + } + + if (matches.last.end != text.length) { + span.add(TextSpan(text: text.substring(matches.last.end), style: style)); + } return span; } diff --git a/commet/linux/flutter/generated_plugin_registrant.cc b/commet/linux/flutter/generated_plugin_registrant.cc index 759b497f7..ec0c4e4ee 100644 --- a/commet/linux/flutter/generated_plugin_registrant.cc +++ b/commet/linux/flutter/generated_plugin_registrant.cc @@ -9,7 +9,6 @@ #include #include #include -#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) desktop_drop_registrar = @@ -21,7 +20,4 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) media_kit_video_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/commet/linux/flutter/generated_plugins.cmake b/commet/linux/flutter/generated_plugins.cmake index d397cb8c6..c8c28ad2a 100644 --- a/commet/linux/flutter/generated_plugins.cmake +++ b/commet/linux/flutter/generated_plugins.cmake @@ -6,7 +6,6 @@ list(APPEND FLUTTER_PLUGIN_LIST desktop_drop media_kit_libs_linux media_kit_video - url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/commet/macos/Flutter/GeneratedPluginRegistrant.swift b/commet/macos/Flutter/GeneratedPluginRegistrant.swift index d3397517d..22217257c 100644 --- a/commet/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/commet/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,11 +8,9 @@ import Foundation import desktop_drop import path_provider_foundation import shared_preferences_foundation -import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/commet/pubspec.lock b/commet/pubspec.lock index e1bb63681..87ce9af8b 100644 --- a/commet/pubspec.lock +++ b/commet/pubspec.lock @@ -323,14 +323,15 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_highlight: - dependency: transitive + flutter_highlighter: + dependency: "direct main" description: - name: flutter_highlight - sha256: "7b96333867aa07e122e245c033b8ad622e4e3a42a1a2372cbb098a2541d8782c" - url: "https://pub.dev" - source: hosted - version: "0.7.0" + path: flutter_highlighter + ref: HEAD + resolved-ref: "4de60d39c9fe4b828f90a019d0afc89e713c704e" + url: "https://github.com/commetchat/highlight.git" + source: git + version: "0.1.1" flutter_launcher_icons: dependency: "direct dev" description: @@ -360,23 +361,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.9+1" - flutter_math_fork: - dependency: transitive - description: - name: flutter_math_fork - sha256: cfec964c4975c6becc64291eb9b782fe70df5e0c5bfe0763d9e856432fcc6fcd - url: "https://pub.dev" - source: hosted - version: "0.4.2+2" - flutter_matrix_html: - dependency: "direct main" - description: - path: "." - ref: HEAD - resolved-ref: f9683a7a3205df6585907322772be3af25a45757 - url: "https://github.com/commetchat/flutter_matrix_html.git" - source: git - version: "1.1.0" flutter_olm: dependency: "direct main" description: @@ -393,14 +377,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0" - flutter_svg: - dependency: transitive - description: - name: flutter_svg - sha256: "9ac1967e2f72a08af11b05b39167920f90d043cf67163d13a544a358c8f31afa" - url: "https://pub.dev" - source: hosted - version: "0.22.0" flutter_test: dependency: "direct dev" description: flutter @@ -452,10 +428,10 @@ packages: dependency: "direct main" description: name: google_fonts - sha256: "8f099045e2f2a30e4d4d0a35f40c6bc941a8f2ca0e10ad9d214ee9edd3f37483" + sha256: "927573f2e8a8d65c17931e21918ad0ab0666b1b636537de7c4932bdb487b190f" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "4.0.3" graphs: dependency: transitive description: @@ -464,14 +440,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" - highlight: + highlighter: dependency: transitive description: - name: highlight - sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21" + name: highlighter + sha256: "92180c72b9da8758e1acf39a45aa305a97dcfe2fdc8f3d1d2947c23f2772bfbc" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.1.1" hive: dependency: "direct main" description: @@ -573,14 +549,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" - isolate: - dependency: transitive - description: - name: isolate - sha256: "3554ab10fdeec965d27e0074c913ccb2229887633da080d2b35a6322da14938b" - url: "https://pub.dev" - source: hosted - version: "2.1.1" js: dependency: transitive description: @@ -661,14 +629,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.10" - matrix_link_text: - dependency: transitive - description: - name: matrix_link_text - sha256: "1860efd69df505807d1b7963794708d1f76cf67b866a00d11ee81e05dc56aaca" - url: "https://pub.dev" - source: hosted - version: "1.0.2" media_kit: dependency: "direct main" description: @@ -773,22 +733,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" - path_drawing: - dependency: transitive - description: - name: path_drawing - sha256: "3bdd251dae9ffaef944450b73f168610db7e968e7b20daf0c3907f8b4aafc8a2" - url: "https://pub.dev" - source: hosted - version: "0.5.1+1" - path_parsing: - dependency: transitive - description: - name: path_parsing - sha256: ee5c47c1058ad66b4a41746ec3996af9593d0858872807bcd64ac118f0700337 - url: "https://pub.dev" - source: hosted - version: "0.2.1" path_provider: dependency: "direct main" description: @@ -1169,14 +1113,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" - tuple: - dependency: transitive - description: - name: tuple - sha256: "0ea99cd2f9352b2586583ab2ce6489d1f95a5f6de6fb9492faaf97ae2060f0aa" - url: "https://pub.dev" - source: hosted - version: "2.0.1" typed_data: dependency: transitive description: @@ -1201,70 +1137,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - url_launcher: - dependency: transitive - description: - name: url_launcher - sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" - url: "https://pub.dev" - source: hosted - version: "6.1.10" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: dd729390aa936bf1bdf5cd1bc7468ff340263f80a2c4f569416507667de8e3c8 - url: "https://pub.dev" - source: hosted - version: "6.0.26" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: "3dedc66ca3c0bef9e6a93c0999aee102556a450afcc1b7bcfeace7a424927d92" - url: "https://pub.dev" - source: hosted - version: "6.1.3" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: "206fb8334a700ef7754d6a9ed119e7349bc830448098f21a69bf1b4ed038cabc" - url: "https://pub.dev" - source: hosted - version: "3.0.4" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: "0ef2b4f97942a16523e51256b799e9aa1843da6c60c55eefbfa9dbc2dcb8331a" - url: "https://pub.dev" - source: hosted - version: "3.0.4" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa" - url: "https://pub.dev" - source: hosted - version: "2.0.16" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: a83ba3607a507758669cfafb03f9de09bf6e6280c14d9b9cb18f013e406dcacd - url: "https://pub.dev" - source: hosted - version: "3.0.5" vector_math: dependency: transitive description: diff --git a/commet/pubspec.yaml b/commet/pubspec.yaml index f7543913f..4fe430554 100644 --- a/commet/pubspec.yaml +++ b/commet/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: sdk: flutter provider: ^6.0.0 - google_fonts: ^3.0.1 + google_fonts: ^4.0.3 matrix: ^0.18.0 flutter_olm: ^1.2.0 flutter_openssl_crypto: ^0.1.0 @@ -44,11 +44,10 @@ dependencies: hive_generator: ^1.1.3 hive_flutter: ^1.1.0 shared_preferences: ^2.0.18 - flutter_matrix_html: + flutter_highlighter: git: - url: https://github.com/commetchat/flutter_matrix_html.git - - + url: https://github.com/commetchat/highlight.git + path: ./flutter_highlighter dev_dependencies: build_runner: widgetbook_generator: @@ -62,11 +61,6 @@ dev_dependencies: flutter_launcher_icons: flutter_lints: ^2.0.0 -dependency_overrides: - # Override to local mono-repo path so devs can test this repo - # against changes that they're making to other mono-repo packages - #flutter_matrix_html: - # path: ../../../flutter_matrix_html flutter_icons: android: "launcher_icon" ios: true diff --git a/commet/windows/flutter/generated_plugin_registrant.cc b/commet/windows/flutter/generated_plugin_registrant.cc index 69cfe0f79..117609bc7 100644 --- a/commet/windows/flutter/generated_plugin_registrant.cc +++ b/commet/windows/flutter/generated_plugin_registrant.cc @@ -8,13 +8,10 @@ #include #include -#include void RegisterPlugins(flutter::PluginRegistry* registry) { DesktopDropPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("DesktopDropPlugin")); MediaKitVideoPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); - UrlLauncherWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/commet/windows/flutter/generated_plugins.cmake b/commet/windows/flutter/generated_plugins.cmake index 0ee58e2a6..9cf668466 100644 --- a/commet/windows/flutter/generated_plugins.cmake +++ b/commet/windows/flutter/generated_plugins.cmake @@ -5,7 +5,6 @@ list(APPEND FLUTTER_PLUGIN_LIST desktop_drop media_kit_video - url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/tiamat/lib/atoms/text.dart b/tiamat/lib/atoms/text.dart index ac7ba1cde..f77011326 100644 --- a/tiamat/lib/atoms/text.dart +++ b/tiamat/lib/atoms/text.dart @@ -139,25 +139,28 @@ class Text extends StatelessWidget { switch (type) { case TextType.label: - style = material.Theme.of(context).textTheme.labelLarge!.copyWith(fontWeight: FontWeight.w300); + style = material.Theme.of(context).textTheme.labelLarge!.copyWith(fontWeight: FontWeight.w300, color: color); break; case TextType.labelEmphasised: - style = material.Theme.of(context).textTheme.labelLarge!.copyWith(fontWeight: FontWeight.w400); + style = material.Theme.of(context).textTheme.labelLarge!.copyWith(fontWeight: FontWeight.w400, color: color); break; case TextType.error: style = material.Theme.of(context) .textTheme .bodyMedium! - .copyWith(fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.error); + .copyWith(fontWeight: FontWeight.w500, color: color ?? Theme.of(context).colorScheme.error); break; case TextType.tiny: - style = material.Theme.of(context).textTheme.bodyMedium!.copyWith(fontWeight: FontWeight.w300, fontSize: 10); + style = material.Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(fontWeight: FontWeight.w300, fontSize: 10, color: color); break; case TextType.body: - style = material.Theme.of(context).textTheme.bodyMedium!.copyWith(fontWeight: FontWeight.w300); + style = material.Theme.of(context).textTheme.bodyMedium!.copyWith(fontWeight: FontWeight.w300, color: color); break; case TextType.largeTitle: - style = material.Theme.of(context).textTheme.titleLarge!; + style = material.Theme.of(context).textTheme.titleLarge!.copyWith(color: color); break; case TextType.name: style = material.Theme.of(context)