budde377/Part

View on GitHub
dart/part/lib/src/site_class_content.dart

Summary

Maintainability
Test Coverage
part of site_classes;

abstract class Content {
  final String id;

  Content(this.id);

  Future<List<DateTime>> get changeTimes;

  Future<Revision> operator [](DateTime time);

  Future<Revision> addContent(String content);

  Future<List<Revision>> listRevisions({DateTime from:null, DateTime to:null});

  Stream<Revision> get onAddContent;

  bool get hasDraft;

  Revision get draft;

  void set draft(Revision);

  void clearDraft();

  Stream<bool> get onHasDraftChange;

}


abstract class AJAXContentFunctionGenerator {

  final String contentLibrary, id, content_id;

  AJAXContentFunctionGenerator(this.contentLibrary, this.id, this.content_id);

  String generateListContentFunction({int from:null, int to: null, bool includeContent:false}) => contentLibrary + ".listContentHistory($from, $to, ${!includeContent})";

  String generateContentAtTimeFunction(num time) => contentLibrary + ".getContentAt($time)";

  Map generateAddContentFunction(String content) {
    var formData = new FormData();
    formData.append("content", content);
    return {
        "function":contentLibrary + ".addContent(POST['content'])", "formdata":formData
    };
  }

}


class JSONContentPageFunctionGenerator extends AJAXContentFunctionGenerator {
  final Page page;

  JSONContentPageFunctionGenerator(Page page, String id) :
  super(
      "PageOrder.getPage(${quoteString(page.id)}).getContent(${quoteString(id)})",
      id,
      "PageContent.${page.id}.$id"), this.page = page;

}

class JSONContentSiteFunctionGenerator extends AJAXContentFunctionGenerator {

  JSONContentSiteFunctionGenerator(String id) : super(
      "Site.getContent(${quoteString(id)})",
      id,
      "SiteContent.$id");


}

class AJAXContent extends Content {

  StreamController<bool> onHasDraftChangeController = new StreamController.broadcast();

  final AJAXContentFunctionGenerator contentStrategy;

  AJAXContent(AJAXContentFunctionGenerator contentStrategy): super(contentStrategy.id), this.contentStrategy = contentStrategy;

  AJAXContent.page(Page page, String id) : this(new JSONContentPageFunctionGenerator(page, id));

  AJAXContent.site(String id) : this(new JSONContentSiteFunctionGenerator(id));

  Map<DateTime, Revision> _revisions = new Map<DateTime, Revision>();

  StreamController<Revision> _streamController = new StreamController<Revision>();
  Stream<Revision> _stream;

  Future<List<DateTime>> get changeTimes {
    var completer = new Completer<List<DateTime>>();
    ajaxClient.callFunctionString(contentStrategy.generateListContentFunction()).then((Response response) {
      if (response.type == Response.RESPONSE_TYPE_SUCCESS) {
        List<String> payload = response.payload == null ? [] : response.payload;
        completer.complete(payload.map((String timeString) => new DateTime.fromMillisecondsSinceEpoch(int.parse(timeString) * 1000)).toList(growable:false));
      } else {
        completer.completeError(new Exception("Could not list times"));
      }
    });
    return completer.future;
  }

  Future<Revision> operator [](DateTime time){
    var completer = new Completer<Revision>();

    ajaxClient.callFunctionString(contentStrategy.generateContentAtTimeFunction(time.millisecond ~/ 1000)).then((Response response) {
      if (response.type != Response.RESPONSE_TYPE_SUCCESS) {
        completer.completeError(new Exception("Could not get content at time"));
        return;
      }
      if (response.payload == null) {
        completer.complete(null);
        return;
      }

      completer.complete(_generateRevision(new DateTime.fromMillisecondsSinceEpoch(int.parse(response.payload['time']) * 1000), response.payload['content']));
    });
    return completer.future;
  }

  Future<Revision> addContent(String content) {
    var completer = new Completer<Revision>();
    var c = contentStrategy.generateAddContentFunction(content);
    ajaxClient.callFunctionString(c["function"], form_data:c["formdata"]).then((Response response) {
      if (response.type != Response.RESPONSE_TYPE_SUCCESS) {
        completer.completeError(new Exception("Could not add content"));
        return;
      }
      var r;
      completer.complete(r = _generateRevision(new DateTime.fromMillisecondsSinceEpoch((response.payload is String ? int.parse(response.payload) : response.payload) * 1000), content));
      _streamController.add(r);
    });
    return completer.future;
  }

  Future<List<Revision>> listRevisions({DateTime from:null, DateTime to:null}) {
    var completer = new Completer<List<Revision>>();
    from = from == null ? new DateTime.fromMillisecondsSinceEpoch(0) : from;
    to = to == null ? new DateTime.now() : to;

    var fromm = from.millisecondsSinceEpoch ~/ 1000, too = to.millisecondsSinceEpoch ~/ 1000;
    ajaxClient.callFunctionString(contentStrategy.generateListContentFunction(from:fromm, to:too, includeContent:true)).then((Response response) {
      if (response.type == Response.RESPONSE_TYPE_SUCCESS) {
        List<Map<String, dynamic>> payload = response.payload == null ? [] : response.payload;
        completer.complete(payload.map((Map<String, dynamic> m) => _generateRevision(new DateTime.fromMillisecondsSinceEpoch(int.parse(m['time']) * 1000), m['content'])).toList(growable:false));

      } else {
        completer.completeError(new Exception("Could not list revisions"));
      }
    });

    return completer.future;
  }

  Revision _generateRevision(DateTime time, String content) => _revisions.putIfAbsent(time, () => new Revision(time, content));


  Stream<Revision> get onAddContent => _stream == null ? _stream = _streamController.stream.asBroadcastStream() : _stream;


  bool get hasDraft => window.localStorage.containsKey(contentStrategy.content_id) && window.localStorage.containsKey(contentStrategy.content_id + ".time");

  Revision get draft => hasDraft ? new Revision(new DateTime.fromMillisecondsSinceEpoch(int.parse(window.localStorage[contentStrategy.content_id + ".time"])), window.localStorage[contentStrategy.content_id]) : null;

  void set draft(Revision revision) {
    window
      ..localStorage[contentStrategy.content_id + ".time"] = revision.time.millisecondsSinceEpoch.toString()
      ..localStorage[contentStrategy.content_id] = revision.content;
    onHasDraftChangeController.add(hasDraft);
  }

  Stream<bool> get onHasDraftChange => onHasDraftChangeController.stream;

  void clearDraft() {
    window.localStorage
      ..remove(contentStrategy.content_id + ".time")
      ..remove(contentStrategy.content_id);
    onHasDraftChangeController.add(hasDraft);
  }

}