TF-4265 Refactor: Move Regex pattern generation to Main Thread to leverage static caching and reduce Isolate initialization overhead.

This commit is contained in:
dab246
2026-01-30 15:36:04 +07:00
committed by Dat H. Pham
parent 55e27c78ed
commit 74c0c75023
5 changed files with 32 additions and 29 deletions
@@ -882,10 +882,7 @@ class ComposerController extends BaseController
}
final emailContent = await getContentInEditor();
if (!context.mounted) {
_sendButtonState = ButtonState.enabled;
return;
}
final attachmentKeywords = await validateAttachmentReminder(
emailSubject: subjectEmail.value ?? '',
emailContent: emailContent,
@@ -893,6 +890,7 @@ class ComposerController extends BaseController
if (!context.mounted) {
logWarning('ComposerController::_prepareToSendMessages: CONTEXT IS NOT MOUNTED');
_sendButtonState = ButtonState.enabled;
return;
}
@@ -46,13 +46,12 @@ class AttachmentTextDetector {
"مستند",
"ملف",
"تقرير",
"ملف",
"إرفاق",
],
};
/// Cached RegExp for the default case (when no include list is provided).
static RegExp? _cachedDefaultRegExp;
/// Cached RegExp Pattern for the default case (when no include list is provided).
static String? _cachedDefaultPattern;
/// Threshold to switch from Sync to Async execution.
///
/// Value: 20,000 characters (approx. 4-5 pages of text).
@@ -118,7 +117,7 @@ class AttachmentTextDetector {
/// Builds the Regex pattern.
/// Merges [defaultKeywords] with [includeList] to create the search scope.
static RegExp _buildRegExp(List<String> additionalKeywords) {
static String _generatePatternString(List<String> additionalKeywords) {
// 1. Get all default keywords
final defaultKeywords = _keywordsByLang.values.expand((l) => l).toList();
@@ -133,11 +132,16 @@ class AttachmentTextDetector {
// (?![\p{L}]) ensures we don't match substrings inside other words (e.g., "filetage").
final pattern = allKeywords.map(RegExp.escape).join('|');
return RegExp(
'($pattern)(?![\\p{L}])',
unicode: true,
caseSensitive: false,
);
return'($pattern)(?![\\p{L}])';
}
static String _getPattern(List<String> includeList) {
if (includeList.isNotEmpty) {
return _generatePatternString(includeList);
}
_cachedDefaultPattern ??= _generatePatternString([]);
return _cachedDefaultPattern!;
}
/// The core logic function.
@@ -148,16 +152,11 @@ class AttachmentTextDetector {
final text = params.text;
if (text.isEmpty) return [];
RegExp regex;
// Use cached Regex if there are no custom keywords.
if (params.includeList.isEmpty) {
_cachedDefaultRegExp ??= _buildRegExp([]);
regex = _cachedDefaultRegExp!;
} else {
// Rebuild Regex if Include List is present.
regex = _buildRegExp(params.includeList);
}
final regex = RegExp(
params.regexPattern,
unicode: true,
caseSensitive: false,
);
final matches = regex.allMatches(text);
final result = <String>{};
@@ -199,10 +198,12 @@ class AttachmentTextDetector {
filters.add(ExcludeListFilter(excludeList));
}
final patternString = _getPattern(includeList);
final params = DetectionParams(
text: text,
includeList: includeList, // Passed to build the Regex
filters: filters, // Passed to filter the matches
regexPattern: patternString,
filters: filters,
);
// Decision: Run Sync (fast) or Async (safe)
@@ -4,12 +4,12 @@ import 'package:tmail_ui_user/features/composer/presentation/manager/keyword_fil
/// Isolates cannot share memory, so we package everything here.
class DetectionParams {
final String text;
final List<String> includeList; // Keywords to ADD to the search.
final String regexPattern;
final List<KeywordFilter> filters; // Filters to BLOCK specific matches.
DetectionParams({
required this.text,
required this.includeList,
required this.regexPattern,
required this.filters,
});
}
@@ -23,6 +23,10 @@ class ExcludeListFilter with TokenExtractionMixin implements KeywordFilter {
final cleanToken = lowerToken.replaceAll(RegExp(r'[^\w\s]+$'), '');
if (_excludes.contains(cleanToken)) return false;
// Check 3: Block even if it has leading punctuation (e.g., "(file")
final fullyCleanToken = cleanToken.replaceAll(RegExp(r'^[^\w\s]+'), '');
if (_excludes.contains(fullyCleanToken)) return false;
return true;
}
}