|
|
|
// Copyright 2021 The Flutter team. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'dart:developer';
|
|
|
|
import 'dart:io';
|
|
|
|
import 'dart:typed_data';
|
|
|
|
|
|
|
|
import 'package:file_selector/file_selector.dart' as file_selector;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'package:json2yaml/json2yaml.dart';
|
|
|
|
import 'package:linting_tool/model/profile.dart';
|
|
|
|
import 'package:linting_tool/model/rule.dart';
|
|
|
|
import 'package:linting_tool/repository/hive_service.dart';
|
|
|
|
import 'package:linting_tool/repository/repository.dart';
|
|
|
|
import 'package:yaml/yaml.dart';
|
|
|
|
|
|
|
|
const _boxName = 'rules_profile';
|
|
|
|
|
|
|
|
class ProfilesStore extends ChangeNotifier {
|
|
|
|
late final Repository repository;
|
|
|
|
ProfilesStore(http.Client httpClient) {
|
|
|
|
repository = Repository(httpClient);
|
|
|
|
fetchSavedProfiles();
|
|
|
|
}
|
|
|
|
bool _isLoading = true;
|
|
|
|
|
|
|
|
bool get isLoading => _isLoading;
|
|
|
|
|
|
|
|
List<RulesProfile> _savedProfiles = [];
|
|
|
|
|
|
|
|
List<RulesProfile> get savedProfiles => _savedProfiles;
|
|
|
|
|
|
|
|
String? _error;
|
|
|
|
|
|
|
|
String? get error => _error;
|
|
|
|
|
|
|
|
Future<void> fetchSavedProfiles() async {
|
|
|
|
if (!_isLoading) _isLoading = true;
|
|
|
|
notifyListeners();
|
|
|
|
try {
|
|
|
|
var profiles = await HiveService.getBoxes<RulesProfile>(_boxName);
|
|
|
|
_savedProfiles = profiles;
|
|
|
|
} on Exception catch (e) {
|
|
|
|
log(e.toString());
|
|
|
|
}
|
|
|
|
_isLoading = false;
|
|
|
|
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> addToNewProfile(RulesProfile profile) async {
|
|
|
|
await HiveService.addBox<RulesProfile>(profile, _boxName);
|
|
|
|
|
|
|
|
await Future.delayed(const Duration(milliseconds: 100), () async {
|
|
|
|
await fetchSavedProfiles();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> addToExistingProfile(RulesProfile profile, Rule rule) async {
|
|
|
|
// TODO(abd99): Consider refactoring to LinkedHashSet/SplayTreeSet to avoid
|
|
|
|
// duplication automatically.
|
|
|
|
// ref: https://github.com/flutter/samples/pull/870#discussion_r685666792
|
|
|
|
var rules = profile.rules;
|
|
|
|
if (!rules.contains(rule)) {
|
|
|
|
rules.add(rule);
|
|
|
|
}
|
|
|
|
|
|
|
|
RulesProfile newProfile = RulesProfile(name: profile.name, rules: rules);
|
|
|
|
|
|
|
|
await HiveService.updateBox<RulesProfile>(profile, newProfile, _boxName);
|
|
|
|
|
|
|
|
await Future.delayed(const Duration(milliseconds: 100), () async {
|
|
|
|
await fetchSavedProfiles();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> updateProfile(
|
|
|
|
RulesProfile oldProfile, RulesProfile newProfile) async {
|
|
|
|
await HiveService.updateBox<RulesProfile>(oldProfile, newProfile, _boxName);
|
|
|
|
|
|
|
|
await Future.delayed(const Duration(milliseconds: 100), () async {
|
|
|
|
await fetchSavedProfiles();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> removeRuleFromProfile(RulesProfile profile, Rule rule) async {
|
|
|
|
var newProfile =
|
|
|
|
RulesProfile(name: profile.name, rules: profile.rules..remove(rule));
|
|
|
|
await updateProfile(profile, newProfile);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> deleteProfile(RulesProfile profile) async {
|
|
|
|
await HiveService.deleteBox<RulesProfile>(profile, _boxName);
|
|
|
|
|
|
|
|
await Future.delayed(const Duration(milliseconds: 100), () async {
|
|
|
|
await fetchSavedProfiles();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> exportProfileFile(
|
|
|
|
RulesProfile profile, {
|
|
|
|
RulesStyle rulesStyle = RulesStyle.booleanMap,
|
|
|
|
}) async {
|
|
|
|
_isLoading = true;
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
var resultSaved = false;
|
|
|
|
|
|
|
|
try {
|
|
|
|
var templateFileData = await repository.getTemplateFile();
|
|
|
|
|
|
|
|
/// Fetch formatted data to create new YamlFile.
|
|
|
|
String newYamlFile =
|
|
|
|
_prepareYamlFile(profile, templateFileData, rulesStyle);
|
|
|
|
|
|
|
|
resultSaved = await _saveFileToDisk(newYamlFile);
|
|
|
|
} on SocketException catch (e) {
|
|
|
|
log(e.toString());
|
|
|
|
_error = 'Check internet connection.';
|
|
|
|
resultSaved = false;
|
|
|
|
} on Exception catch (e) {
|
|
|
|
log(e.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLoading = false;
|
|
|
|
notifyListeners();
|
|
|
|
|
|
|
|
return resultSaved;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> _saveFileToDisk(String newYamlFile) async {
|
|
|
|
const name = 'analysis_options.yaml';
|
|
|
|
|
|
|
|
/// Get file path using file picker.
|
|
|
|
var savePath = await file_selector.getSavePath(
|
|
|
|
suggestedName: name,
|
|
|
|
);
|
|
|
|
|
|
|
|
final data = Uint8List.fromList(newYamlFile.codeUnits);
|
|
|
|
final file = file_selector.XFile.fromData(data, name: name);
|
|
|
|
|
|
|
|
/// Save file to disk if path was provided.
|
|
|
|
if (savePath != null) {
|
|
|
|
file.saveTo(savePath);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
var errorMessage = 'File path not found.';
|
|
|
|
_error = errorMessage;
|
|
|
|
throw Exception(errorMessage);
|
|
|
|
}
|
|
|
|
|
|
|
|
String _prepareYamlFile(
|
|
|
|
RulesProfile profile, YamlMap templateFile, RulesStyle rulesStyle) {
|
|
|
|
var rules = profile.rules.map((e) => e.name).toList();
|
|
|
|
|
|
|
|
var rulesData =
|
|
|
|
json.decode(json.encode(templateFile)) as Map<String, dynamic>;
|
|
|
|
|
|
|
|
/// Add rules to existing template according to formatting style.
|
|
|
|
if (rulesStyle == RulesStyle.booleanMap) {
|
|
|
|
var rulesMap = Map.fromEntries(
|
|
|
|
rules.map(
|
|
|
|
(e) => MapEntry(e, true),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
rulesData.update('linter', (dynamic value) => {'rules': rulesMap});
|
|
|
|
} else {
|
|
|
|
rulesData.update('linter', (dynamic value) => {'rules': rules});
|
|
|
|
}
|
|
|
|
|
|
|
|
return json2yaml(rulesData, yamlStyle: YamlStyle.pubspecYaml);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Formatting style for rules.
|
|
|
|
enum RulesStyle {
|
|
|
|
list,
|
|
|
|
booleanMap,
|
|
|
|
}
|