Skip to content

Commit

Permalink
[Application] Added new bloc events
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolfteam committed Jan 16, 2022
1 parent e85fa20 commit 8188a80
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 30 deletions.
241 changes: 211 additions & 30 deletions lib/application/custom_build/custom_build_bloc.dart
@@ -1,6 +1,8 @@
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:shiori/domain/app_constants.dart';
import 'package:shiori/domain/enums/enums.dart';
import 'package:shiori/domain/extensions/string_extensions.dart';
import 'package:shiori/domain/models/models.dart';
import 'package:shiori/domain/services/data_service.dart';
import 'package:shiori/domain/services/genshin_service.dart';
Expand All @@ -13,18 +15,26 @@ class CustomBuildBloc extends Bloc<CustomBuildEvent, CustomBuildState> {
final GenshinService _genshinService;
final DataService _dataService;

static int maxTitleLength = 40;
static int maxNoteLength = 100;
static int maxNumberOfNotes = 5;
static List<CharacterSkillType> validSkillTypes = [
CharacterSkillType.normalAttack,
CharacterSkillType.elementalSkill,
CharacterSkillType.elementalBurst,
];
static List<CharacterSkillType> excludedSkillTypes = [CharacterSkillType.others];

CustomBuildBloc(this._genshinService, this._dataService) : super(const CustomBuildState.loading()) {
on<CustomBuildEvent>(_handleEvent);
}

Future<void> _handleEvent(CustomBuildEvent event, Emitter<CustomBuildState> emit) async {
//todo: SHOULD I TRHOW ON INVALID REQUEST ?
final s = await event.map(
load: (e) async => _init(e.key),
characterChanged: (e) async => state.maybeMap(
loaded: (state) {
final newCharacter = _genshinService.getCharacterForCard(e.newKey);
return state.copyWith.call(character: newCharacter);
},
loaded: (state) => _characterChanged(e, state),
orElse: () => state,
),
titleChanged: (e) async => state.maybeMap(
Expand All @@ -44,41 +54,59 @@ class CustomBuildBloc extends Bloc<CustomBuildEvent, CustomBuildState> {
orElse: () => state,
),
addWeapon: (e) async => state.maybeMap(
loaded: (state) {
//TODO: CHECK FOR REPEATED
if (state.weapons.any((el) => el.key == e.key)) {
throw Exception('Weapons cannot be repeated');
}
final weapon = _genshinService.getWeaponForCard(e.key);
final weapons = [...state.weapons, weapon];
return state.copyWith.call(weapons: weapons);
},
orElse: () => state,
),
deleteWeapon: (e) async => state,
loaded: (state) => _addWeapon(e, state),
orElse: () => state,
),
deleteWeapon: (e) async => state.maybeMap(
loaded: (state) => _deleteWeapon(e, state),
orElse: () => state,
),
weaponOrderChanged: (e) async => state,
addArtifact: (e) async => state.maybeMap(
loaded: (state) {
//TODO: CHECK FOR REPEATED
if (state.artifacts.any((el) => el.key == e.key && el.type == e.type)) {
throw Exception('Artifact types cannot be repeated');
}
final fullArtifact = _genshinService.getArtifact(e.key);
final translation = _genshinService.getArtifactTranslation(e.key);
final img = _genshinService.getArtifactRelatedPart(fullArtifact.fullImagePath, fullArtifact.image, translation.bonus.length, e.type);
final artifacts = [
...state.artifacts,
CustomBuildArtifactModel(type: e.type, image: img, key: e.key, rarity: fullArtifact.maxRarity, statType: e.statType),
]..sort((x, y) => x.type.index.compareTo(y.type.index));
return state.copyWith.call(artifacts: artifacts);
},
loaded: (state) => _addArtifact(e, state),
orElse: () => state,
),
saveChanges: (e) async => state.maybeMap(
loaded: (state) => _saveChanges(state),
orElse: () async => state,
),
reset: (e) async => state,
addNote: (e) async => state.maybeMap(
loaded: (state) => _addNote(e, state),
orElse: () => state,
),
deleteNote: (e) async => state.maybeMap(
loaded: (state) => _deleteNote(e, state),
orElse: () => state,
),
deleteWeapons: (e) async => state.maybeMap(
loaded: (state) => state.copyWith.call(weapons: []),
orElse: () => state,
),
deleteArtifacts: (e) async => state.maybeMap(
loaded: (state) => state.copyWith.call(artifacts: [], subStatsSummary: []),
orElse: () => state,
),
deleteSkillPriority: (e) async => state.maybeMap(
loaded: (state) => _deleteSkillPriority(e, state),
orElse: () => state,
),
addSkillPriority: (e) async => state.maybeMap(
loaded: (state) => _addSkillPriority(e, state),
orElse: () => state,
),
isRecommendedChanged: (e) async => state.maybeMap(
loaded: (state) => state.copyWith.call(isRecommended: e.newValue),
orElse: () => state,
),
addArtifactSubStats: (e) async => state.maybeMap(
loaded: (state) => _addArtifactSubStats(e, state),
orElse: () => state,
),
deleteArtifact: (e) async => state.maybeMap(
loaded: (state) => _deleteArtifact(e, state),
orElse: () => state,
),
);

emit(s);
Expand All @@ -93,9 +121,13 @@ class CustomBuildBloc extends Bloc<CustomBuildEvent, CustomBuildState> {
type: build.type,
subType: build.subType,
showOnCharacterDetail: build.showOnCharacterDetail,
isRecommended: build.isRecommended,
character: build.character,
weapons: build.weapons,
notes: build.notes,
skillPriorities: build.skillPriorities,
artifacts: build.artifacts..sort((x, y) => x.type.index.compareTo(y.type.index)),
subStatsSummary: _generateSubStatSummary(build.artifacts),
);
}

Expand All @@ -105,12 +137,140 @@ class CustomBuildBloc extends Bloc<CustomBuildEvent, CustomBuildState> {
type: CharacterRoleType.dps,
subType: CharacterRoleSubType.none,
showOnCharacterDetail: true,
isRecommended: false,
character: character,
notes: [],
weapons: [],
artifacts: []..sort((x, y) => x.type.index.compareTo(y.type.index)),
skillPriorities: [],
subStatsSummary: [],
);
}

CustomBuildState _addNote(_AddNote e, _LoadedState state) {
if (e.note.isNullEmptyOrWhitespace || state.notes.length >= maxNumberOfNotes) {
return state;
}
final newNote = CustomBuildNoteModel(index: state.notes.length, note: e.note);
return state.copyWith.call(notes: [...state.notes, newNote]);
}

CustomBuildState _deleteNote(_DeleteNote e, _LoadedState state) {
if (e.index < 0 || e.index >= state.notes.length) {
return state;
}

final notes = [...state.notes];
notes.removeAt(e.index);
return state.copyWith.call(notes: notes);
}

CustomBuildState _addSkillPriority(_AddSkillPriority e, _LoadedState state) {
if (state.skillPriorities.contains(e.type) || !validSkillTypes.contains(e.type)) {
return state;
}
return state.copyWith.call(skillPriorities: [...state.skillPriorities, e.type]);
}

CustomBuildState _deleteSkillPriority(_DeleteSkillPriority e, _LoadedState state) {
if (e.index < 0 || e.index >= state.skillPriorities.length) {
return state;
}

final skillPriorities = [...state.skillPriorities];
skillPriorities.removeAt(e.index);
return state.copyWith.call(skillPriorities: skillPriorities);
}

CustomBuildState _characterChanged(_CharacterChanged e, _LoadedState state) {
if (state.character.key == e.newKey) {
return state;
}
final newCharacter = _genshinService.getCharacterForCard(e.newKey);
return state.copyWith.call(character: newCharacter);
}

CustomBuildState _addWeapon(_AddWeapon e, _LoadedState state) {
if (state.weapons.any((el) => el.key == e.key)) {
throw Exception('Weapons cannot be repeated');
}
final weapon = _genshinService.getWeaponForCard(e.key);
final weapons = [...state.weapons, weapon];
return state.copyWith.call(weapons: weapons);
}

CustomBuildState _deleteWeapon(_DeleteWeapon e, _LoadedState state) {
if (!state.weapons.any((el) => el.key == e.key)) {
return state;
}

final updated = [...state.weapons];
updated.removeWhere((el) => el.key == e.key);
return state.copyWith.call(weapons: updated);
}

CustomBuildState _addArtifact(_AddArtifact e, _LoadedState state) {
final fullArtifact = _genshinService.getArtifact(e.key);
final translation = _genshinService.getArtifactTranslation(e.key);
final img = _genshinService.getArtifactRelatedPart(fullArtifact.fullImagePath, fullArtifact.image, translation.bonus.length, e.type);

final updatedArtifacts = [...state.artifacts];
final old = state.artifacts.firstWhereOrNull((el) => el.type == e.type);
if (old != null) {
updatedArtifacts.removeWhere((el) => el.type == e.type);
final updatedSubStats = [...old.subStats]..removeWhere((el) => el == e.statType);
final updated = old.copyWith.call(
type: e.type,
image: img,
key: e.key,
rarity: fullArtifact.maxRarity,
statType: e.statType,
subStats: updatedSubStats,
);
updatedArtifacts.add(updated);
} else {
final newOne = CustomBuildArtifactModel(
type: e.type,
image: img,
key: e.key,
rarity: fullArtifact.maxRarity,
statType: e.statType,
subStats: [],
);
updatedArtifacts.add(newOne);
}
return state.copyWith.call(artifacts: updatedArtifacts..sort((x, y) => x.type.index.compareTo(y.type.index)));
}

CustomBuildState _addArtifactSubStats(_AddArtifactSubStats e, _LoadedState state) {
final artifact = state.artifacts.firstWhereOrNull((el) => el.type == e.type);
if (artifact == null) {
return state;
}

final possibleSubStats = getArtifactPossibleSubStats(artifact.statType);
if (e.subStats.any((s) => !possibleSubStats.contains(s))) {
throw Exception('One of the provided sub-stats is not valid');
}

final index = state.artifacts.indexOf(artifact);
final updated = artifact.copyWith.call(subStats: e.subStats);
final artifacts = [...state.artifacts];
artifacts.removeAt(index);
artifacts.insert(index, updated);
return state.copyWith.call(artifacts: artifacts, subStatsSummary: _generateSubStatSummary(artifacts));
}

CustomBuildState _deleteArtifact(_DeleteArtifact e, _LoadedState state) {
if (!state.artifacts.any((el) => el.type == e.type)) {
return state;
}

final updated = [...state.artifacts];
updated.removeWhere((el) => el.type == e.type);
return state.copyWith.call(artifacts: updated, subStatsSummary: _generateSubStatSummary(updated));
}

Future<CustomBuildState> _saveChanges(_LoadedState state) async {
if (state.key != null) {
await _dataService.customBuilds.updateCustomBuild(
Expand All @@ -119,6 +279,8 @@ class CustomBuildBloc extends Bloc<CustomBuildEvent, CustomBuildState> {
state.type,
state.subType,
state.showOnCharacterDetail,
state.isRecommended,
state.notes,
state.weapons.map((e) => e.key).toList(),
[],
[],
Expand All @@ -132,6 +294,8 @@ class CustomBuildBloc extends Bloc<CustomBuildEvent, CustomBuildState> {
state.type,
state.subType,
state.showOnCharacterDetail,
state.isRecommended,
state.notes,
state.weapons.map((e) => e.key).toList(),
//TODO: THIS
[],
Expand All @@ -140,4 +304,21 @@ class CustomBuildBloc extends Bloc<CustomBuildEvent, CustomBuildState> {

return _init(build.key);
}

List<StatType> _generateSubStatSummary(List<CustomBuildArtifactModel> artifacts) {
final weightMap = <StatType, int>{};

for (final artifact in artifacts) {
int weight = artifact.subStats.length;
for (var i = 0; i < artifact.subStats.length; i++) {
final subStat = artifact.subStats[i];
final ifAbsent = weightMap.containsKey(subStat) ? i : weight;
weightMap.update(subStat, (value) => value + weight, ifAbsent: () => ifAbsent);
weight--;
}
}

final sorted = weightMap.entries.sorted((a, b) => b.value.compareTo(a.value));
return sorted.map((e) => e.key).toList();
}
}
22 changes: 22 additions & 0 deletions lib/application/custom_build/custom_build_event.dart
Expand Up @@ -14,6 +14,8 @@ class CustomBuildEvent with _$CustomBuildEvent {

const factory CustomBuildEvent.showOnCharacterDetailChanged({required bool newValue}) = _ShowOnCharacterDetailChanged;

const factory CustomBuildEvent.isRecommendedChanged({required bool newValue}) = _IsRecommendedChanged;

const factory CustomBuildEvent.addWeapon({required String key}) = _AddWeapon;

const factory CustomBuildEvent.deleteWeapon({required String key}) = _DeleteWeapon;
Expand All @@ -26,8 +28,28 @@ class CustomBuildEvent with _$CustomBuildEvent {
required StatType statType,
}) = _AddArtifact;

const factory CustomBuildEvent.addArtifactSubStats({
required ArtifactType type,
required List<StatType> subStats,
}) = _AddArtifactSubStats;

const factory CustomBuildEvent.deleteArtifact({required ArtifactType type}) = _DeleteArtifact;

const factory CustomBuildEvent.addNote({required String note}) = _AddNote;

const factory CustomBuildEvent.deleteNote({required int index}) = _DeleteNote;

const factory CustomBuildEvent.deleteWeapons() = _DeleteWeapons;

const factory CustomBuildEvent.deleteArtifacts() = _DeleteArtifacts;

const factory CustomBuildEvent.addSkillPriority({required CharacterSkillType type}) = _AddSkillPriority;

const factory CustomBuildEvent.deleteSkillPriority({required int index}) = _DeleteSkillPriority;

const factory CustomBuildEvent.saveChanges() = _SaveChanges;

//TODO: DELETE THIS?
const factory CustomBuildEvent.reset() = _Reset;

//TODO: SHARE, SBUSTATS, TALENETS, ARTIFACT'S PIECE BONUS
Expand Down
4 changes: 4 additions & 0 deletions lib/application/custom_build/custom_build_state.dart
Expand Up @@ -10,8 +10,12 @@ class CustomBuildState with _$CustomBuildState {
required CharacterRoleType type,
required CharacterRoleSubType subType,
required bool showOnCharacterDetail,
required bool isRecommended,
required CharacterCardModel character,
required List<WeaponCardModel> weapons,
required List<CustomBuildArtifactModel> artifacts,
required List<CustomBuildNoteModel> notes,
required List<CharacterSkillType> skillPriorities,
required List<StatType> subStatsSummary,
}) = _LoadedState;
}

0 comments on commit 8188a80

Please sign in to comment.