@banta/sdk 5.5.3 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  import { InjectionToken } from "@angular/core";
2
2
  export const BANTA_SDK_OPTIONS = new InjectionToken('BANTA_SDK_OPTIONS');
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2RrLW9wdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9zZGsvc3JjL2xpYi9zZGstb3B0aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBTy9DLE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLElBQUksY0FBYyxDQUFhLG1CQUFtQixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3Rpb25Ub2tlbiB9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFNka09wdGlvbnMge1xyXG4gICAgc2VydmljZVVybD86IHN0cmluZztcclxuICAgIGVtb2ppVXJsPzogc3RyaW5nO1xyXG59XHJcblxyXG5leHBvcnQgY29uc3QgQkFOVEFfU0RLX09QVElPTlMgPSBuZXcgSW5qZWN0aW9uVG9rZW48U2RrT3B0aW9ucz4oJ0JBTlRBX1NES19PUFRJT05TJyk7Il19
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2RrLW9wdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9zZGsvc3JjL2xpYi9zZGstb3B0aW9ucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsY0FBYyxFQUFRLE1BQU0sZUFBZSxDQUFDO0FBU3JELE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLElBQUksY0FBYyxDQUFhLG1CQUFtQixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3Rpb25Ub2tlbiwgVHlwZSB9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XHJcbmltcG9ydCB7IENoYXRCYWNrZW5kQmFzZSB9IGZyb20gXCIuL2NoYXQtYmFja2VuZC1iYXNlXCI7XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFNka09wdGlvbnMge1xyXG4gICAgc2VydmljZVVybD86IHN0cmluZztcclxuICAgIGVtb2ppVXJsPzogc3RyaW5nO1xyXG4gICAgYmFja2VuZENsYXNzPzogVHlwZTxDaGF0QmFja2VuZEJhc2U+XHJcbn1cclxuXHJcbmV4cG9ydCBjb25zdCBCQU5UQV9TREtfT1BUSU9OUyA9IG5ldyBJbmplY3Rpb25Ub2tlbjxTZGtPcHRpb25zPignQkFOVEFfU0RLX09QVElPTlMnKTsiXX0=
@@ -7601,7 +7601,27 @@ class CommentViewComponent {
7601
7601
  this.isViewingMore = false;
7602
7602
  this.isLoadingMore = false;
7603
7603
  this.hasMore = false;
7604
+ /**
7605
+ * While this is called "new" messages, it really represents the messages that would be visible *at the beginning
7606
+ * of the sort order*, which can be flipped by the newestLast feature (used for replies mode).
7607
+ *
7608
+ * So, when newestLast is false, regardless of the current sortOrder, newMessages are conceptually
7609
+ * *above* the visible set of messages.
7610
+ *
7611
+ * When newestLast is true (as in replies mode), regardless of the current sortOrder, newMessages are conceptually
7612
+ * *below* the visible set of messages.
7613
+ */
7604
7614
  this.newMessages = [];
7615
+ /**
7616
+ * While this is called "older" messages, it really represents the messages that would be visible *at the end of the
7617
+ * sort order*, which can be flipped by the newestLast feature (useds for replies mode).
7618
+ *
7619
+ * So, when newestLast is false, regardless of the current sortOrder, olderMessages are conceptually *below*
7620
+ * the visible set of messages.
7621
+ *
7622
+ * When newestLast is true (as in replies mode), regardless of the current sortOrder, olderMessages are conceptually
7623
+ * *above* the visible set of messages.
7624
+ */
7605
7625
  this.olderMessages = [];
7606
7626
  //#endregion
7607
7627
  //#region Inputs
@@ -7640,9 +7660,12 @@ class CommentViewComponent {
7640
7660
  this.messageEdited = this._messageEdited.asObservable();
7641
7661
  this.sortOrderChanged = this._sortOrderChanged.asObservable();
7642
7662
  this.filterModeChanged = this._filterModeChanged.asObservable();
7663
+ this.heldMessages = [];
7643
7664
  }
7644
7665
  get source() { return this._source; }
7645
7666
  set source(value) { this.setSource(value); }
7667
+ get previousMessages() { return this.newestLast ? this.olderMessages : this.newMessages; }
7668
+ get nextMessages() { return this.newestLast ? this.newMessages : this.olderMessages; }
7646
7669
  get comments() { return Array.from(this.commentsQuery); }
7647
7670
  //#endregion
7648
7671
  /**
@@ -7681,10 +7704,12 @@ class CommentViewComponent {
7681
7704
  get shouldShowNewMessageIndicator() {
7682
7705
  return this.isViewingMore
7683
7706
  || this.customSortEnabled
7684
- || this.source.filterMode !== FilterMode.ALL
7685
- || this.newMessages.length > 0;
7707
+ || this.sourceFilterMode !== FilterMode.ALL
7708
+ || this.heldMessages.length > 0;
7686
7709
  }
7687
7710
  get shouldHoldNewMessages() {
7711
+ if (this.customSortEnabled)
7712
+ return true;
7688
7713
  if (this.holdNewMessages || this.isViewingMore) {
7689
7714
  console.log(`holding due to settings`);
7690
7715
  return true;
@@ -7772,7 +7797,7 @@ class CommentViewComponent {
7772
7797
  this._deleted.next(message);
7773
7798
  }
7774
7799
  setSource(value) {
7775
- this.customSortEnabled = value?.sortOrder !== CommentsOrder.NEWEST;
7800
+ this.customSortEnabled = (value?.sortOrder ?? CommentsOrder.NEWEST) !== CommentsOrder.NEWEST;
7776
7801
  this.newMessages = [];
7777
7802
  this.olderMessages = [];
7778
7803
  window.bantaSourceDebug = value;
@@ -7797,23 +7822,78 @@ class CommentViewComponent {
7797
7822
  let messages = (await this._source.getExistingMessages());
7798
7823
  messages.forEach(m => m.transientState ??= {});
7799
7824
  this.messages = this.newestLast ? messages.slice().reverse() : messages;
7825
+ if (this.messages.length > this.maxVisibleMessages) {
7826
+ if (this.newestLast) {
7827
+ this.previousMessages.push(...this.messages.splice(0, this.messages.length - this.maxVisibleMessages));
7828
+ }
7829
+ else {
7830
+ this.nextMessages.unshift(...this.messages.splice(this.maxVisibleMessages, this.messages.length));
7831
+ }
7832
+ }
7833
+ this.debugMessages();
7800
7834
  this.sortMessages();
7801
7835
  if (this.markSourceLoaded)
7802
7836
  this.markSourceLoaded();
7803
7837
  }
7838
+ debugMessages() {
7839
+ console.log([
7840
+ ...this.previousMessages.map(x => x.message),
7841
+ '[[',
7842
+ ...this.messages.map(x => x.message),
7843
+ ']]',
7844
+ ...this.nextMessages.map(x => x.message)
7845
+ ].map(x => /\d+/.test(x) ? this.zeroPad(x, 2) : x).join(" "));
7846
+ }
7847
+ zeroPad(number, count = 2) {
7848
+ let str;
7849
+ if (typeof number === 'number')
7850
+ str = String(number);
7851
+ else
7852
+ str = number;
7853
+ while (str.length < count)
7854
+ str = '0' + str;
7855
+ return str;
7856
+ }
7857
+ leftPad(str, count = 2) {
7858
+ while (str.length < count)
7859
+ str = ' ' + str;
7860
+ return str;
7861
+ }
7804
7862
  messageIdentity(index, chatMessage) {
7805
7863
  return chatMessage.id;
7806
7864
  }
7807
- async showNew(event) {
7865
+ get sourceSortOrder() {
7866
+ return this.source?.sortOrder ?? CommentsOrder.NEWEST;
7867
+ }
7868
+ get sourceFilterMode() {
7869
+ return this.source?.filterMode ?? FilterMode.ALL;
7870
+ }
7871
+ /**
7872
+ * Show the newest content.
7873
+ * - If an unnatural sort order is active (ie Oldest or Likes), it will be changed to Newest.
7874
+ * - The new content will be placed where new content goes based on newestLast (replies mode), so if it is true, the content is
7875
+ * placed at the end, otherwise it is placed at the beginning.
7876
+ *
7877
+ * @param event
7878
+ * @returns
7879
+ */
7880
+ async showNewest(event) {
7881
+ // Regardless of how we handle this, clear out our held messages
7882
+ this.heldMessages = [];
7883
+ // If the sort order is not already Newest, switch to Newest and stop.
7884
+ // The act of changing the sort order will cause the newest content to be loaded.
7808
7885
  let naturalOrder = CommentsOrder.NEWEST;
7809
- if (this.source && (this.source.sortOrder !== naturalOrder || this.source.filterMode !== FilterMode.ALL)) {
7810
- if (this.source.sortOrder !== naturalOrder)
7886
+ if (this.sourceSortOrder !== naturalOrder || this.sourceFilterMode !== FilterMode.ALL) {
7887
+ if (this.sourceSortOrder !== naturalOrder)
7811
7888
  this._sortOrderChanged.next(naturalOrder);
7812
- if (this.source.filterMode !== FilterMode.ALL)
7889
+ if (this.sourceFilterMode !== FilterMode.ALL)
7813
7890
  this._filterModeChanged.next(FilterMode.ALL);
7814
7891
  return;
7815
7892
  }
7893
+ // On this path, we are already on Newest, but there is newer content available (such as when new content is
7894
+ // being buffered due to user engagement on a comment)
7816
7895
  this.isViewingMore = false;
7896
+ // Move all newerMessages into messages, respecting the newestLast direction (normal or replies mode)
7817
7897
  if (this.newestLast)
7818
7898
  this.messages = this.messages.concat(this.newMessages.splice(0, this.newMessages.length));
7819
7899
  else
@@ -7822,6 +7902,7 @@ class CommentViewComponent {
7822
7902
  this.olderMessages = overflow.concat(this.olderMessages);
7823
7903
  this.olderMessages.splice(this.maxMessages - this.maxVisibleMessages, this.olderMessages.length);
7824
7904
  this.hasMore = this.olderMessages.length > 0;
7905
+ // Scroll to the newest comment.
7825
7906
  if (this.messages.length > 0) {
7826
7907
  if (this.newestLast) {
7827
7908
  this.scrollToComment(this.messages[this.messages.length - 1].id);
@@ -7831,39 +7912,191 @@ class CommentViewComponent {
7831
7912
  }
7832
7913
  }
7833
7914
  }
7834
- async showMore() {
7915
+ get showDebug() {
7916
+ if (typeof window === 'undefined')
7917
+ return false;
7918
+ return localStorage['banta:debug'] === '1';
7919
+ }
7920
+ get shouldShowNext() {
7921
+ if (!this.newestLast) {
7922
+ return this.hasMore || this.nextMessages.length > 0;
7923
+ }
7924
+ return this.nextMessages.length > 0;
7925
+ }
7926
+ get shouldShowPrevious() {
7927
+ if (this.newestLast) {
7928
+ return this.hasMore || this.previousMessages.length > 0;
7929
+ }
7930
+ return this.previousMessages.length > 0;
7931
+ }
7932
+ get pageSize() {
7933
+ return Math.min(20, this.maxVisibleMessages);
7934
+ }
7935
+ async showPrevious() {
7835
7936
  this.isViewingMore = true;
7836
- if (this.olderMessages.length > 0) {
7937
+ let nextPageSize = this.pageSize;
7938
+ this.isLoadingMore = false;
7939
+ if (this.previousMessages.length > 0) {
7940
+ const storedMessages = this.previousMessages.splice(Math.max(0, this.previousMessages.length - nextPageSize), nextPageSize);
7941
+ this.messages = [...storedMessages, ...this.messages];
7942
+ nextPageSize -= storedMessages.length;
7943
+ }
7944
+ // Load more from backend if needed
7945
+ // Note: Backend only supports fetching more content in one direction.
7946
+ let lastMessage = this.previousMessages[0] ?? this.messages[0];
7947
+ if (!lastMessage)
7948
+ this.hasMore = false;
7949
+ if (nextPageSize > 0 && this.newestLast && lastMessage) {
7950
+ this.isLoadingMore = true;
7951
+ let messages = await this.source.loadAfter(lastMessage, nextPageSize);
7952
+ messages = messages.slice().reverse(); // because newestLast === true
7953
+ messages.forEach(m => m.transientState ??= {});
7954
+ // In replies mode (newestLast), we want to put these new messages onto the *top* of the set of visible messages.
7955
+ // Otherwise we want to put them on the *bottom*.
7956
+ this.messages = [...messages, ...this.messages];
7957
+ // If we didn't receive any messages at all, there's no more to fetch.
7958
+ if (messages.length === 0)
7959
+ this.hasMore = false;
7837
7960
  this.isLoadingMore = false;
7838
- this.messages = this.messages.concat(this.olderMessages.splice(0, 50));
7839
7961
  }
7840
- this.isLoadingMore = true;
7841
- let nextPageSize = 20;
7842
- let lastMessage;
7843
- if (this.newestLast) {
7844
- lastMessage = this.olderMessages[0] ?? this.messages[0];
7962
+ // Extract the messages that do not fit in the maxVisibleMessages buffer.
7963
+ if (this.messages.length > this.maxVisibleMessages) {
7964
+ let overflow = this.messages.splice(this.maxVisibleMessages, this.messages.length);
7965
+ this.nextMessages.unshift(...overflow);
7966
+ if (this.nextMessages.length > this.maxMessages)
7967
+ this.nextMessages.splice(this.maxMessages, this.nextMessages.length);
7845
7968
  }
7846
- else {
7847
- lastMessage = this.olderMessages[this.olderMessages.length - 1] ?? this.messages[this.messages.length - 1];
7969
+ this.debugMessages();
7970
+ }
7971
+ get sortNextLabel() {
7972
+ if (this.sourceSortOrder === 'newest') {
7973
+ return 'Older';
7848
7974
  }
7849
- if (!lastMessage) {
7850
- this.isLoadingMore = false;
7851
- this.hasMore = false;
7852
- return;
7975
+ else if (this.sourceSortOrder === 'oldest') {
7976
+ return 'Newer';
7853
7977
  }
7854
- let messages = await this.source.loadAfter(lastMessage, nextPageSize);
7855
- if (this.newestLast)
7856
- messages = messages.slice().reverse();
7857
- messages.forEach(m => m.transientState ??= {});
7858
- if (this.newestLast)
7859
- this.messages = messages.concat(this.messages);
7860
- else
7861
- this.messages = this.messages.concat(messages);
7978
+ else if (this.sourceSortOrder === 'likes') {
7979
+ return 'Less Likes';
7980
+ }
7981
+ return 'More';
7982
+ }
7983
+ get sortPreviousLabel() {
7984
+ if (this.sourceSortOrder === 'newest') {
7985
+ return 'Newer';
7986
+ }
7987
+ else if (this.sourceSortOrder === 'oldest') {
7988
+ return 'Older';
7989
+ }
7990
+ else if (this.sourceSortOrder === 'likes') {
7991
+ return 'More Likes';
7992
+ }
7993
+ return 'More';
7994
+ }
7995
+ get nextLabel() { return this.newestLast ? this.sortPreviousLabel : this.sortNextLabel; }
7996
+ get previousLabel() { return this.newestLast ? this.sortNextLabel : this.sortPreviousLabel; }
7997
+ /**
7998
+ * Show more content
7999
+ * - When in replies mode (newestLast), the content is added at the top
8000
+ * - When in normal mode, the content is added at the bottom
8001
+ * - The current sort order does *not* factor in here, which is why it is showMore() not showEarlier().
8002
+ *
8003
+ * @returns
8004
+ */
8005
+ async showNext() {
8006
+ this.isViewingMore = true;
8007
+ let nextPageSize = this.pageSize;
7862
8008
  this.isLoadingMore = false;
7863
- if (messages.length === 0) {
7864
- console.log(`Reached the end of the list.`);
8009
+ if (this.nextMessages.length > 0) {
8010
+ const storedMessages = this.nextMessages.splice(0, nextPageSize);
8011
+ this.messages = [...this.messages, ...storedMessages];
8012
+ nextPageSize -= storedMessages.length;
8013
+ }
8014
+ const lastMessage = this.olderMessages[this.olderMessages.length - 1] ?? this.messages[this.messages.length - 1];
8015
+ if (!lastMessage)
7865
8016
  this.hasMore = false;
8017
+ if (nextPageSize > 0 && !this.newestLast && lastMessage) {
8018
+ // Load more from backend
8019
+ this.isLoadingMore = true;
8020
+ let messages = await this.source.loadAfter(lastMessage, nextPageSize);
8021
+ messages.forEach(m => m.transientState ??= {});
8022
+ this.messages = [...this.messages, ...messages];
8023
+ // If we didn't receive any messages at all, there's no more to fetch.
8024
+ if (messages.length === 0)
8025
+ this.hasMore = false;
8026
+ this.isLoadingMore = false;
8027
+ }
8028
+ // Extract the messages that do not fit in the maxVisibleMessages buffer.
8029
+ if (this.messages.length > this.maxVisibleMessages) {
8030
+ let overflow = this.messages.splice(0, this.messages.length - this.maxVisibleMessages);
8031
+ // Regardless of the order (newestLast), newMessages represents the direction that is being pushed, since it's definition
8032
+ // depends on that order. Move overflowing messages into newMessages.
8033
+ this.previousMessages.push(...overflow);
8034
+ if (this.previousMessages.length > this.maxMessages)
8035
+ this.previousMessages.splice(0, this.previousMessages.length - this.maxMessages);
8036
+ }
8037
+ this.debugMessages();
8038
+ }
8039
+ /**
8040
+ * Show more content
8041
+ * - When in replies mode (newestLast), the content is added at the top
8042
+ * - When in normal mode, the content is added at the bottom
8043
+ * - The current sort order does *not* factor in here, which is why it is showMore() not showEarlier().
8044
+ *
8045
+ * @returns
8046
+ */
8047
+ async showMore() {
8048
+ this.isViewingMore = true;
8049
+ let nextPageSize = this.pageSize;
8050
+ this.isLoadingMore = false;
8051
+ if (this.olderMessages.length > 0) {
8052
+ const storedMessages = this.olderMessages.splice(0, nextPageSize);
8053
+ this.messages = this.messages.concat(storedMessages);
8054
+ nextPageSize -= storedMessages.length;
8055
+ this.hasMore = this.olderMessages.length > 0;
8056
+ }
8057
+ if (nextPageSize > 0) {
8058
+ // Load more from backend
8059
+ this.isLoadingMore = true;
8060
+ let lastMessage;
8061
+ if (this.newestLast) {
8062
+ lastMessage = this.olderMessages[0] ?? this.messages[0];
8063
+ }
8064
+ else {
8065
+ lastMessage = this.olderMessages[this.olderMessages.length - 1] ?? this.messages[this.messages.length - 1];
8066
+ }
8067
+ if (!lastMessage) {
8068
+ this.isLoadingMore = false;
8069
+ this.hasMore = false;
8070
+ return;
8071
+ }
8072
+ let messages = await this.source.loadAfter(lastMessage, nextPageSize);
8073
+ if (this.newestLast)
8074
+ messages = messages.slice().reverse();
8075
+ messages.forEach(m => m.transientState ??= {});
8076
+ // In replies mode (newestLast), we want to put these new messages onto the *top* of the set of visible messages.
8077
+ // Otherwise we want to put them on the *bottom*.
8078
+ if (this.newestLast) {
8079
+ this.messages = messages.concat(this.messages);
8080
+ }
8081
+ else {
8082
+ this.messages = this.messages.concat(messages);
8083
+ }
8084
+ // If we didn't receive any messages at all, there's no more to fetch.
8085
+ if (messages.length === 0) {
8086
+ this.hasMore = false;
8087
+ }
8088
+ this.isLoadingMore = false;
7866
8089
  }
8090
+ // Extract the messages that do not fit in the maxVisibleMessages buffer.
8091
+ let overflow;
8092
+ if (this.newestLast)
8093
+ overflow = this.messages.splice(this.maxVisibleMessages, this.messages.length);
8094
+ else
8095
+ overflow = this.messages.splice(0, this.maxVisibleMessages);
8096
+ // Regardless of the order (newestLast), newMessages represents the direction that is being pushed, since it's definition
8097
+ // depends on that order. Move overflowing messages into newMessages.
8098
+ this.newMessages = overflow.concat(this.newMessages);
8099
+ this.newMessages.splice(this.maxMessages - this.maxVisibleMessages, this.newMessages.length);
7867
8100
  }
7868
8101
  addMessage(message) {
7869
8102
  if (!message.transientState)
@@ -7872,9 +8105,13 @@ class CommentViewComponent {
7872
8105
  let bucket = this.olderMessages;
7873
8106
  let newestLast = this.newestLast;
7874
8107
  if (this.shouldHoldNewMessages) {
8108
+ this.heldMessages.push(message);
7875
8109
  destination = this.newMessages;
7876
8110
  bucket = null;
7877
8111
  }
8112
+ // If we aren't on the newest sort order, new messages shouldn't be added at all
8113
+ if (this.sourceSortOrder !== CommentsOrder.NEWEST)
8114
+ return;
7878
8115
  if (newestLast) {
7879
8116
  destination.push(message);
7880
8117
  let overflow = destination.splice(this.maxVisibleMessages, destination.length);
@@ -7980,11 +8217,11 @@ class CommentViewComponent {
7980
8217
  return false;
7981
8218
  }
7982
8219
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: CommentViewComponent, deps: [{ token: ChatBackendBase }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
7983
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.9", type: CommentViewComponent, selector: "banta-comment-view", inputs: { source: "source", maxMessages: "maxMessages", maxVisibleMessages: "maxVisibleMessages", newestLast: "newestLast", holdNewMessages: "holdNewMessages", showEmptyState: "showEmptyState", allowReplies: "allowReplies", enableHoldOnClick: "enableHoldOnClick", enableHoldOnScroll: "enableHoldOnScroll", customMenuItems: "customMenuItems", fixedHeight: "fixedHeight", selectedMessage: "selectedMessage", genericAvatarUrl: "genericAvatarUrl" }, outputs: { userSelected: "userSelected", reported: "reported", liked: "liked", unliked: "unliked", usernameSelected: "usernameSelected", avatarSelected: "avatarSelected", shared: "shared", deleted: "deleted", selected: "selected", messageEdited: "messageEdited", sortOrderChanged: "sortOrderChanged", filterModeChanged: "filterModeChanged" }, host: { properties: { "class.fixed-height": "this.fixedHeight" } }, viewQueries: [{ propertyName: "messageContainer", first: true, predicate: ["messageContainer"], descendants: true }, { propertyName: "commentsQuery", predicate: CommentComponent, descendants: true }], ngImport: i0, template: "<div class=\"banta-message-container\" #messageContainer>\r\n <ng-content select=\"[data-before]\"></ng-content>\r\n\r\n <div class=\"banta-top-sticky\">\r\n <a *ngIf=\"!newestLast\" mat-button class=\"nav\" [class.visible]=\"shouldShowNewMessageIndicator\" href=\"javascript:;\" (click)=\"showNew($event)\">\r\n <mat-icon>file_upload</mat-icon>\r\n <ng-container *ngIf=\"newMessages.length >= 1\">\r\n New ({{newMessages.length}})\r\n </ng-container>\r\n <ng-container *ngIf=\"newMessages.length == 0\">\r\n Newest\r\n </ng-container>\r\n </a>\r\n </div>\r\n <a mat-button class=\"nav\" [class.visible]=\"newestLast && hasMore && !isLoadingMore\" href=\"javascript:;\" (click)=\"showMore()\">Show earlier</a>\r\n\r\n <ng-container *ngIf=\"messages.length === 0\">\r\n <div class=\"banta-empty-state\" *ngIf=\"showEmptyState\">\r\n Be the first to comment!\r\n </div>\r\n </ng-container>\r\n <ng-container *ngFor=\"let message of messages; trackBy: messageIdentity\">\r\n <banta-comment\r\n *ngIf=\"!message.hidden\"\r\n class=\"abbreviated\"\r\n \r\n [customMenuItems]=\"customMenuItems\"\r\n [message]=\"message\"\r\n [mine]=\"currentUser?.id === message.user?.id\"\r\n [permissions]=\"source?.permissions\"\r\n [showReplyAction]=\"allowReplies\"\r\n [editing]=\"message.transientState.editing\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n [readonly]=\"source?.readonly\"\r\n (click)=\"enableHoldOnClick ? (holdNewMessages = true) : undefined\"\r\n (editStarted)=\"startEditing(message)\"\r\n (deleted)=\"deleteMessage(message)\"\r\n (editEnded)=\"message.transientState.editing = false\"\r\n (edited)=\"saveEdit(message, $event)\"\r\n (userSelected)=\"selectMessageUser(message)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (liked)=\"likeMessage(message)\"\r\n (unliked)=\"unlikeMessage(message)\"\r\n (reported)=\"reportMessage(message)\"\r\n (selected)=\"selectMessage(message)\"\r\n (shared)=\"sharedMessage($event)\"\r\n ></banta-comment>\r\n <div class=\"banta-inline-replies-container\" *ngIf=\"selectedMessage === message\">\r\n <ng-content select=\".inline-replies\"></ng-content>\r\n </div>\r\n </ng-container>\r\n\r\n <div class=\"banta-nav-point banta-bottom-sticky\">\r\n <a *ngIf=\"newestLast\" mat-button class=\"nav\" [class.visible]=\"shouldShowNewMessageIndicator\" href=\"javascript:;\" (click)=\"showNew($event)\">\r\n <mat-icon>file_download</mat-icon>\r\n <ng-container *ngIf=\"newMessages.length >= 1\">\r\n New ({{newMessages.length}})\r\n </ng-container>\r\n <ng-container *ngIf=\"newMessages.length == 0\">\r\n Newest\r\n </ng-container>\r\n </a>\r\n </div>\r\n <a mat-button class=\"banta-nav\" [class.visible]=\"!newestLast && hasMore && !isLoadingMore\" href=\"javascript:;\" (click)=\"showMore()\">Show more</a>\r\n\r\n <div class=\"banta-loading-more\" *ngIf=\"isLoadingMore\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n\r\n <!-- <div style=\"color: #666\">\r\n n={{newMessages.length}}, m={{messages.length}}, o={{olderMessages.length}},\r\n v={{maxVisibleMessages}}, M={{maxMessages}}\r\n </div> -->\r\n\r\n <ng-content select=\":not([data-before]):not(.inline-replies)\"></ng-content>\r\n</div>\r\n", styles: [":host{flex-grow:1;display:flex;flex-direction:column;opacity:1;transition:.2s opacity ease-in}.banta-message-container{flex-grow:1;color:#111;background:#fff;padding:.5em 1em 3em .5em;opacity:1;transition:.5s opacity ease-in-out;position:relative}.banta-message-container.no-scroll{height:auto;overflow-y:visible}.banta-message-container.faded{opacity:.25}.banta-message-container .overlay{position:absolute;inset:0;z-index:10}:host.fixed-height .banta-message-container{overflow-y:auto}:host-context(.mat-dark-theme) .banta-message-container{color:#fff;background:#111}.banta-empty-state{text-align:center;margin:3em;color:#666}:host-context(.mat-dark-theme) .empty-state{color:#666}a.banta-nav{position:absolute;right:.5em;z-index:10;text-align:center;opacity:0;transition:.4s opacity ease-in-out;pointer-events:none;border-radius:2em;background:#222}a.banta-nav.visible{opacity:1;pointer-events:initial}.banta-top-sticky{position:sticky;top:.5em;z-index:10}.banta-bottom-sticky{position:sticky;bottom:3em;z-index:10}.banta-loading-more{padding:2em;text-align:center;margin:0 auto;width:fit-content}@media (max-width: 400px){.banta-message-container{padding:0 0 3em}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i6.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i8.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: CommentComponent, selector: "banta-comment", inputs: ["message", "customMenuItems", "showReplyAction", "maxLength", "permissions", "mine", "editing", "genericAvatarUrl", "readonly"], outputs: ["liked", "unliked", "selected", "edited", "deleted", "editStarted", "editEnded", "shared", "userSelected", "usernameSelected", "avatarSelected", "reported", "loaded"] }] }); }
8220
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.9", type: CommentViewComponent, selector: "banta-comment-view", inputs: { source: "source", maxMessages: "maxMessages", maxVisibleMessages: "maxVisibleMessages", newestLast: "newestLast", holdNewMessages: "holdNewMessages", showEmptyState: "showEmptyState", allowReplies: "allowReplies", enableHoldOnClick: "enableHoldOnClick", enableHoldOnScroll: "enableHoldOnScroll", customMenuItems: "customMenuItems", fixedHeight: "fixedHeight", selectedMessage: "selectedMessage", genericAvatarUrl: "genericAvatarUrl" }, outputs: { userSelected: "userSelected", reported: "reported", liked: "liked", unliked: "unliked", usernameSelected: "usernameSelected", avatarSelected: "avatarSelected", shared: "shared", deleted: "deleted", selected: "selected", messageEdited: "messageEdited", sortOrderChanged: "sortOrderChanged", filterModeChanged: "filterModeChanged" }, host: { properties: { "class.fixed-height": "this.fixedHeight" } }, viewQueries: [{ propertyName: "messageContainer", first: true, predicate: ["messageContainer"], descendants: true }, { propertyName: "commentsQuery", predicate: CommentComponent, descendants: true }], ngImport: i0, template: "<div class=\"banta-message-container\" #messageContainer>\r\n <ng-content select=\"[data-before]\"></ng-content>\r\n\r\n <div class=\"banta-top-sticky\">\r\n @if (!newestLast) {\r\n <button \r\n mat-button \r\n class=\"banta-nav\" \r\n [class.visible]=\"shouldShowNewMessageIndicator\" \r\n href=\"javascript:;\" \r\n (click)=\"showNewest($event)\"\r\n >\r\n <mat-icon>file_upload</mat-icon>\r\n Newest\r\n @if (heldMessages.length > 0) {\r\n <span class=\"count\">{{ heldMessages.length | number }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <button mat-button class=\"pager\" (click)=\"showPrevious()\" [class.visible]=\"shouldShowPrevious\" [disabled]=\"isLoadingMore\">\r\n <mat-icon>expand_less</mat-icon>\r\n {{ previousLabel }}\r\n </button>\r\n\r\n @for (message of messages; track message.id) {\r\n @if (!message.hidden) {\r\n <banta-comment\r\n class=\"abbreviated\"\r\n \r\n [customMenuItems]=\"customMenuItems\"\r\n [message]=\"message\"\r\n [mine]=\"currentUser?.id === message.user?.id\"\r\n [permissions]=\"source?.permissions\"\r\n [showReplyAction]=\"allowReplies\"\r\n [editing]=\"message.transientState.editing\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n [readonly]=\"source?.readonly\"\r\n (click)=\"enableHoldOnClick ? (holdNewMessages = true) : undefined\"\r\n (editStarted)=\"startEditing(message)\"\r\n (deleted)=\"deleteMessage(message)\"\r\n (editEnded)=\"message.transientState.editing = false\"\r\n (edited)=\"saveEdit(message, $event)\"\r\n (userSelected)=\"selectMessageUser(message)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (liked)=\"likeMessage(message)\"\r\n (unliked)=\"unlikeMessage(message)\"\r\n (reported)=\"reportMessage(message)\"\r\n (selected)=\"selectMessage(message)\"\r\n (shared)=\"sharedMessage($event)\"\r\n />\r\n <div class=\"banta-inline-replies-container\" *ngIf=\"selectedMessage === message\">\r\n <ng-content select=\".inline-replies\"></ng-content>\r\n </div>\r\n }\r\n } @empty {\r\n <div class=\"banta-empty-state\" *ngIf=\"showEmptyState\">\r\n Be the first to comment!\r\n </div>\r\n }\r\n\r\n <button mat-button class=\"pager\" (click)=\"showNext()\" [class.visible]=\"shouldShowNext\" [disabled]=\"isLoadingMore\">\r\n <mat-icon>expand_more</mat-icon>\r\n {{ nextLabel }}\r\n </button>\r\n\r\n <div class=\"banta-nav-point banta-bottom-sticky\">\r\n @if (newestLast) {\r\n <button \r\n [matBadge]=\"10\" matBadgeOverlap=\"false\"\r\n matBadgePosition=\"after\" matBadgeSize=\"large\" \r\n mat-button \r\n class=\"banta-nav\" \r\n [class.visible]=\"shouldShowNewMessageIndicator\" \r\n href=\"javascript:;\" \r\n (click)=\"showNewest($event)\"\r\n >\r\n <mat-icon>file_download</mat-icon>\r\n Newest\r\n @if (heldMessages.length > 0) {\r\n <span class=\"count\">{{ heldMessages.length | number }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <div class=\"banta-loading-more\" *ngIf=\"isLoadingMore\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n\r\n @if (showDebug) {\r\n <div style=\"color: #666\">\r\n ({{ previousMessages.length }} .. {{ messages.length }} .. {{ nextMessages.length }})\r\n\r\n dir={{newestLast ? '-1' : '1'}}\r\n v={{maxVisibleMessages}}, M={{maxMessages}}\r\n </div>\r\n }\r\n\r\n <ng-content select=\":not([data-before]):not(.inline-replies)\"></ng-content>\r\n</div>\r\n", styles: [":host{flex-grow:1;display:flex;flex-direction:column;opacity:1;transition:.2s opacity ease-in}.banta-message-container{flex-grow:1;color:#111;background:#fff;padding:.5em 1em 3em .5em;opacity:1;transition:.5s opacity ease-in-out;position:relative}.banta-message-container.no-scroll{height:auto;overflow-y:visible}.banta-message-container.faded{opacity:.25}.banta-message-container .overlay{position:absolute;inset:0;z-index:10}:host.fixed-height .banta-message-container{overflow-y:auto}:host-context(.mat-dark-theme) .banta-message-container{color:#fff;background:#111}.banta-empty-state{text-align:center;margin:3em;color:#666}:host-context(.mat-dark-theme) .empty-state{color:#666}button.banta-nav{position:absolute;right:.5em;z-index:10;text-align:center;opacity:0;transition:.4s opacity ease-in-out;pointer-events:none;border-radius:2em;background-color:#ddd}:host-context(.mat-dark-theme) button.banta-nav{background-color:#222;color:#fff}button.banta-nav span.count{background-color:#a93535;color:#fff;padding:4px 10px;border-radius:.5em;margin-left:.25em;font-size:90%}button.banta-nav.visible{opacity:1;pointer-events:initial}button.pager{appearance:none;border:none;width:100%;opacity:0;pointer-events:none;transition:.4s opacity ease-in-out}button.pager.visible{opacity:1;pointer-events:initial}.banta-top-sticky{position:sticky;top:.5em;z-index:10}.banta-bottom-sticky{position:sticky;bottom:3em;z-index:10}.banta-loading-more{padding:2em;text-align:center;margin:0 auto;width:fit-content}@media (max-width: 400px){.banta-message-container{padding:0 0 3em}}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i6.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i8.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: CommentComponent, selector: "banta-comment", inputs: ["message", "customMenuItems", "showReplyAction", "maxLength", "permissions", "mine", "editing", "genericAvatarUrl", "readonly"], outputs: ["liked", "unliked", "selected", "edited", "deleted", "editStarted", "editEnded", "shared", "userSelected", "usernameSelected", "avatarSelected", "reported", "loaded"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
7984
8221
  }
7985
8222
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: CommentViewComponent, decorators: [{
7986
8223
  type: Component,
7987
- args: [{ selector: 'banta-comment-view', template: "<div class=\"banta-message-container\" #messageContainer>\r\n <ng-content select=\"[data-before]\"></ng-content>\r\n\r\n <div class=\"banta-top-sticky\">\r\n <a *ngIf=\"!newestLast\" mat-button class=\"nav\" [class.visible]=\"shouldShowNewMessageIndicator\" href=\"javascript:;\" (click)=\"showNew($event)\">\r\n <mat-icon>file_upload</mat-icon>\r\n <ng-container *ngIf=\"newMessages.length >= 1\">\r\n New ({{newMessages.length}})\r\n </ng-container>\r\n <ng-container *ngIf=\"newMessages.length == 0\">\r\n Newest\r\n </ng-container>\r\n </a>\r\n </div>\r\n <a mat-button class=\"nav\" [class.visible]=\"newestLast && hasMore && !isLoadingMore\" href=\"javascript:;\" (click)=\"showMore()\">Show earlier</a>\r\n\r\n <ng-container *ngIf=\"messages.length === 0\">\r\n <div class=\"banta-empty-state\" *ngIf=\"showEmptyState\">\r\n Be the first to comment!\r\n </div>\r\n </ng-container>\r\n <ng-container *ngFor=\"let message of messages; trackBy: messageIdentity\">\r\n <banta-comment\r\n *ngIf=\"!message.hidden\"\r\n class=\"abbreviated\"\r\n \r\n [customMenuItems]=\"customMenuItems\"\r\n [message]=\"message\"\r\n [mine]=\"currentUser?.id === message.user?.id\"\r\n [permissions]=\"source?.permissions\"\r\n [showReplyAction]=\"allowReplies\"\r\n [editing]=\"message.transientState.editing\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n [readonly]=\"source?.readonly\"\r\n (click)=\"enableHoldOnClick ? (holdNewMessages = true) : undefined\"\r\n (editStarted)=\"startEditing(message)\"\r\n (deleted)=\"deleteMessage(message)\"\r\n (editEnded)=\"message.transientState.editing = false\"\r\n (edited)=\"saveEdit(message, $event)\"\r\n (userSelected)=\"selectMessageUser(message)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (liked)=\"likeMessage(message)\"\r\n (unliked)=\"unlikeMessage(message)\"\r\n (reported)=\"reportMessage(message)\"\r\n (selected)=\"selectMessage(message)\"\r\n (shared)=\"sharedMessage($event)\"\r\n ></banta-comment>\r\n <div class=\"banta-inline-replies-container\" *ngIf=\"selectedMessage === message\">\r\n <ng-content select=\".inline-replies\"></ng-content>\r\n </div>\r\n </ng-container>\r\n\r\n <div class=\"banta-nav-point banta-bottom-sticky\">\r\n <a *ngIf=\"newestLast\" mat-button class=\"nav\" [class.visible]=\"shouldShowNewMessageIndicator\" href=\"javascript:;\" (click)=\"showNew($event)\">\r\n <mat-icon>file_download</mat-icon>\r\n <ng-container *ngIf=\"newMessages.length >= 1\">\r\n New ({{newMessages.length}})\r\n </ng-container>\r\n <ng-container *ngIf=\"newMessages.length == 0\">\r\n Newest\r\n </ng-container>\r\n </a>\r\n </div>\r\n <a mat-button class=\"banta-nav\" [class.visible]=\"!newestLast && hasMore && !isLoadingMore\" href=\"javascript:;\" (click)=\"showMore()\">Show more</a>\r\n\r\n <div class=\"banta-loading-more\" *ngIf=\"isLoadingMore\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n\r\n <!-- <div style=\"color: #666\">\r\n n={{newMessages.length}}, m={{messages.length}}, o={{olderMessages.length}},\r\n v={{maxVisibleMessages}}, M={{maxMessages}}\r\n </div> -->\r\n\r\n <ng-content select=\":not([data-before]):not(.inline-replies)\"></ng-content>\r\n</div>\r\n", styles: [":host{flex-grow:1;display:flex;flex-direction:column;opacity:1;transition:.2s opacity ease-in}.banta-message-container{flex-grow:1;color:#111;background:#fff;padding:.5em 1em 3em .5em;opacity:1;transition:.5s opacity ease-in-out;position:relative}.banta-message-container.no-scroll{height:auto;overflow-y:visible}.banta-message-container.faded{opacity:.25}.banta-message-container .overlay{position:absolute;inset:0;z-index:10}:host.fixed-height .banta-message-container{overflow-y:auto}:host-context(.mat-dark-theme) .banta-message-container{color:#fff;background:#111}.banta-empty-state{text-align:center;margin:3em;color:#666}:host-context(.mat-dark-theme) .empty-state{color:#666}a.banta-nav{position:absolute;right:.5em;z-index:10;text-align:center;opacity:0;transition:.4s opacity ease-in-out;pointer-events:none;border-radius:2em;background:#222}a.banta-nav.visible{opacity:1;pointer-events:initial}.banta-top-sticky{position:sticky;top:.5em;z-index:10}.banta-bottom-sticky{position:sticky;bottom:3em;z-index:10}.banta-loading-more{padding:2em;text-align:center;margin:0 auto;width:fit-content}@media (max-width: 400px){.banta-message-container{padding:0 0 3em}}\n"] }]
8224
+ args: [{ selector: 'banta-comment-view', template: "<div class=\"banta-message-container\" #messageContainer>\r\n <ng-content select=\"[data-before]\"></ng-content>\r\n\r\n <div class=\"banta-top-sticky\">\r\n @if (!newestLast) {\r\n <button \r\n mat-button \r\n class=\"banta-nav\" \r\n [class.visible]=\"shouldShowNewMessageIndicator\" \r\n href=\"javascript:;\" \r\n (click)=\"showNewest($event)\"\r\n >\r\n <mat-icon>file_upload</mat-icon>\r\n Newest\r\n @if (heldMessages.length > 0) {\r\n <span class=\"count\">{{ heldMessages.length | number }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <button mat-button class=\"pager\" (click)=\"showPrevious()\" [class.visible]=\"shouldShowPrevious\" [disabled]=\"isLoadingMore\">\r\n <mat-icon>expand_less</mat-icon>\r\n {{ previousLabel }}\r\n </button>\r\n\r\n @for (message of messages; track message.id) {\r\n @if (!message.hidden) {\r\n <banta-comment\r\n class=\"abbreviated\"\r\n \r\n [customMenuItems]=\"customMenuItems\"\r\n [message]=\"message\"\r\n [mine]=\"currentUser?.id === message.user?.id\"\r\n [permissions]=\"source?.permissions\"\r\n [showReplyAction]=\"allowReplies\"\r\n [editing]=\"message.transientState.editing\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n [readonly]=\"source?.readonly\"\r\n (click)=\"enableHoldOnClick ? (holdNewMessages = true) : undefined\"\r\n (editStarted)=\"startEditing(message)\"\r\n (deleted)=\"deleteMessage(message)\"\r\n (editEnded)=\"message.transientState.editing = false\"\r\n (edited)=\"saveEdit(message, $event)\"\r\n (userSelected)=\"selectMessageUser(message)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (liked)=\"likeMessage(message)\"\r\n (unliked)=\"unlikeMessage(message)\"\r\n (reported)=\"reportMessage(message)\"\r\n (selected)=\"selectMessage(message)\"\r\n (shared)=\"sharedMessage($event)\"\r\n />\r\n <div class=\"banta-inline-replies-container\" *ngIf=\"selectedMessage === message\">\r\n <ng-content select=\".inline-replies\"></ng-content>\r\n </div>\r\n }\r\n } @empty {\r\n <div class=\"banta-empty-state\" *ngIf=\"showEmptyState\">\r\n Be the first to comment!\r\n </div>\r\n }\r\n\r\n <button mat-button class=\"pager\" (click)=\"showNext()\" [class.visible]=\"shouldShowNext\" [disabled]=\"isLoadingMore\">\r\n <mat-icon>expand_more</mat-icon>\r\n {{ nextLabel }}\r\n </button>\r\n\r\n <div class=\"banta-nav-point banta-bottom-sticky\">\r\n @if (newestLast) {\r\n <button \r\n [matBadge]=\"10\" matBadgeOverlap=\"false\"\r\n matBadgePosition=\"after\" matBadgeSize=\"large\" \r\n mat-button \r\n class=\"banta-nav\" \r\n [class.visible]=\"shouldShowNewMessageIndicator\" \r\n href=\"javascript:;\" \r\n (click)=\"showNewest($event)\"\r\n >\r\n <mat-icon>file_download</mat-icon>\r\n Newest\r\n @if (heldMessages.length > 0) {\r\n <span class=\"count\">{{ heldMessages.length | number }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <div class=\"banta-loading-more\" *ngIf=\"isLoadingMore\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n\r\n @if (showDebug) {\r\n <div style=\"color: #666\">\r\n ({{ previousMessages.length }} .. {{ messages.length }} .. {{ nextMessages.length }})\r\n\r\n dir={{newestLast ? '-1' : '1'}}\r\n v={{maxVisibleMessages}}, M={{maxMessages}}\r\n </div>\r\n }\r\n\r\n <ng-content select=\":not([data-before]):not(.inline-replies)\"></ng-content>\r\n</div>\r\n", styles: [":host{flex-grow:1;display:flex;flex-direction:column;opacity:1;transition:.2s opacity ease-in}.banta-message-container{flex-grow:1;color:#111;background:#fff;padding:.5em 1em 3em .5em;opacity:1;transition:.5s opacity ease-in-out;position:relative}.banta-message-container.no-scroll{height:auto;overflow-y:visible}.banta-message-container.faded{opacity:.25}.banta-message-container .overlay{position:absolute;inset:0;z-index:10}:host.fixed-height .banta-message-container{overflow-y:auto}:host-context(.mat-dark-theme) .banta-message-container{color:#fff;background:#111}.banta-empty-state{text-align:center;margin:3em;color:#666}:host-context(.mat-dark-theme) .empty-state{color:#666}button.banta-nav{position:absolute;right:.5em;z-index:10;text-align:center;opacity:0;transition:.4s opacity ease-in-out;pointer-events:none;border-radius:2em;background-color:#ddd}:host-context(.mat-dark-theme) button.banta-nav{background-color:#222;color:#fff}button.banta-nav span.count{background-color:#a93535;color:#fff;padding:4px 10px;border-radius:.5em;margin-left:.25em;font-size:90%}button.banta-nav.visible{opacity:1;pointer-events:initial}button.pager{appearance:none;border:none;width:100%;opacity:0;pointer-events:none;transition:.4s opacity ease-in-out}button.pager.visible{opacity:1;pointer-events:initial}.banta-top-sticky{position:sticky;top:.5em;z-index:10}.banta-bottom-sticky{position:sticky;bottom:3em;z-index:10}.banta-loading-more{padding:2em;text-align:center;margin:0 auto;width:fit-content}@media (max-width: 400px){.banta-message-container{padding:0 0 3em}}\n"] }]
7988
8225
  }], ctorParameters: () => [{ type: ChatBackendBase }, { type: i0.ElementRef }], propDecorators: { source: [{
7989
8226
  type: Input
7990
8227
  }], maxMessages: [{
@@ -8292,7 +8529,7 @@ class CommentFieldComponent {
8292
8529
  return false;
8293
8530
  if (this.signInState === 'signing-in')
8294
8531
  return false;
8295
- if (!['connected', 'restored'].includes(this.source.state))
8532
+ if (!['connected', 'restored'].includes(this.source?.state ?? 'connected'))
8296
8533
  return false;
8297
8534
  if (!this.canComment) {
8298
8535
  // In this case, we want to enable the button because we want to be able to
@@ -8873,8 +9110,13 @@ class BantaCommentsComponent {
8873
9110
  await this.threadViewQuery.changes.pipe(take(1)).toPromise();
8874
9111
  return this.threadView;
8875
9112
  }
9113
+ get sourceState() {
9114
+ if (!this.source)
9115
+ return 'no-source';
9116
+ return this.source.state ?? 'connected';
9117
+ }
8876
9118
  updateLoading() {
8877
- if (this.source?.state && !['connecting', 'lost'].includes(this.source?.state)) {
9119
+ if (this.sourceState && !['connecting', 'lost'].includes(this.sourceState)) {
8878
9120
  clearInterval(this._loadingTimer);
8879
9121
  this.loadingMessage = `Here we go!`;
8880
9122
  setTimeout(() => {
@@ -8883,7 +9125,7 @@ class BantaCommentsComponent {
8883
9125
  }, 750);
8884
9126
  return true;
8885
9127
  }
8886
- console.log(`[Banta/Loader] State=${this.source ? this.source.state : 'no-source'}`);
9128
+ console.log(`[Banta/Loader] State=${this.sourceState}`);
8887
9129
  let messageSwitchTime = 5 * 1000;
8888
9130
  if (this.messageChangedAt + messageSwitchTime < Date.now()) {
8889
9131
  if (this.loadingMessages[this._loadingMessageIndex]) {
@@ -8911,7 +9153,10 @@ class BantaCommentsComponent {
8911
9153
  this.sharedCommentID = null;
8912
9154
  }
8913
9155
  this._source.messages.forEach(m => this.addParticipant(m));
8914
- this._sourceSubscription.add(this._source.connectionStateChanged.subscribe(state => this.connectionState = state));
9156
+ if (this._source.connectionStateChanged)
9157
+ this._sourceSubscription.add(this._source.connectionStateChanged.subscribe(state => this.connectionState = state));
9158
+ else
9159
+ this.connectionState = 'connected';
8915
9160
  this._sourceSubscription.add(this._source.messageReceived.subscribe(m => this.addParticipant(m)));
8916
9161
  this._sourceSubscription.add(this._source.messageSent.subscribe(m => this.addParticipant(m)));
8917
9162
  this._sourceSubscription.add(this._source.messageObserved.subscribe(m => this.addParticipant(m)));
@@ -10916,12 +11161,13 @@ class TweetAttachmentResolver {
10916
11161
  }
10917
11162
 
10918
11163
  class BantaSdkModule {
10919
- constructor(chatBackend) {
10920
- chatBackend.registerAttachmentScraper(new UrlAttachmentScraper());
10921
- chatBackend.registerAttachmentResolver(new GiphyAttachmentResolver());
10922
- chatBackend.registerAttachmentResolver(new YouTubeAttachmentResolver());
10923
- chatBackend.registerAttachmentResolver(new TweetAttachmentResolver());
10924
- chatBackend.registerAttachmentResolver(new UrlAttachmentResolver(chatBackend));
11164
+ constructor() {
11165
+ this.chatBackend = inject(ChatBackendBase);
11166
+ this.chatBackend.registerAttachmentScraper(new UrlAttachmentScraper());
11167
+ this.chatBackend.registerAttachmentResolver(new GiphyAttachmentResolver());
11168
+ this.chatBackend.registerAttachmentResolver(new YouTubeAttachmentResolver());
11169
+ this.chatBackend.registerAttachmentResolver(new TweetAttachmentResolver());
11170
+ this.chatBackend.registerAttachmentResolver(new UrlAttachmentResolver(this.chatBackend));
10925
11171
  }
10926
11172
  static configure(options) {
10927
11173
  return {
@@ -10931,11 +11177,11 @@ class BantaSdkModule {
10931
11177
  provide: BANTA_SDK_OPTIONS,
10932
11178
  useValue: options || {}
10933
11179
  },
10934
- { provide: ChatBackendBase, useClass: ChatBackend }
11180
+ { provide: ChatBackendBase, useClass: options?.backendClass ?? ChatBackend }
10935
11181
  ]
10936
11182
  };
10937
11183
  }
10938
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: BantaSdkModule, deps: [{ token: ChatBackendBase }], target: i0.ɵɵFactoryTarget.NgModule }); }
11184
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: BantaSdkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
10939
11185
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.9", ngImport: i0, type: BantaSdkModule, declarations: [BantaComponent,
10940
11186
  BantaLogoComponent,
10941
11187
  LiveMessageComponent], imports: [CommonModule,
@@ -11017,7 +11263,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
11017
11263
  EmojiModule
11018
11264
  ]
11019
11265
  }]
11020
- }], ctorParameters: () => [{ type: ChatBackendBase }] });
11266
+ }], ctorParameters: () => [] });
11021
11267
 
11022
11268
  /*
11023
11269
  * Public API Surface of sdk