Merge branch 'main' into pat/archive-room-names
This commit is contained in:
commit
78c610ab92
|
|
@ -0,0 +1 @@
|
||||||
|
* @famedly/instant-messaging
|
||||||
|
|
@ -2,15 +2,6 @@ name: "All the sdk specific jobs"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
|
||||||
flutter_version:
|
|
||||||
description: "The flutter version used for tests and builds"
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
dart_version:
|
|
||||||
description: "The dart version used for tests and builds"
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
e2ee_test:
|
e2ee_test:
|
||||||
|
|
@ -22,13 +13,14 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
export NETWORK='--network mynet'
|
export NETWORK='--network mynet'
|
||||||
docker network create mynet
|
docker network create mynet
|
||||||
# deploy homeserver instance
|
# deploy homeserver instance
|
||||||
scripts/integration-server-${{matrix.homeserver}}.sh
|
scripts/integration-server-${{matrix.homeserver}}.sh
|
||||||
docker run $NETWORK --env GITHUB_ACTIONS="${GITHUB_ACTIONS}" --env HOMESERVER_IMPLEMENTATION="${{matrix.homeserver}}" --env HOMESERVER="${{startsWith('dendrite', matrix.homeserver) && format('{0}:8008', matrix.homeserver) || matrix.homeserver }}" --volume="$(pwd):/workdir" --workdir /workdir ghcr.io/famedly/container-image-flutter/flutter:${{inputs.flutter_version}} /bin/bash -c "set -e
|
docker run $NETWORK --env GITHUB_ACTIONS="${GITHUB_ACTIONS}" --env HOMESERVER_IMPLEMENTATION="${{matrix.homeserver}}" --env HOMESERVER="${{startsWith('dendrite', matrix.homeserver) && format('{0}:8008', matrix.homeserver) || matrix.homeserver }}" --volume="$(pwd):/workdir" --workdir /workdir ghcr.io/famedly/container-image-flutter/flutter:${{env.flutter_version}} /bin/bash -c "set -e
|
||||||
scripts/integration-prepare-alpine.sh
|
scripts/integration-prepare-alpine.sh
|
||||||
# create test user environment variables
|
# create test user environment variables
|
||||||
source scripts/integration-create-environment-variables.sh
|
source scripts/integration-create-environment-variables.sh
|
||||||
|
|
@ -42,11 +34,13 @@ jobs:
|
||||||
# coverage_without_olm is done on dart images because why not :D
|
# coverage_without_olm is done on dart images because why not :D
|
||||||
coverage:
|
coverage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
|
||||||
image: ghcr.io/famedly/container-image-flutter/flutter-linux:${{inputs.flutter_version}}
|
|
||||||
options: --user root
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
|
||||||
|
- uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa
|
||||||
|
with:
|
||||||
|
flutter-version: ${{ env.flutter_version }}
|
||||||
|
cache: true
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
sed -i 's/#flutter_test/flutter_test/g' pubspec.yaml
|
sed -i 's/#flutter_test/flutter_test/g' pubspec.yaml
|
||||||
|
|
@ -56,12 +50,14 @@ jobs:
|
||||||
|
|
||||||
coverage_without_olm:
|
coverage_without_olm:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
|
||||||
image: dart:${{inputs.dart_version}}
|
|
||||||
env:
|
env:
|
||||||
NO_OLM: 1
|
NO_OLM: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
|
||||||
|
- uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46
|
||||||
|
with:
|
||||||
|
sdk: ${{ env.dart_version }}
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install --no-install-recommends --no-install-suggests -y curl lcov python3 python3-distutils libsqlite3-dev
|
apt-get update && apt-get install --no-install-recommends --no-install-suggests -y curl lcov python3 python3-distutils libsqlite3-dev
|
||||||
|
|
@ -71,10 +67,12 @@ jobs:
|
||||||
|
|
||||||
pub-dev-dry-run:
|
pub-dev-dry-run:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
|
||||||
image: dart:${{inputs.dart_version}}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
|
||||||
|
- uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46
|
||||||
|
with:
|
||||||
|
sdk: ${{ env.dart_version }}
|
||||||
- name: pub.dev publish dry run
|
- name: pub.dev publish dry run
|
||||||
run: |
|
run: |
|
||||||
dart pub get
|
dart pub get
|
||||||
|
|
|
||||||
|
|
@ -13,31 +13,13 @@ concurrency:
|
||||||
group: ${{ github.ref }}
|
group: ${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
|
||||||
FLUTTER_VERSION: 3.10.6
|
|
||||||
DART_VERSION: 3.0.6
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# because there is no easy way to pass env variables to jobs
|
|
||||||
versions:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
flutter_version: ${{ steps.flutterver.outputs.FLUTTER_VERSION }}
|
|
||||||
dart_version: ${{ steps.dartver.outputs.DART_VERSION }}
|
|
||||||
steps:
|
|
||||||
- id: flutterver
|
|
||||||
run: echo "FLUTTER_VERSION=${{ env.FLUTTER_VERSION }}" >> "$GITHUB_OUTPUT"
|
|
||||||
- id: dartver
|
|
||||||
run: echo "DART_VERSION=${{ env.DART_VERSION }}" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
dart:
|
dart:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
uses: famedly/frontend-ci-templates/.github/workflows/dart.yml@main
|
uses: famedly/frontend-ci-templates/.github/workflows/dart.yml@main
|
||||||
needs: [versions]
|
|
||||||
with:
|
with:
|
||||||
flutter_version: ${{ needs.versions.outputs.flutter_version }}
|
env_file: ".github/workflows/versions.env"
|
||||||
dart_version: ${{ needs.versions.outputs.dart_version }}
|
|
||||||
secrets:
|
secrets:
|
||||||
ssh_key: "${{ secrets.CI_SSH_PRIVATE_KEY }}"
|
ssh_key: "${{ secrets.CI_SSH_PRIVATE_KEY }}"
|
||||||
|
|
||||||
|
|
@ -49,7 +31,3 @@ jobs:
|
||||||
app_jobs:
|
app_jobs:
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
uses: ./.github/workflows/app.yml
|
uses: ./.github/workflows/app.yml
|
||||||
needs: [versions]
|
|
||||||
with:
|
|
||||||
flutter_version: ${{ needs.versions.outputs.flutter_version }}
|
|
||||||
dart_version: ${{ needs.versions.outputs.dart_version }}
|
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,5 @@ jobs:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
uses: famedly/frontend-ci-templates/.github/workflows/publish-pub.yml@main
|
uses: famedly/frontend-ci-templates/.github/workflows/publish-pub.yml@main
|
||||||
|
with:
|
||||||
|
env_file: ".github/workflows/versions.env"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
flutter_version=3.19.0
|
||||||
|
dart_version=3.3.0
|
||||||
|
|
@ -1,3 +1,12 @@
|
||||||
|
## [0.25.11] 26th Februray 2024
|
||||||
|
- feat: Implement handling soft logout (Krille)
|
||||||
|
- feat: Store accesstokenExpiresIn and call softlogout 5 minutes before (Krille)
|
||||||
|
- fix: convert boxNames to List in clear function when creating transaction (Gabby Gurdin)
|
||||||
|
|
||||||
|
## [0.25.10] 23rd February 2024
|
||||||
|
- chore: remove state events both in imp and preview events list (td)
|
||||||
|
- feat: specify history_visibility when creating group chat (Karthikeyan S)
|
||||||
|
|
||||||
## [0.25.9] 14th February 2024
|
## [0.25.9] 14th February 2024
|
||||||
- fix: group calls terminator having sync glares (td)
|
- fix: group calls terminator having sync glares (td)
|
||||||
- fix: ignore expired calls rather than killing them (td)
|
- fix: ignore expired calls rather than killing them (td)
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,10 @@ class Client extends MatrixApi {
|
||||||
|
|
||||||
bool shareKeysWithUnverifiedDevices;
|
bool shareKeysWithUnverifiedDevices;
|
||||||
|
|
||||||
|
Future<void> Function(Client client)? onSoftLogout;
|
||||||
|
|
||||||
|
DateTime? accessTokenExpiresAt;
|
||||||
|
|
||||||
// For CommandsClientExtension
|
// For CommandsClientExtension
|
||||||
final Map<String, FutureOr<String?> Function(CommandArgs)> commands = {};
|
final Map<String, FutureOr<String?> Function(CommandArgs)> commands = {};
|
||||||
final Filter syncFilter;
|
final Filter syncFilter;
|
||||||
|
|
@ -161,6 +165,12 @@ class Client extends MatrixApi {
|
||||||
Set<KeyVerificationMethod>? verificationMethods,
|
Set<KeyVerificationMethod>? verificationMethods,
|
||||||
http.Client? httpClient,
|
http.Client? httpClient,
|
||||||
Set<String>? importantStateEvents,
|
Set<String>? importantStateEvents,
|
||||||
|
|
||||||
|
/// You probably don't want to add state events which are also
|
||||||
|
/// in important state events to this list, or get ready to face
|
||||||
|
/// only having one event of that particular type in preLoad because
|
||||||
|
/// previewEvents are stored with stateKey '' not the actual state key
|
||||||
|
/// of your state event
|
||||||
Set<String>? roomPreviewLastEvents,
|
Set<String>? roomPreviewLastEvents,
|
||||||
this.pinUnreadRooms = false,
|
this.pinUnreadRooms = false,
|
||||||
this.pinInvitedRooms = true,
|
this.pinInvitedRooms = true,
|
||||||
|
|
@ -178,6 +188,13 @@ class Client extends MatrixApi {
|
||||||
this.shareKeysWithUnverifiedDevices = true,
|
this.shareKeysWithUnverifiedDevices = true,
|
||||||
this.enableDehydratedDevices = false,
|
this.enableDehydratedDevices = false,
|
||||||
this.receiptsPublicByDefault = true,
|
this.receiptsPublicByDefault = true,
|
||||||
|
|
||||||
|
/// Implement your https://spec.matrix.org/v1.9/client-server-api/#soft-logout
|
||||||
|
/// logic here.
|
||||||
|
/// Set this to `refreshAccessToken()` for the easiest way to handle the
|
||||||
|
/// most common reason for soft logouts.
|
||||||
|
/// You can also perform a new login here by passing the existing deviceId.
|
||||||
|
this.onSoftLogout,
|
||||||
}) : syncFilter = syncFilter ??
|
}) : syncFilter = syncFilter ??
|
||||||
Filter(
|
Filter(
|
||||||
room: RoomFilter(
|
room: RoomFilter(
|
||||||
|
|
@ -216,14 +233,53 @@ class Client extends MatrixApi {
|
||||||
EventTypes.CallAnswer,
|
EventTypes.CallAnswer,
|
||||||
EventTypes.CallReject,
|
EventTypes.CallReject,
|
||||||
EventTypes.CallHangup,
|
EventTypes.CallHangup,
|
||||||
EventTypes.GroupCallPrefix,
|
|
||||||
EventTypes.GroupCallMemberPrefix,
|
/// hack because having them both in important events and roomPreivew
|
||||||
|
/// makes the statekey '' which means you can only have one event of that
|
||||||
|
/// type
|
||||||
|
// EventTypes.GroupCallPrefix,
|
||||||
|
// EventTypes.GroupCallMemberPrefix,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// register all the default commands
|
// register all the default commands
|
||||||
registerDefaultCommands();
|
registerDefaultCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetches the refreshToken from the database and tries to get a new
|
||||||
|
/// access token from the server and then stores it correctly. Unlike the
|
||||||
|
/// pure API call of `Client.refresh()` this handles the complete soft
|
||||||
|
/// logout case.
|
||||||
|
/// Throws an Exception if there is no refresh token available or the
|
||||||
|
/// client is not logged in.
|
||||||
|
Future<void> refreshAccessToken() async {
|
||||||
|
final storedClient = await database?.getClient(clientName);
|
||||||
|
final refreshToken = storedClient?.tryGet<String>('refresh_token');
|
||||||
|
if (refreshToken == null) {
|
||||||
|
throw Exception('No refresh token available');
|
||||||
|
}
|
||||||
|
final homeserverUrl = homeserver?.toString();
|
||||||
|
final userId = userID;
|
||||||
|
final deviceId = deviceID;
|
||||||
|
if (homeserverUrl == null || userId == null || deviceId == null) {
|
||||||
|
throw Exception('Cannot refresh access token when not logged in');
|
||||||
|
}
|
||||||
|
|
||||||
|
final tokenResponse = await refresh(refreshToken);
|
||||||
|
|
||||||
|
accessToken = tokenResponse.accessToken;
|
||||||
|
await database?.updateClient(
|
||||||
|
homeserverUrl,
|
||||||
|
tokenResponse.accessToken,
|
||||||
|
accessTokenExpiresAt,
|
||||||
|
tokenResponse.refreshToken,
|
||||||
|
userId,
|
||||||
|
deviceId,
|
||||||
|
deviceName,
|
||||||
|
prevBatch,
|
||||||
|
encryption?.pickledOlmAccount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// The required name for this client.
|
/// The required name for this client.
|
||||||
final String clientName;
|
final String clientName;
|
||||||
|
|
||||||
|
|
@ -475,6 +531,7 @@ class Client extends MatrixApi {
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
initialDeviceDisplayName: initialDeviceDisplayName,
|
initialDeviceDisplayName: initialDeviceDisplayName,
|
||||||
inhibitLogin: inhibitLogin,
|
inhibitLogin: inhibitLogin,
|
||||||
|
refreshToken: refreshToken ?? onSoftLogout != null,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Connect if there is an access token in the response.
|
// Connect if there is an access token in the response.
|
||||||
|
|
@ -486,8 +543,15 @@ class Client extends MatrixApi {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
'Registered but token, device ID, user ID or homeserver is null.');
|
'Registered but token, device ID, user ID or homeserver is null.');
|
||||||
}
|
}
|
||||||
|
final expiresInMs = response.expiresInMs;
|
||||||
|
final tokenExpiresAt = expiresInMs == null
|
||||||
|
? null
|
||||||
|
: DateTime.now().add(Duration(milliseconds: expiresInMs));
|
||||||
|
|
||||||
await init(
|
await init(
|
||||||
newToken: accessToken,
|
newToken: accessToken,
|
||||||
|
newTokenExpiresAt: tokenExpiresAt,
|
||||||
|
newRefreshToken: response.refreshToken,
|
||||||
newUserID: userId,
|
newUserID: userId,
|
||||||
newHomeserver: homeserver,
|
newHomeserver: homeserver,
|
||||||
newDeviceName: initialDeviceDisplayName ?? '',
|
newDeviceName: initialDeviceDisplayName ?? '',
|
||||||
|
|
@ -538,6 +602,7 @@ class Client extends MatrixApi {
|
||||||
medium: medium,
|
medium: medium,
|
||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
address: address,
|
address: address,
|
||||||
|
refreshToken: refreshToken ?? onSoftLogout != null,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Connect if there is an access token in the response.
|
// Connect if there is an access token in the response.
|
||||||
|
|
@ -548,8 +613,16 @@ class Client extends MatrixApi {
|
||||||
if (homeserver_ == null) {
|
if (homeserver_ == null) {
|
||||||
throw Exception('Registered but homerserver is null.');
|
throw Exception('Registered but homerserver is null.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final expiresInMs = response.expiresInMs;
|
||||||
|
final tokenExpiresAt = expiresInMs == null
|
||||||
|
? null
|
||||||
|
: DateTime.now().add(Duration(milliseconds: expiresInMs));
|
||||||
|
|
||||||
await init(
|
await init(
|
||||||
newToken: accessToken,
|
newToken: accessToken,
|
||||||
|
newTokenExpiresAt: tokenExpiresAt,
|
||||||
|
newRefreshToken: response.refreshToken,
|
||||||
newUserID: userId,
|
newUserID: userId,
|
||||||
newHomeserver: homeserver_,
|
newHomeserver: homeserver_,
|
||||||
newDeviceName: initialDeviceDisplayName ?? '',
|
newDeviceName: initialDeviceDisplayName ?? '',
|
||||||
|
|
@ -695,6 +768,7 @@ class Client extends MatrixApi {
|
||||||
CreateRoomPreset preset = CreateRoomPreset.privateChat,
|
CreateRoomPreset preset = CreateRoomPreset.privateChat,
|
||||||
List<StateEvent>? initialState,
|
List<StateEvent>? initialState,
|
||||||
Visibility? visibility,
|
Visibility? visibility,
|
||||||
|
HistoryVisibility? historyVisibility,
|
||||||
bool waitForSync = true,
|
bool waitForSync = true,
|
||||||
bool groupCall = false,
|
bool groupCall = false,
|
||||||
Map<String, dynamic>? powerLevelContentOverride,
|
Map<String, dynamic>? powerLevelContentOverride,
|
||||||
|
|
@ -712,6 +786,17 @@ class Client extends MatrixApi {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (historyVisibility != null) {
|
||||||
|
initialState ??= [];
|
||||||
|
if (!initialState.any((s) => s.type == EventTypes.HistoryVisibility)) {
|
||||||
|
initialState.add(StateEvent(
|
||||||
|
content: {
|
||||||
|
'history_visibility': historyVisibility.text,
|
||||||
|
},
|
||||||
|
type: EventTypes.HistoryVisibility,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (groupCall) {
|
if (groupCall) {
|
||||||
powerLevelContentOverride ??= {};
|
powerLevelContentOverride ??= {};
|
||||||
powerLevelContentOverride['events'] = <String, dynamic>{
|
powerLevelContentOverride['events'] = <String, dynamic>{
|
||||||
|
|
@ -1452,6 +1537,8 @@ class Client extends MatrixApi {
|
||||||
/// `userDeviceKeysLoading` where it is necessary.
|
/// `userDeviceKeysLoading` where it is necessary.
|
||||||
Future<void> init({
|
Future<void> init({
|
||||||
String? newToken,
|
String? newToken,
|
||||||
|
DateTime? newTokenExpiresAt,
|
||||||
|
String? newRefreshToken,
|
||||||
Uri? newHomeserver,
|
Uri? newHomeserver,
|
||||||
String? newUserID,
|
String? newUserID,
|
||||||
String? newDeviceName,
|
String? newDeviceName,
|
||||||
|
|
@ -1509,6 +1596,11 @@ class Client extends MatrixApi {
|
||||||
_id = account['client_id'];
|
_id = account['client_id'];
|
||||||
homeserver = Uri.parse(account['homeserver_url']);
|
homeserver = Uri.parse(account['homeserver_url']);
|
||||||
accessToken = this.accessToken = account['token'];
|
accessToken = this.accessToken = account['token'];
|
||||||
|
final tokenExpiresAtMs =
|
||||||
|
int.tryParse(account.tryGet<String>('token_expires_at') ?? '');
|
||||||
|
accessTokenExpiresAt = tokenExpiresAtMs == null
|
||||||
|
? null
|
||||||
|
: DateTime.fromMillisecondsSinceEpoch(tokenExpiresAtMs);
|
||||||
userID = _userID = account['user_id'];
|
userID = _userID = account['user_id'];
|
||||||
_deviceID = account['device_id'];
|
_deviceID = account['device_id'];
|
||||||
_deviceName = account['device_name'];
|
_deviceName = account['device_name'];
|
||||||
|
|
@ -1518,6 +1610,7 @@ class Client extends MatrixApi {
|
||||||
}
|
}
|
||||||
if (newToken != null) {
|
if (newToken != null) {
|
||||||
accessToken = this.accessToken = newToken;
|
accessToken = this.accessToken = newToken;
|
||||||
|
accessTokenExpiresAt = newTokenExpiresAt;
|
||||||
homeserver = newHomeserver;
|
homeserver = newHomeserver;
|
||||||
userID = _userID = newUserID;
|
userID = _userID = newUserID;
|
||||||
_deviceID = newDeviceID;
|
_deviceID = newDeviceID;
|
||||||
|
|
@ -1525,6 +1618,7 @@ class Client extends MatrixApi {
|
||||||
olmAccount = newOlmAccount;
|
olmAccount = newOlmAccount;
|
||||||
} else {
|
} else {
|
||||||
accessToken = this.accessToken = newToken ?? accessToken;
|
accessToken = this.accessToken = newToken ?? accessToken;
|
||||||
|
accessTokenExpiresAt = newTokenExpiresAt ?? accessTokenExpiresAt;
|
||||||
homeserver = newHomeserver ?? homeserver;
|
homeserver = newHomeserver ?? homeserver;
|
||||||
userID = _userID = newUserID ?? userID;
|
userID = _userID = newUserID ?? userID;
|
||||||
_deviceID = newDeviceID ?? _deviceID;
|
_deviceID = newDeviceID ?? _deviceID;
|
||||||
|
|
@ -1565,6 +1659,8 @@ class Client extends MatrixApi {
|
||||||
await database.updateClient(
|
await database.updateClient(
|
||||||
homeserver.toString(),
|
homeserver.toString(),
|
||||||
accessToken,
|
accessToken,
|
||||||
|
accessTokenExpiresAt,
|
||||||
|
newRefreshToken,
|
||||||
userID,
|
userID,
|
||||||
_deviceID,
|
_deviceID,
|
||||||
_deviceName,
|
_deviceName,
|
||||||
|
|
@ -1576,6 +1672,8 @@ class Client extends MatrixApi {
|
||||||
clientName,
|
clientName,
|
||||||
homeserver.toString(),
|
homeserver.toString(),
|
||||||
accessToken,
|
accessToken,
|
||||||
|
accessTokenExpiresAt,
|
||||||
|
newRefreshToken,
|
||||||
userID,
|
userID,
|
||||||
_deviceID,
|
_deviceID,
|
||||||
_deviceName,
|
_deviceName,
|
||||||
|
|
@ -1722,6 +1820,15 @@ class Client extends MatrixApi {
|
||||||
Object? syncError;
|
Object? syncError;
|
||||||
await _checkSyncFilter();
|
await _checkSyncFilter();
|
||||||
|
|
||||||
|
// Call onSoftLogout 5 minutes before access token expires to prevent
|
||||||
|
// failing network requests.
|
||||||
|
final tokenExpiresAt = accessTokenExpiresAt;
|
||||||
|
if (onSoftLogout != null &&
|
||||||
|
tokenExpiresAt != null &&
|
||||||
|
tokenExpiresAt.difference(DateTime.now()) <= Duration(minutes: 5)) {
|
||||||
|
await onSoftLogout?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
// The timeout we send to the server for the sync loop. It says to the
|
// The timeout we send to the server for the sync loop. It says to the
|
||||||
// server that we want to receive an empty sync response after this
|
// server that we want to receive an empty sync response after this
|
||||||
// amount of time if nothing happens.
|
// amount of time if nothing happens.
|
||||||
|
|
@ -1800,9 +1907,20 @@ class Client extends MatrixApi {
|
||||||
onSyncStatus.add(SyncStatusUpdate(SyncStatus.error,
|
onSyncStatus.add(SyncStatusUpdate(SyncStatus.error,
|
||||||
error: SdkError(exception: e, stackTrace: s)));
|
error: SdkError(exception: e, stackTrace: s)));
|
||||||
if (e.error == MatrixError.M_UNKNOWN_TOKEN) {
|
if (e.error == MatrixError.M_UNKNOWN_TOKEN) {
|
||||||
|
final onSoftLogout = this.onSoftLogout;
|
||||||
|
if (e.raw.tryGet<bool>('soft_logout') == true && onSoftLogout != null) {
|
||||||
|
Logs().w('The user has been soft logged out! Try to login again...');
|
||||||
|
try {
|
||||||
|
await onSoftLogout(this);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logs().e('Unable to login again', e, s);
|
||||||
|
await clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Logs().w('The user has been logged out!');
|
Logs().w('The user has been logged out!');
|
||||||
await clear();
|
await clear();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} on SyncConnectionException catch (e, s) {
|
} on SyncConnectionException catch (e, s) {
|
||||||
Logs().w('Syncloop failed: Client has not connection to the server');
|
Logs().w('Syncloop failed: Client has not connection to the server');
|
||||||
onSyncStatus.add(SyncStatusUpdate(SyncStatus.error,
|
onSyncStatus.add(SyncStatusUpdate(SyncStatus.error,
|
||||||
|
|
@ -3086,10 +3204,16 @@ class Client extends MatrixApi {
|
||||||
Logs().i('Found data in the legacy database!');
|
Logs().i('Found data in the legacy database!');
|
||||||
onMigration?.call();
|
onMigration?.call();
|
||||||
_id = migrateClient['client_id'];
|
_id = migrateClient['client_id'];
|
||||||
|
final tokenExpiresAtMs =
|
||||||
|
int.tryParse(migrateClient.tryGet<String>('token_expires_at') ?? '');
|
||||||
await database.insertClient(
|
await database.insertClient(
|
||||||
clientName,
|
clientName,
|
||||||
migrateClient['homeserver_url'],
|
migrateClient['homeserver_url'],
|
||||||
migrateClient['token'],
|
migrateClient['token'],
|
||||||
|
tokenExpiresAtMs == null
|
||||||
|
? null
|
||||||
|
: DateTime.fromMillisecondsSinceEpoch(tokenExpiresAtMs),
|
||||||
|
migrateClient['refresh_token'],
|
||||||
migrateClient['user_id'],
|
migrateClient['user_id'],
|
||||||
migrateClient['device_id'],
|
migrateClient['device_id'],
|
||||||
migrateClient['device_name'],
|
migrateClient['device_name'],
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ abstract class DatabaseApi {
|
||||||
Future updateClient(
|
Future updateClient(
|
||||||
String homeserverUrl,
|
String homeserverUrl,
|
||||||
String token,
|
String token,
|
||||||
|
DateTime? tokenExpiresAt,
|
||||||
|
String? refreshToken,
|
||||||
String userId,
|
String userId,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|
@ -44,6 +46,8 @@ abstract class DatabaseApi {
|
||||||
String name,
|
String name,
|
||||||
String homeserverUrl,
|
String homeserverUrl,
|
||||||
String token,
|
String token,
|
||||||
|
DateTime? tokenExpiresAt,
|
||||||
|
String? refreshToken,
|
||||||
String userId,
|
String userId,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|
|
||||||
|
|
@ -785,6 +785,8 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
||||||
String name,
|
String name,
|
||||||
String homeserverUrl,
|
String homeserverUrl,
|
||||||
String token,
|
String token,
|
||||||
|
DateTime? tokenExpiresAt,
|
||||||
|
String? refreshToken,
|
||||||
String userId,
|
String userId,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|
@ -794,6 +796,19 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
||||||
await _clientBox.put('homeserver_url', homeserverUrl);
|
await _clientBox.put('homeserver_url', homeserverUrl);
|
||||||
await _clientBox.put('token', token);
|
await _clientBox.put('token', token);
|
||||||
await _clientBox.put('user_id', userId);
|
await _clientBox.put('user_id', userId);
|
||||||
|
if (refreshToken == null) {
|
||||||
|
await _clientBox.delete('refresh_token');
|
||||||
|
} else {
|
||||||
|
await _clientBox.put('refresh_token', refreshToken);
|
||||||
|
}
|
||||||
|
if (tokenExpiresAt == null) {
|
||||||
|
await _clientBox.delete('token_expires_at');
|
||||||
|
} else {
|
||||||
|
await _clientBox.put(
|
||||||
|
'token_expires_at',
|
||||||
|
tokenExpiresAt.millisecondsSinceEpoch.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (deviceId == null) {
|
if (deviceId == null) {
|
||||||
await _clientBox.delete('device_id');
|
await _clientBox.delete('device_id');
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1371,6 +1386,8 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
||||||
Future<void> updateClient(
|
Future<void> updateClient(
|
||||||
String homeserverUrl,
|
String homeserverUrl,
|
||||||
String token,
|
String token,
|
||||||
|
DateTime? tokenExpiresAt,
|
||||||
|
String? refreshToken,
|
||||||
String userId,
|
String userId,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|
@ -1380,6 +1397,17 @@ class HiveCollectionsDatabase extends DatabaseApi {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _clientBox.put('homeserver_url', homeserverUrl);
|
await _clientBox.put('homeserver_url', homeserverUrl);
|
||||||
await _clientBox.put('token', token);
|
await _clientBox.put('token', token);
|
||||||
|
if (tokenExpiresAt == null) {
|
||||||
|
await _clientBox.delete('token_expires_at');
|
||||||
|
} else {
|
||||||
|
await _clientBox.put('token_expires_at',
|
||||||
|
tokenExpiresAt.millisecondsSinceEpoch.toString());
|
||||||
|
}
|
||||||
|
if (refreshToken == null) {
|
||||||
|
await _clientBox.delete('refresh_token');
|
||||||
|
} else {
|
||||||
|
await _clientBox.put('refresh_token', refreshToken);
|
||||||
|
}
|
||||||
await _clientBox.put('user_id', userId);
|
await _clientBox.put('user_id', userId);
|
||||||
if (deviceId == null) {
|
if (deviceId == null) {
|
||||||
await _clientBox.delete('device_id');
|
await _clientBox.delete('device_id');
|
||||||
|
|
|
||||||
|
|
@ -750,6 +750,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin {
|
||||||
String name,
|
String name,
|
||||||
String homeserverUrl,
|
String homeserverUrl,
|
||||||
String token,
|
String token,
|
||||||
|
DateTime? tokenExpiresAt,
|
||||||
|
String? refreshToken,
|
||||||
String userId,
|
String userId,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|
@ -757,6 +759,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin {
|
||||||
String? olmAccount) async {
|
String? olmAccount) async {
|
||||||
await _clientBox.put('homeserver_url', homeserverUrl);
|
await _clientBox.put('homeserver_url', homeserverUrl);
|
||||||
await _clientBox.put('token', token);
|
await _clientBox.put('token', token);
|
||||||
|
await _clientBox.put(
|
||||||
|
'token_expires_at', tokenExpiresAt?.millisecondsSinceEpoch.toString());
|
||||||
|
await _clientBox.put('refresh_token', refreshToken);
|
||||||
await _clientBox.put('user_id', userId);
|
await _clientBox.put('user_id', userId);
|
||||||
await _clientBox.put('device_id', deviceId);
|
await _clientBox.put('device_id', deviceId);
|
||||||
await _clientBox.put('device_name', deviceName);
|
await _clientBox.put('device_name', deviceName);
|
||||||
|
|
@ -1314,6 +1319,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin {
|
||||||
Future<void> updateClient(
|
Future<void> updateClient(
|
||||||
String homeserverUrl,
|
String homeserverUrl,
|
||||||
String token,
|
String token,
|
||||||
|
DateTime? tokenExpiresAt,
|
||||||
|
String? refreshToken,
|
||||||
String userId,
|
String userId,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|
@ -1322,6 +1329,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin {
|
||||||
) async {
|
) async {
|
||||||
await _clientBox.put('homeserver_url', homeserverUrl);
|
await _clientBox.put('homeserver_url', homeserverUrl);
|
||||||
await _clientBox.put('token', token);
|
await _clientBox.put('token', token);
|
||||||
|
await _clientBox.put(
|
||||||
|
'token_expires_at', tokenExpiresAt?.millisecondsSinceEpoch.toString());
|
||||||
|
await _clientBox.put('refresh_token', refreshToken);
|
||||||
await _clientBox.put('user_id', userId);
|
await _clientBox.put('user_id', userId);
|
||||||
await _clientBox.put('device_id', deviceId);
|
await _clientBox.put('device_id', deviceId);
|
||||||
await _clientBox.put('device_name', deviceName);
|
await _clientBox.put('device_name', deviceName);
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ class BoxCollection with ZoneTransactionMixin {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> clear() async {
|
Future<void> clear() async {
|
||||||
final txn = _db.transaction(boxNames, 'readwrite');
|
final txn = _db.transaction(boxNames.toList(), 'readwrite');
|
||||||
for (final name in boxNames) {
|
for (final name in boxNames) {
|
||||||
unawaited(txn.objectStore(name).clear());
|
unawaited(txn.objectStore(name).clear());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -727,6 +727,8 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
String name,
|
String name,
|
||||||
String homeserverUrl,
|
String homeserverUrl,
|
||||||
String token,
|
String token,
|
||||||
|
DateTime? tokenExpiresAt,
|
||||||
|
String? refreshToken,
|
||||||
String userId,
|
String userId,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|
@ -735,6 +737,17 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _clientBox.put('homeserver_url', homeserverUrl);
|
await _clientBox.put('homeserver_url', homeserverUrl);
|
||||||
await _clientBox.put('token', token);
|
await _clientBox.put('token', token);
|
||||||
|
if (tokenExpiresAt == null) {
|
||||||
|
await _clientBox.delete('token_expires_at');
|
||||||
|
} else {
|
||||||
|
await _clientBox.put('token_expires_at',
|
||||||
|
tokenExpiresAt.millisecondsSinceEpoch.toString());
|
||||||
|
}
|
||||||
|
if (refreshToken == null) {
|
||||||
|
await _clientBox.delete('refresh_token');
|
||||||
|
} else {
|
||||||
|
await _clientBox.put('refresh_token', refreshToken);
|
||||||
|
}
|
||||||
await _clientBox.put('user_id', userId);
|
await _clientBox.put('user_id', userId);
|
||||||
if (deviceId == null) {
|
if (deviceId == null) {
|
||||||
await _clientBox.delete('device_id');
|
await _clientBox.delete('device_id');
|
||||||
|
|
@ -1343,6 +1356,8 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
Future<void> updateClient(
|
Future<void> updateClient(
|
||||||
String homeserverUrl,
|
String homeserverUrl,
|
||||||
String token,
|
String token,
|
||||||
|
DateTime? tokenExpiresAt,
|
||||||
|
String? refreshToken,
|
||||||
String userId,
|
String userId,
|
||||||
String? deviceId,
|
String? deviceId,
|
||||||
String? deviceName,
|
String? deviceName,
|
||||||
|
|
@ -1352,6 +1367,17 @@ class MatrixSdkDatabase extends DatabaseApi {
|
||||||
await transaction(() async {
|
await transaction(() async {
|
||||||
await _clientBox.put('homeserver_url', homeserverUrl);
|
await _clientBox.put('homeserver_url', homeserverUrl);
|
||||||
await _clientBox.put('token', token);
|
await _clientBox.put('token', token);
|
||||||
|
if (tokenExpiresAt == null) {
|
||||||
|
await _clientBox.delete('token_expires_at');
|
||||||
|
} else {
|
||||||
|
await _clientBox.put('token_expires_at',
|
||||||
|
tokenExpiresAt.millisecondsSinceEpoch.toString());
|
||||||
|
}
|
||||||
|
if (refreshToken == null) {
|
||||||
|
await _clientBox.delete('refresh_token');
|
||||||
|
} else {
|
||||||
|
await _clientBox.put('refresh_token', refreshToken);
|
||||||
|
}
|
||||||
await _clientBox.put('user_id', userId);
|
await _clientBox.put('user_id', userId);
|
||||||
if (deviceId == null) {
|
if (deviceId == null) {
|
||||||
await _clientBox.delete('device_id');
|
await _clientBox.delete('device_id');
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class Receipt {
|
||||||
const Receipt(this.user, this.time);
|
const Receipt(this.user, this.time);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) => (other is Receipt &&
|
bool operator ==(Object other) => (other is Receipt &&
|
||||||
other.user == user &&
|
other.user == user &&
|
||||||
other.time.millisecondsSinceEpoch == time.millisecondsSinceEpoch);
|
other.time.millisecondsSinceEpoch == time.millisecondsSinceEpoch);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@ const Map<GuestAccess, String> _guestAccessMap = {
|
||||||
GuestAccess.forbidden: 'forbidden',
|
GuestAccess.forbidden: 'forbidden',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extension GuestAccessExtension on GuestAccess {
|
||||||
|
String get text => _guestAccessMap[this]!;
|
||||||
|
}
|
||||||
|
|
||||||
const Map<HistoryVisibility, String> _historyVisibilityMap = {
|
const Map<HistoryVisibility, String> _historyVisibilityMap = {
|
||||||
HistoryVisibility.invited: 'invited',
|
HistoryVisibility.invited: 'invited',
|
||||||
HistoryVisibility.joined: 'joined',
|
HistoryVisibility.joined: 'joined',
|
||||||
|
|
@ -52,6 +56,10 @@ const Map<HistoryVisibility, String> _historyVisibilityMap = {
|
||||||
HistoryVisibility.worldReadable: 'world_readable',
|
HistoryVisibility.worldReadable: 'world_readable',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extension HistoryVisibilityExtension on HistoryVisibility {
|
||||||
|
String get text => _historyVisibilityMap[this]!;
|
||||||
|
}
|
||||||
|
|
||||||
const String messageSendingStatusKey =
|
const String messageSendingStatusKey =
|
||||||
'com.famedly.famedlysdk.message_sending_status';
|
'com.famedly.famedlysdk.message_sending_status';
|
||||||
|
|
||||||
|
|
@ -2135,7 +2143,7 @@ class Room {
|
||||||
EventTypes.GuestAccess,
|
EventTypes.GuestAccess,
|
||||||
'',
|
'',
|
||||||
{
|
{
|
||||||
'guest_access': _guestAccessMap[guestAccess],
|
'guest_access': guestAccess.text,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|
@ -2160,7 +2168,7 @@ class Room {
|
||||||
EventTypes.HistoryVisibility,
|
EventTypes.HistoryVisibility,
|
||||||
'',
|
'',
|
||||||
{
|
{
|
||||||
'history_visibility': _historyVisibilityMap[historyVisibility],
|
'history_visibility': historyVisibility.text,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|
@ -2353,7 +2361,7 @@ class Room {
|
||||||
: setSpaceChild(roomId, via: const []);
|
: setSpaceChild(roomId, via: const []);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) => (other is Room && other.id == id);
|
bool operator ==(Object other) => (other is Room && other.id == id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hashAll([id]);
|
int get hashCode => Object.hashAll([id]);
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ class User extends Event {
|
||||||
room.canChangePowerLevel && powerLevel < room.ownPowerLevel;
|
room.canChangePowerLevel && powerLevel < room.ownPowerLevel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) => (other is User &&
|
bool operator ==(Object other) => (other is User &&
|
||||||
other.id == id &&
|
other.id == id &&
|
||||||
other.room == room &&
|
other.room == room &&
|
||||||
other.membership == membership);
|
other.membership == membership);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
// ignore_for_file: deprecated_member_use
|
||||||
|
// ignoring the elementAt deprecation because this would make the SDK
|
||||||
|
// incompatible with older flutter versions than 3.19.0 or dart 3.3.0
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@ abstract class SignableKey extends MatrixSignableKey {
|
||||||
String toString() => json.encode(toJson());
|
String toString() => json.encode(toJson());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) => (other is SignableKey &&
|
bool operator ==(Object other) => (other is SignableKey &&
|
||||||
other.userId == userId &&
|
other.userId == userId &&
|
||||||
other.identifier == identifier);
|
other.identifier == identifier);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: matrix
|
name: matrix
|
||||||
description: Matrix Dart SDK
|
description: Matrix Dart SDK
|
||||||
version: 0.25.9
|
version: 0.25.11
|
||||||
homepage: https://famedly.com
|
homepage: https://famedly.com
|
||||||
repository: https://github.com/famedly/matrix-dart-sdk.git
|
repository: https://github.com/famedly/matrix-dart-sdk.git
|
||||||
issue_tracker: https://github.com/famedly/matrix-dart-sdk/issues
|
issue_tracker: https://github.com/famedly/matrix-dart-sdk/issues
|
||||||
|
|
|
||||||
|
|
@ -964,6 +964,35 @@ void main() {
|
||||||
await client.dispose(closeDatabase: true);
|
await client.dispose(closeDatabase: true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('refreshAccessToken', () async {
|
||||||
|
final client = await getClient();
|
||||||
|
expect(client.accessToken, 'abcd');
|
||||||
|
await client.refreshAccessToken();
|
||||||
|
expect(client.accessToken, 'a_new_token');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handleSoftLogout', () async {
|
||||||
|
final client = await getClient();
|
||||||
|
expect(client.accessToken, 'abcd');
|
||||||
|
var softLoggedOut = 0;
|
||||||
|
client.onSoftLogout = (client) {
|
||||||
|
softLoggedOut++;
|
||||||
|
return client.refreshAccessToken();
|
||||||
|
};
|
||||||
|
FakeMatrixApi.expectedAccessToken = 'a_new_token';
|
||||||
|
await client.oneShotSync();
|
||||||
|
await client.oneShotSync();
|
||||||
|
FakeMatrixApi.expectedAccessToken = null;
|
||||||
|
expect(client.accessToken, 'a_new_token');
|
||||||
|
expect(softLoggedOut, 1);
|
||||||
|
final storedClient = await client.database?.getClient(client.clientName);
|
||||||
|
expect(storedClient?.tryGet<String>('token'), 'a_new_token');
|
||||||
|
expect(
|
||||||
|
storedClient?.tryGet<String>('refresh_token'),
|
||||||
|
'another_new_token',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('object equality', () async {
|
test('object equality', () async {
|
||||||
final time1 = DateTime.fromMillisecondsSinceEpoch(1);
|
final time1 = DateTime.fromMillisecondsSinceEpoch(1);
|
||||||
final time2 = DateTime.fromMillisecondsSinceEpoch(0);
|
final time2 = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
|
|
|
||||||
|
|
@ -129,10 +129,13 @@ void main() {
|
||||||
await database.getClient('name');
|
await database.getClient('name');
|
||||||
});
|
});
|
||||||
test('insertClient', () async {
|
test('insertClient', () async {
|
||||||
|
final now = DateTime.now();
|
||||||
await database.insertClient(
|
await database.insertClient(
|
||||||
'name',
|
'name',
|
||||||
'homeserverUrl',
|
'homeserverUrl',
|
||||||
'token',
|
'token',
|
||||||
|
now,
|
||||||
|
'refresh_token',
|
||||||
'userId',
|
'userId',
|
||||||
'deviceId',
|
'deviceId',
|
||||||
'deviceName',
|
'deviceName',
|
||||||
|
|
@ -142,11 +145,17 @@ void main() {
|
||||||
|
|
||||||
final client = await database.getClient('name');
|
final client = await database.getClient('name');
|
||||||
expect(client?['token'], 'token');
|
expect(client?['token'], 'token');
|
||||||
|
expect(
|
||||||
|
client?['token_expires_at'],
|
||||||
|
now.millisecondsSinceEpoch.toString(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
test('updateClient', () async {
|
test('updateClient', () async {
|
||||||
await database.updateClient(
|
await database.updateClient(
|
||||||
'homeserverUrl',
|
'homeserverUrl',
|
||||||
'token_different',
|
'token_different',
|
||||||
|
DateTime.now(),
|
||||||
|
'refresh_token',
|
||||||
'userId',
|
'userId',
|
||||||
'deviceId',
|
'deviceId',
|
||||||
'deviceName',
|
'deviceName',
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,14 @@ Future<Client> getClient() async {
|
||||||
'testclient',
|
'testclient',
|
||||||
httpClient: FakeMatrixApi(),
|
httpClient: FakeMatrixApi(),
|
||||||
databaseBuilder: getDatabase,
|
databaseBuilder: getDatabase,
|
||||||
|
onSoftLogout: (client) => client.refreshAccessToken(),
|
||||||
);
|
);
|
||||||
FakeMatrixApi.client = client;
|
FakeMatrixApi.client = client;
|
||||||
await client.checkHomeserver(Uri.parse('https://fakeServer.notExisting'),
|
await client.checkHomeserver(Uri.parse('https://fakeServer.notExisting'),
|
||||||
checkWellKnown: false);
|
checkWellKnown: false);
|
||||||
await client.init(
|
await client.init(
|
||||||
newToken: 'abcd',
|
newToken: 'abcd',
|
||||||
|
newRefreshToken: 'refresh_abcd',
|
||||||
newUserID: '@test:fakeServer.notExisting',
|
newUserID: '@test:fakeServer.notExisting',
|
||||||
newHomeserver: client.homeserver,
|
newHomeserver: client.homeserver,
|
||||||
newDeviceName: 'Text Matrix Client',
|
newDeviceName: 'Text Matrix Client',
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ Map<String, dynamic> decodeJson(dynamic data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeMatrixApi extends BaseClient {
|
class FakeMatrixApi extends BaseClient {
|
||||||
|
static String? expectedAccessToken;
|
||||||
|
|
||||||
static Map<String, List<dynamic>> get calledEndpoints =>
|
static Map<String, List<dynamic>> get calledEndpoints =>
|
||||||
currentApi!._calledEndpoints;
|
currentApi!._calledEndpoints;
|
||||||
static int get eventCounter => currentApi!._eventCounter;
|
static int get eventCounter => currentApi!._eventCounter;
|
||||||
|
|
@ -129,6 +131,23 @@ class FakeMatrixApi extends BaseClient {
|
||||||
'<html><head></head><body>Not found...</body></html>', 404);
|
'<html><head></head><body>Not found...</body></html>', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!{
|
||||||
|
'/client/v3/refresh',
|
||||||
|
'/client/v3/login',
|
||||||
|
'/client/v3/register',
|
||||||
|
}.contains(action) &&
|
||||||
|
expectedAccessToken != null &&
|
||||||
|
request.headers['Authorization'] != 'Bearer $expectedAccessToken') {
|
||||||
|
return Response(
|
||||||
|
jsonEncode({
|
||||||
|
'errcode': 'M_UNKNOWN_TOKEN',
|
||||||
|
'error': 'Soft logged out',
|
||||||
|
'soft_logout': true,
|
||||||
|
}),
|
||||||
|
401,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Call API
|
// Call API
|
||||||
(_calledEndpoints[action] ??= <dynamic>[]).add(data);
|
(_calledEndpoints[action] ??= <dynamic>[]).add(data);
|
||||||
final act = api[method]?[action];
|
final act = api[method]?[action];
|
||||||
|
|
@ -2013,6 +2032,11 @@ class FakeMatrixApi extends BaseClient {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'POST': {
|
'POST': {
|
||||||
|
'/client/v3/refresh': (var req) => {
|
||||||
|
'access_token': 'a_new_token',
|
||||||
|
'expires_in_ms': 60000,
|
||||||
|
'refresh_token': 'another_new_token'
|
||||||
|
},
|
||||||
'/client/v3/delete_devices': (var req) => {},
|
'/client/v3/delete_devices': (var req) => {},
|
||||||
'/client/v3/account/3pid/add': (var req) => {},
|
'/client/v3/account/3pid/add': (var req) => {},
|
||||||
'/client/v3/account/3pid/bind': (var req) => {},
|
'/client/v3/account/3pid/bind': (var req) => {},
|
||||||
|
|
@ -2397,6 +2421,7 @@ class FakeMatrixApi extends BaseClient {
|
||||||
'/client/v3/login': (var req) => {
|
'/client/v3/login': (var req) => {
|
||||||
'user_id': '@test:fakeServer.notExisting',
|
'user_id': '@test:fakeServer.notExisting',
|
||||||
'access_token': 'abc123',
|
'access_token': 'abc123',
|
||||||
|
'refresh_token': 'refresh_abc123',
|
||||||
'device_id': 'GHTYAJCE',
|
'device_id': 'GHTYAJCE',
|
||||||
'well_known': {
|
'well_known': {
|
||||||
'm.homeserver': {'base_url': 'https://example.org'},
|
'm.homeserver': {'base_url': 'https://example.org'},
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ void main() {
|
||||||
'testclient',
|
'testclient',
|
||||||
'https://example.org',
|
'https://example.org',
|
||||||
'blubb',
|
'blubb',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
'@test:example.org',
|
'@test:example.org',
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue