// Model for a resource (Link / Link - Directory)
import 'package:logging/logging.dart';
class Resource {
Resource(this.linkId, this.linkDirectoryId, this.displayName,
[this.authorNameFirst,
this.authorNameLast,
this.createdTime,
this.linkUrl,
this.mimeType,
this.storedFilename]);
int linkId;
int linkDirectoryId;
String displayName;
String authorNameLast;
String authorNameFirst;
String createdTime;
String linkUrl;
String mimeType;
String storedFilename;
static Resource createResourceFromLink(dynamic link) {
final Resource _itemResource = Resource(
link['linkId'],
link['linkDirectroyId'],
link['displayName'],
link['authorNameFirst'],
link['authorNameLast'],
link['createdTime'],
link['linkUrl'],
link['mimeType'],
link['storedFilename'],
);
return _itemResource;
}
}
// Define what will be our array (or arrays of arrays, etc) of resources..
class ResourceEntry {
ResourceEntry(this.directoryId, this.resource,
{this.children, this.isFolder = false});
final int directoryId;
final Resource resource;
bool isFolder;
List<ResourceEntry> children;
}
// OK -- now create the utility class to hold everything ..
class ResourceTree {
ResourceTree() {
resourceTree = <ResourceEntry>[];
}
final Logger logger = Logger('ResourceTree');
List<ResourceEntry> resourceTree;
void clear() => resourceTree.clear();
int length() => resourceTree.length;
ResourceEntry get(int pos) => resourceTree[pos];
ResourceEntry operator [](int pos) => resourceTree[pos];
final List<int> _directoryIdsAdded = <int>[];
bool isEmpty() {
return resourceTree.isEmpty;
}
bool isNotEmpty() {
return resourceTree.isNotEmpty;
}
// For the Customer Recource Library page - need to parse all teh data and fill the tree
void addResourcesFromCustomerResourceQuery(dynamic data) {
final List<dynamic> _resourceList =
data['customerByCustomerId']['customerLinksByCustomerId']['nodes'];
final List<dynamic> _directoryList = data['customerByCustomerId']
['customerLinkDirectoriesByCustomerId']['nodes'];
if (_resourceList.isNotEmpty) {
// OK -- build our node strucutre first...
_addResourcesFromLists(_resourceList, _directoryList);
}
}
// For the Event Detail Recources page - need to parse all teh data and fill the tree
void addResourcesFromEventResourceQuery(dynamic data) {
// Get the bare resources for the event...
final List<dynamic> _resourceList =
data['eventByEventId']['eventLinksByEventId']['nodes'];
final List<dynamic> _directoryList =
data['eventByEventId']['eventLinkDirectoriesByEventId']['nodes'];
// logger.finer('** DIRECTORY LIST FROM EVENT **');
// logger.finer(_directoryList);
// Get the bare resources for the customer that is in all events..
_resourceList.addAll(data['eventByEventId']['customerByCustomerId']
['customerLinksByCustomerId']['nodes']);
_directoryList.addAll(data['eventByEventId']['customerByCustomerId']
['customerLinkDirectoriesByCustomerId']['nodes']);
// logger.finer('** DIRECTORY LIST FROM CUSTOMER **');
// logger.finer(_directoryList);
// See if this was generated from a Template (Event Category) and if so see if there
// is anything here to grab
if (data['eventByEventId']['eventCategoryByEventCategoryId'] != null) {
// Get the bare resources for the Event Category Level (Templates)...
// These have a sub-element so they are a bit harder...
final List<dynamic> _templateLinks = data['eventByEventId']
['eventCategoryByEventCategoryId']
['eventCatCustomerLinkMapsByEventCategoryId']['nodes']
.map((dynamic link) => link['customerLinkByCustomerLinkId'])
.toList();
// logger.finer('**** DATA ****');
// logger.finer(_templateLinks);
_resourceList.addAll(_templateLinks);
// Grab the directories from the template..
final List<dynamic> _templateDirs = data['eventByEventId']
['eventCategoryByEventCategoryId']
['eventCatCustomerLinkDirMapsByEventCategoryId']['nodes']
.map((dynamic dir) =>
dir['customerLinkDirectoryByCustomerLinkDirectoryId'])
.toList();
// logger.finer('** DIRECTORY LIST FROM TEMPLATE **');
// logger.finer(_templateDirs);
_directoryList.addAll(_templateDirs);
}
// logger.finer('** RESOURCE LIST **');
// logger.finer(_resourceList);
// logger.finer('** DIRECTORY LIST **');
// logger.finer(_directoryList);
// See if we have anything to show after all that work!
if (_resourceList.isNotEmpty) {
// OK -- build our node strucutre first...
_addResourcesFromLists(_resourceList, _directoryList);
}
}
// Main routine that will fill in the tree from two lists -- resources and directories..
void _addResourcesFromLists(
List<dynamic> _resourceList, List<dynamic> _directoryList) {
// keep list of directories and resource so we do not add duplicates.
_directoryIdsAdded.clear();
// Walk through the root level resources ...
_addResourcesToNode(null, _resourceList);
// Now add the root folder structures and then the resources for each folder
// logger.fine('directory list');
// logger.fine(_directoryList);
_addFolders(null, _resourceList, _directoryList);
}
// Given a folder (somewhere) grab the children and recurse if needed ..
void _addFolders(ResourceEntry folder, List<dynamic> _resourceList,
List<dynamic> _directoryList) {
// Any resources at this level?
final int directoryId = folder?.directoryId;
// logger.fine('[_addSubFolders() - subfolders for $directoryId');
// Unless we're at the root level (null) add our entry to the tree..
if (folder != null) {
_addResourcesToNode(folder, _resourceList);
}
// Now look for subfolders and recurse back to us ..
_directoryList
.where(
(dynamic folder) => folder['parentLinkDirectoryId'] == directoryId)
// ignore: avoid_function_literals_in_foreach_calls
.forEach((dynamic directoryEntry) {
// Create our simple ResourceEntry for the folder..
final Resource _folderResource = _createFolderResource(directoryEntry);
final ResourceEntry _entry = ResourceEntry(
_folderResource.linkDirectoryId, _folderResource,
children: <ResourceEntry>[], isFolder: true);
// Add this to our root or the folder if it's alive..
// logger.fine('Directory id: ${_entry.directoryId}');
if (!_directoryIdsAdded.contains(_entry.directoryId)) {
_directoryIdsAdded.add(_entry.directoryId);
if (folder == null) {
// logger.fine(
// 'Adding directory id: ${_entry.directoryId} to resourceTree');
resourceTree.add(_entry);
} else {
// logger.fine(
// 'Adding directory id: ${_entry.directoryId} to directory ${folder.directoryId}');
folder.children.add(_entry);
}
}
// Now recurse for children folders
_addFolders(_entry, _resourceList, _directoryList);
});
}
Resource _createFolderResource(dynamic folder) {
// logger.fine('creating resource for folder:');
// logger.fine(folder);
final Resource _folderResource =
Resource(0, folder['linkDirectoryId'], folder['name']);
return _folderResource;
}
void _addResourcesToNode(
ResourceEntry nodeRoot, List<dynamic> _resourceList) {
// Find any resources for this node and add them ..
final int directoryId = nodeRoot?.directoryId;
// logger.fine(
// '[ResourceLibraryScreen - _addResourcesToNode()] - searching for directroyId $directoryId');
// Now walk the resources and find the ones that should be here and add it to the children
_resourceList
.where((dynamic link) => link['linkDirectoryId'] == directoryId)
// ignore: avoid_function_literals_in_foreach_calls
.forEach((dynamic link) {
// logger.fine('[_addResourcesToNode() - adding resource $link to folder');
// logger.fine(link);
final Resource _item = Resource.createResourceFromLink(link);
final ResourceEntry _entry = ResourceEntry(0, _item);
// Add it to the proper locale .. either the root or the child node..
// logger.fine('Resource Link id: ${_entry.resource.linkId}');
if (nodeRoot == null) {
// logger.fine(
// 'Adding resource Link id: ${_entry.resource.linkId} to resourceTree');
resourceTree.add(_entry);
} else {
// logger.fine(
// 'Adding resource Link id: ${_entry.resource.linkId} to nodeRoot ${nodeRoot.directoryId}');
nodeRoot.children.add(_entry);
}
});
}
}
-- ResourceTile.dart
// Show and control one resource tile
import 'package:logging/logging.dart';
import 'package:flutter/material.dart';
import '../../models/resource.dart';
import '../../util/mime_utils.dart';
import '../utilities/show_resource.dart';
import './expansionTile.dart';
final Logger _logger = Logger('ResourceTile');
class ResourceTile extends StatelessWidget {
const ResourceTile({
Key key,
this.resourceEntry,
}) : super(key: key);
final ResourceEntry resourceEntry;
@override
Widget build(BuildContext context) {
_logger.fine('[build()]');
final Resource _resource = resourceEntry.resource;
Widget _returnTile;
// If we're a folder use the ExpansionTile..
if (resourceEntry.isFolder) {
// ThemeData _theme = Theme.of(context);
// _logger.fine('Theme divider color is ${_theme.dividerColor}');
_returnTile = Theme(
data: Theme.of(context).copyWith(
dividerColor: Colors.yellow,
),
child: MMExpansionTile(
key: PageStorageKey<ResourceEntry>(resourceEntry),
title: Text(
resourceEntry.resource.displayName,
),
children: resourceEntry.children
.map<Widget>((ResourceEntry resourceEntry) =>
ResourceTile(resourceEntry: resourceEntry))
.toList(),
),
);
} else {
// We're a normal thing.. find an icon and just use the Listtile..
IconData _iconData;
if (_resource.mimeType == null) {
// Must be a URL
_iconData = Icons.bookmark;
} else {
_iconData = getIconForMime(_resource.mimeType);
}
_returnTile = ListTile(
leading: Icon(
_iconData,
),
title: Text(
resourceEntry.resource.displayName,
),
onTap: () => Launch.handleResourceTap(context,
url: resourceEntry.resource.linkUrl,
storedFilename: resourceEntry.resource.storedFilename,
mimeType: resourceEntry.resource.mimeType),
);
}
// Ok -- this is a parent node.. use an Expansion Tile
return _returnTile;
}
}