Skip to content

Commit

Permalink
implement sharing of objects
Browse files Browse the repository at this point in the history
* refactor routing to be more reliable and interactive
* refactor edit dialogs for all objects to reuse more
shared code
* add new action buttons to all object views
* add file sharing of all current objects (except subItems)
* add import dialogs for all current objects (except subItems)
* add handler for receiving sharing intents on android
* add first test code for an api connection (not usable yet)
  • Loading branch information
HJFinch committed Apr 8, 2023
1 parent d3d6674 commit b3dd8bd
Show file tree
Hide file tree
Showing 100 changed files with 4,110 additions and 1,920 deletions.
5 changes: 5 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/cypher-sheet-object" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Expand Down
1 change: 1 addition & 0 deletions icons/system_update_alt.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion lib/components/ability.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:cypher_sheet/components/dialog.dart';
import 'package:cypher_sheet/extensions/pool.dart';
import 'package:cypher_sheet/state/providers/abilities.dart';
import 'package:cypher_sheet/views/dialogs/view_ability.dart';
import 'package:cypher_sheet/views/dialogs/object/ability/view.dart';
import 'package:flutter/material.dart';
import 'package:cypher_sheet/components/box.dart';
import 'package:cypher_sheet/components/icon.dart';
Expand Down
1 change: 1 addition & 0 deletions lib/components/appbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class AppBar extends StatelessWidget {
child: child,
),
),
automaticallyImplyLeading: false,
);
}
}
4 changes: 2 additions & 2 deletions lib/components/cypher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'package:cypher_sheet/components/dialog.dart';
import 'package:cypher_sheet/extensions/cypher.dart';
import 'package:cypher_sheet/proto/character.pb.dart';
import 'package:cypher_sheet/state/providers/cyphers.dart';
import 'package:cypher_sheet/views/dialogs/view_artifact.dart';
import 'package:cypher_sheet/views/dialogs/view_cypher.dart';
import 'package:cypher_sheet/views/dialogs/object/artifact/view.dart';
import 'package:cypher_sheet/views/dialogs/object/cypher/view.dart';
import 'package:flutter/material.dart';
import 'package:cypher_sheet/components/box.dart';
import 'package:cypher_sheet/components/icon.dart';
Expand Down
2 changes: 1 addition & 1 deletion lib/components/equipment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:cypher_sheet/components/dialog.dart';
import 'package:cypher_sheet/extensions/item.dart';
import 'package:cypher_sheet/state/providers/character.dart';
import 'package:cypher_sheet/state/providers/items.dart';
import 'package:cypher_sheet/views/dialogs/view_item.dart';
import 'package:cypher_sheet/views/dialogs/object/item/view.dart';
import 'package:flutter/material.dart';
import 'package:cypher_sheet/components/box.dart';
import 'package:cypher_sheet/components/cypher.dart';
Expand Down
3 changes: 3 additions & 0 deletions lib/components/icons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ enum AppIcons {
edit,
deleteForever,
share,
import,
devMode;

@override
Expand Down Expand Up @@ -145,6 +146,8 @@ String iconName(AppIcons from) {
return "delete_forever";
case AppIcons.share:
return "share";
case AppIcons.import:
return "system_update_alt";
case AppIcons.devMode:
return "developer_mode";
}
Expand Down
2 changes: 2 additions & 0 deletions lib/components/meta.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:cypher_sheet/main.dart';
import 'package:cypher_sheet/state/providers/character.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand Down Expand Up @@ -44,6 +45,7 @@ class CharacterMeta extends ConsumerWidget {
child: AppBox(
onTap: () {
ref.invalidate(characterListProvider);
Navigator.of(context).pushReplacementNamed(routeCharacters);
ref.read(characterProvider.notifier).reset();
},
flat: true,
Expand Down
6 changes: 3 additions & 3 deletions lib/components/note.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import 'package:cypher_sheet/components/icons.dart';
import 'package:cypher_sheet/extensions/note.dart';
import 'package:cypher_sheet/proto/character.pb.dart';
import 'package:cypher_sheet/state/providers/notes.dart';
import 'package:cypher_sheet/views/dialogs/create_note.dart';
import 'package:cypher_sheet/views/dialogs/view_note.dart';
import 'package:cypher_sheet/views/dialogs/object/note/update.dart';
import 'package:cypher_sheet/views/dialogs/object/note/view.dart';
import 'package:flutter/material.dart';
import 'package:cypher_sheet/components/box.dart';
import 'package:cypher_sheet/components/icon.dart';
Expand Down Expand Up @@ -51,7 +51,7 @@ class NoteListItem extends ConsumerWidget {
padding: 4,
size: 24,
onTap: () {
showAppDialog(context, CreateNote.fromState(note));
showAppDialog(context, UpdateNote(note));
}),
],
),
Expand Down
23 changes: 23 additions & 0 deletions lib/components/share.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:cypher_sheet/components/icon.dart';
import 'package:cypher_sheet/components/icons.dart';
import 'package:flutter/material.dart';
import 'package:share_plus/share_plus.dart';

class ShareObjectButton extends StatelessWidget {
const ShareObjectButton(this.getFile, {super.key});

final Future<XFile> Function() getFile;

@override
Widget build(BuildContext context) {
return SVGBox(
padding: 12,
onTap: (() async {
Share.shareXFiles(subject: "Shared from Cypher Sheet", [
await getFile(),
]);
}),
icon: AppIcons.share,
);
}
}
2 changes: 1 addition & 1 deletion lib/components/skill.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:cypher_sheet/components/dialog.dart';
import 'package:cypher_sheet/extensions/pool.dart';
import 'package:cypher_sheet/extensions/skill.dart';
import 'package:cypher_sheet/state/providers/skills.dart';
import 'package:cypher_sheet/views/dialogs/view_skill.dart';
import 'package:cypher_sheet/views/dialogs/object/skill/view.dart';
import 'package:flutter/material.dart';
import 'package:cypher_sheet/components/box.dart';
import 'package:cypher_sheet/components/icon.dart';
Expand Down
11 changes: 11 additions & 0 deletions lib/extensions/ability.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:cypher_sheet/proto/character.pb.dart';

extension ShareHelper on Ability {
SharedObject share() {
return SharedObject(
uuid: uuid,
name: name,
ability: this,
);
}
}
11 changes: 11 additions & 0 deletions lib/extensions/artifact.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:cypher_sheet/proto/character.pb.dart';

extension ShareHelper on Artifact {
SharedObject share() {
return SharedObject(
uuid: uuid,
name: name,
artifact: this,
);
}
}
10 changes: 10 additions & 0 deletions lib/extensions/cypher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,13 @@ extension CypherTypeHelper on CypherType {
}
}
}

extension ShareHelper on Cypher {
SharedObject share() {
return SharedObject(
uuid: uuid,
name: name,
cypher: this,
);
}
}
195 changes: 195 additions & 0 deletions lib/extensions/editable.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import 'package:cypher_sheet/components/equipment.dart';
import 'package:flutter/widgets.dart';
import 'package:protobuf/protobuf.dart';

Map<String, TextEditingController> getEditableTextFieldsFrom(
GeneratedMessage msg) {
final Map<String, TextEditingController> fields = {};
mergeEditableTextFieldsFrom(msg, fields);
return fields;
}

void mergeEditableTextFieldsFrom(
GeneratedMessage msg, Map<String, TextEditingController> into) {
// TODO: is byIndex more efficient but still safe?
for (var fieldName in msg.info_.byName.keys) {
final fieldValue = msg.info_.byName[fieldName];
if (fieldValue == null) {
assert(fieldValue != null);
continue;
}

if (fieldValue.type != PbFieldType.OS) {
continue;
}
into[fieldName] =
TextEditingController(text: msg.getField(fieldValue.tagNumber));
}
}

// Must only be called once per message as it disposes all TextEditingControllers.
Function(String key, TextEditingController value) textFieldsUpdater(
GeneratedMessage msg) {
return (key, value) {
final tagNumber = msg.getTagNumber(key);
if (tagNumber == null) {
assert(tagNumber != null);
return;
}
msg.setField(tagNumber, value.value.text);
value.dispose();
};
}

Map<String, TextEditingController> getEditableDoubleFieldsFrom(
GeneratedMessage msg) {
final Map<String, TextEditingController> fields = {};
mergeEditableDoubleFieldsFrom(msg, fields);
return fields;
}

void mergeEditableDoubleFieldsFrom(
GeneratedMessage msg, Map<String, TextEditingController> into) {
// TODO: is byIndex more efficient but still safe?
for (var fieldName in msg.info_.byName.keys) {
final fieldValue = msg.info_.byName[fieldName];
if (fieldValue == null) {
assert(fieldValue != null);
continue;
}

if (fieldValue.type != PbFieldType.OD) {
continue;
}
into[fieldName] = TextEditingController(
text:
removeZeroDecimals(msg.getField(fieldValue.tagNumber)).toString());
}
}

Function(String key, TextEditingController value) doubleFieldsUpdater(
GeneratedMessage msg) {
return (key, value) {
final tagNumber = msg.getTagNumber(key);
if (tagNumber == null) {
assert(tagNumber != null);
return;
}
final parsed = double.tryParse(value.value.text);
if (value.value.text.isNotEmpty && parsed != null) {
msg.setField(tagNumber, parsed);
}
value.dispose();
};
}

Map<String, bool> getEditableBoolFieldsFrom(GeneratedMessage msg) {
final Map<String, bool> fields = {};
mergeEditableBoolFieldsFrom(msg, fields);
return fields;
}

void mergeEditableBoolFieldsFrom(GeneratedMessage msg, Map<String, bool> into) {
// TODO: is byIndex more efficient but still safe?
for (var fieldName in msg.info_.byName.keys) {
final fieldValue = msg.info_.byName[fieldName];
if (fieldValue == null) {
assert(fieldValue != null);
continue;
}

if (fieldValue.type != PbFieldType.OB) {
continue;
}
into[fieldName] = msg.getField(fieldValue.tagNumber);
}
}

Function(String key, bool value) boolFieldsUpdater(GeneratedMessage msg) {
return (key, value) {
final tagNumber = msg.getTagNumber(key);
if (tagNumber == null) {
assert(tagNumber != null);
return;
}
msg.setField(tagNumber, value);
};
}

Map<String, TextEditingController> getEditableIntFieldsFrom(
GeneratedMessage msg) {
final Map<String, TextEditingController> fields = {};
mergeEditableDoubleFieldsFrom(msg, fields);
return fields;
}

void mergeEditableIntFieldsFrom(
GeneratedMessage msg, Map<String, TextEditingController> into) {
// TODO: is byIndex more efficient but still safe?
for (var fieldName in msg.info_.byName.keys) {
final fieldValue = msg.info_.byName[fieldName];
if (fieldValue == null) {
assert(fieldValue != null);
continue;
}

if (fieldValue.type != PbFieldType.OD) {
continue;
}
into[fieldName] = TextEditingController(
text: msg.getField(fieldValue.tagNumber).toString());
}
}

Function(String key, TextEditingController value) intFieldsUpdater(
GeneratedMessage msg) {
return (key, value) {
final tagNumber = msg.getTagNumber(key);
if (tagNumber == null) {
assert(tagNumber != null);
return;
}
final parsed = int.tryParse(value.value.text);
if (value.value.text.isNotEmpty && parsed != null) {
msg.setField(tagNumber, parsed);
}
value.dispose();
};
}

void mergeEditableFieldsFrom(
GeneratedMessage msg, {
required Map<String, TextEditingController> strings,
required Map<String, bool> bools,
required Map<String, TextEditingController> doubles,
required Map<String, TextEditingController> ints,
}) {
// TODO: is byIndex more efficient but still safe?
for (var fieldName in msg.info_.byName.keys) {
final fieldValue = msg.info_.byName[fieldName];
if (fieldValue == null) {
assert(fieldValue != null);
continue;
}

switch (fieldValue.type) {
case PbFieldType.OS:
strings[fieldName] =
TextEditingController(text: msg.getField(fieldValue.tagNumber));
break;
case PbFieldType.OB:
bools[fieldName] = msg.getField(fieldValue.tagNumber);
break;
case PbFieldType.OD:
doubles[fieldName] = TextEditingController(
text: removeZeroDecimals(msg.getField(fieldValue.tagNumber))
.toString());
break;
case PbFieldType.O3:
ints[fieldName] = TextEditingController(
text: msg.getField(fieldValue.tagNumber).toString());
break;
default:
}
}
}
11 changes: 11 additions & 0 deletions lib/extensions/item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,14 @@ extension Label on ItemType {
return name[0].toUpperCase() + name.substring(1);
}
}

extension ShareHelper on Item {
SharedObject share() {
// TODO: figure out how to handle subItems.
return SharedObject(
uuid: path.self,
name: name,
item: this,
);
}
}
10 changes: 10 additions & 0 deletions lib/extensions/note.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@ extension Label on NoteType {
return name[0].toUpperCase() + name.substring(1);
}
}

extension ShareHelper on Note {
SharedObject share() {
return SharedObject(
uuid: uuid,
name: title,
note: this,
);
}
}
Loading

0 comments on commit b3dd8bd

Please sign in to comment.