Native set load measured (#8242)
* Track notification loaded on native side instead of JS * iOS details and iOS 18 changes * Fix tests
This commit is contained in:
parent
e7d597f70e
commit
bc8a42b161
9 changed files with 89 additions and 4 deletions
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import RNUtils from '@mattermost/rnutils';
|
||||
import performance from 'react-native-performance';
|
||||
|
||||
import {mockApiClient} from '@test/mock_api_client';
|
||||
|
|
@ -64,6 +65,12 @@ describe('load metrics', () => {
|
|||
'setInterval',
|
||||
]}).setSystemTime(new Date(TEST_EPOCH));
|
||||
PerformanceMetricsManager = new PerformanceMetricsManagerClass();
|
||||
|
||||
const mockHasRegisteredLoad = {hasRegisteredLoad: false};
|
||||
jest.mocked(RNUtils.setHasRegisteredLoad).mockImplementation(() => {
|
||||
mockHasRegisteredLoad.hasRegisteredLoad = true;
|
||||
});
|
||||
jest.mocked(RNUtils.getHasRegisteredLoad).mockImplementation(() => mockHasRegisteredLoad);
|
||||
});
|
||||
afterEach(async () => {
|
||||
jest.useRealTimers();
|
||||
|
|
@ -91,6 +98,26 @@ describe('load metrics', () => {
|
|||
expect(mockApiClient.post).toHaveBeenCalledWith(expectedUrl, expectedRequest);
|
||||
});
|
||||
|
||||
it('only register load once', async () => {
|
||||
performance.mark('nativeLaunchStart');
|
||||
const measure = getMeasure(TEST_EPOCH, 0);
|
||||
const expectedRequest = getBaseReportRequest(measure.timestamp, measure.timestamp + 1);
|
||||
expectedRequest.body.histograms = [measure];
|
||||
|
||||
PerformanceMetricsManager.setLoadTarget('HOME');
|
||||
PerformanceMetricsManager.finishLoad('HOME', serverUrl);
|
||||
await TestHelper.tick();
|
||||
jest.advanceTimersByTime(INTERVAL_TIME);
|
||||
await TestHelper.tick();
|
||||
expect(mockApiClient.post).toHaveBeenCalledWith(expectedUrl, expectedRequest);
|
||||
mockApiClient.post.mockClear();
|
||||
PerformanceMetricsManager.finishLoad('HOME', serverUrl);
|
||||
await TestHelper.tick();
|
||||
jest.advanceTimersByTime(INTERVAL_TIME);
|
||||
await TestHelper.tick();
|
||||
expect(mockApiClient.post).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('retry if the mark is not yet present', async () => {
|
||||
const measure = getMeasure(TEST_EPOCH + (RETRY_TIME * 2), RETRY_TIME);
|
||||
const expectedRequest = getBaseReportRequest(measure.timestamp, measure.timestamp + 1);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import RNUtils from '@mattermost/rnutils';
|
||||
import {AppState, type AppStateStatus} from 'react-native';
|
||||
import performance from 'react-native-performance';
|
||||
|
||||
|
|
@ -18,7 +19,6 @@ const MAX_RETRIES = 3;
|
|||
class PerformanceMetricsManager {
|
||||
private target: Target;
|
||||
private batchers: {[serverUrl: string]: Batcher} = {};
|
||||
private hasRegisteredLoad = false;
|
||||
private lastAppStateIsActive = AppState.currentState === 'active';
|
||||
|
||||
constructor() {
|
||||
|
|
@ -49,7 +49,7 @@ class PerformanceMetricsManager {
|
|||
}
|
||||
|
||||
public skipLoadMetric() {
|
||||
this.hasRegisteredLoad = true;
|
||||
RNUtils.setHasRegisteredLoad();
|
||||
}
|
||||
|
||||
public finishLoad(location: Target, serverUrl: string) {
|
||||
|
|
@ -57,7 +57,7 @@ class PerformanceMetricsManager {
|
|||
}
|
||||
|
||||
private finishLoadWithRetries(location: Target, serverUrl: string, retries: number) {
|
||||
if (this.target !== location || this.hasRegisteredLoad) {
|
||||
if (this.target !== location || RNUtils.getHasRegisteredLoad().hasRegisteredLoad) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ class PerformanceMetricsManager {
|
|||
logWarning('We could not retrieve the mobile load metric');
|
||||
}
|
||||
|
||||
this.hasRegisteredLoad = true;
|
||||
RNUtils.setHasRegisteredLoad();
|
||||
}
|
||||
|
||||
public startMetric(metricName: MetricName) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class RNUtilsModuleImpl(private val reactContext: ReactApplicationContext) {
|
|||
const val NAME = "RNUtils"
|
||||
|
||||
private var context: ReactApplicationContext? = null
|
||||
private var hasRegisteredLoad = false
|
||||
|
||||
fun sendJSEvent(eventName: String, data: ReadableMap?) {
|
||||
if (context?.hasActiveReactInstance() == true) {
|
||||
|
|
@ -73,6 +74,16 @@ class RNUtilsModuleImpl(private val reactContext: ReactApplicationContext) {
|
|||
return SplitView.getWindowDimensions()
|
||||
}
|
||||
|
||||
fun setHasRegisteredLoad() {
|
||||
hasRegisteredLoad = true
|
||||
}
|
||||
|
||||
fun getHasRegisteredLoad(): WritableMap {
|
||||
val map = Arguments.createMap()
|
||||
map.putBoolean("hasRegisteredLoad", hasRegisteredLoad)
|
||||
return map
|
||||
}
|
||||
|
||||
fun unlockOrientation() {}
|
||||
|
||||
fun lockPortrait() {}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ class RNUtilsModule(val reactContext: ReactApplicationContext) : NativeRNUtilsSp
|
|||
|
||||
override fun getWindowDimensions(): WritableMap? = implementation.getWindowDimensions()
|
||||
|
||||
override fun setHasRegisteredLoad() = implementation.setHasRegisteredLoad()
|
||||
override fun getHasRegisteredLoad(): WritableMap = implementation.getHasRegisteredLoad()
|
||||
|
||||
override fun unlockOrientation() {
|
||||
implementation.unlockOrientation()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,16 @@ class RNUtilsModule(context: ReactApplicationContext) :
|
|||
return implementation.getWindowDimensions()
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun setHasRegisteredLoad() {
|
||||
implementation.setHasRegisteredLoad()
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
fun getHasRegisteredLoad(): WritableMap {
|
||||
return implementation.getHasRegisteredLoad()
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun unlockOrientation() {
|
||||
implementation.unlockOrientation()
|
||||
|
|
|
|||
|
|
@ -76,6 +76,14 @@ RCT_REMAP_BLOCKING_SYNCHRONOUS_METHOD(getWindowDimensions, NSDictionary*, window
|
|||
return [wrapper getWindowDimensions];
|
||||
}
|
||||
|
||||
RCT_REMAP_BLOCKING_SYNCHRONOUS_METHOD(getHasRegisteredLoad, NSDictionary*, getLoad) {
|
||||
return [wrapper getHasRegisteredLoad];
|
||||
}
|
||||
|
||||
RCT_REMAP_METHOD(setHasRegisteredLoad, setLoad) {
|
||||
[wrapper setHasRegisteredLoad];
|
||||
}
|
||||
|
||||
RCT_REMAP_METHOD(unlockOrientation, unlock) {
|
||||
[wrapper unlockOrientation];
|
||||
}
|
||||
|
|
@ -166,6 +174,14 @@ RCT_EXPORT_METHOD(saveFile:(NSString *)filePath
|
|||
return [wrapper getWindowDimensions];
|
||||
}
|
||||
|
||||
- (NSDictionary *)getHasRegisteredLoad {
|
||||
return [wrapper getHasRegisteredLoad];
|
||||
}
|
||||
|
||||
- (void)setHasRegisteredLoad {
|
||||
[wrapper setHasRegisteredLoad];
|
||||
}
|
||||
|
||||
- (void)lockPortrait {
|
||||
[wrapper lockOrientation];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import React
|
|||
|
||||
@objc public class RNUtilsWrapper: NSObject {
|
||||
@objc public weak var delegate: RNUtilsDelegate? = nil
|
||||
@objc private var hasRegisteredLoad = false
|
||||
|
||||
deinit {
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -251,6 +252,14 @@ import React
|
|||
group.wait()
|
||||
return dimensions
|
||||
}
|
||||
|
||||
@objc public func setHasRegisteredLoad() {
|
||||
hasRegisteredLoad = true
|
||||
}
|
||||
|
||||
@objc public func getHasRegisteredLoad() -> Dictionary<String, Any> {
|
||||
return ["hasRegisteredLoad": hasRegisteredLoad]
|
||||
}
|
||||
|
||||
@objc public func unlockOrientation() {
|
||||
DispatchQueue.main.async {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ export type WindowDimensionsChanged = Readonly<{
|
|||
height: Double;
|
||||
}>
|
||||
|
||||
type HasRegisteredLoadResponse = {
|
||||
hasRegisteredLoad: boolean;
|
||||
}
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
readonly getConstants: () => Constants;
|
||||
|
||||
|
|
@ -55,6 +59,9 @@ export interface Spec extends TurboModule {
|
|||
unlockOrientation: () => void;
|
||||
lockPortrait: () => void;
|
||||
|
||||
getHasRegisteredLoad: () => HasRegisteredLoadResponse;
|
||||
setHasRegisteredLoad: () => void;
|
||||
|
||||
deleteDatabaseDirectory: (databaseName: string, shouldRemoveDirectory: boolean) => DatabaseOperationResult;
|
||||
renameDatabase: (databaseName: string, newDatabaseName: string) => DatabaseOperationResult;
|
||||
deleteEntitiesFile: () => boolean;
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ jest.doMock('react-native', () => {
|
|||
addListener: jest.fn(),
|
||||
removeListeners: jest.fn(),
|
||||
isRunningInSplitView: jest.fn().mockReturnValue({isSplit: false, isTablet: false}),
|
||||
getHasRegisteredLoad: jest.fn().mockReturnValue({hasRegisteredLoad: false}),
|
||||
setHasRegisteredLoad: jest.fn(),
|
||||
|
||||
getDeliveredNotifications: jest.fn().mockResolvedValue([]),
|
||||
removeChannelNotifications: jest.fn().mockImplementation(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue