@banta/sdk 5.5.3 → 5.6.0

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,192 @@ 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
+ this.hasMore = this.nextMessages.length > 0;
8014
+ }
8015
+ const lastMessage = this.olderMessages[this.olderMessages.length - 1] ?? this.messages[this.messages.length - 1];
8016
+ if (!lastMessage)
7865
8017
  this.hasMore = false;
8018
+ if (nextPageSize > 0 && !this.newestLast && lastMessage) {
8019
+ // Load more from backend
8020
+ this.isLoadingMore = true;
8021
+ let messages = await this.source.loadAfter(lastMessage, nextPageSize);
8022
+ messages.forEach(m => m.transientState ??= {});
8023
+ this.messages = [...this.messages, ...messages];
8024
+ // If we didn't receive any messages at all, there's no more to fetch.
8025
+ if (messages.length === 0)
8026
+ this.hasMore = false;
8027
+ this.isLoadingMore = false;
8028
+ }
8029
+ // Extract the messages that do not fit in the maxVisibleMessages buffer.
8030
+ if (this.messages.length > this.maxVisibleMessages) {
8031
+ let overflow = this.messages.splice(0, this.messages.length - this.maxVisibleMessages);
8032
+ // Regardless of the order (newestLast), newMessages represents the direction that is being pushed, since it's definition
8033
+ // depends on that order. Move overflowing messages into newMessages.
8034
+ this.previousMessages.push(...overflow);
8035
+ if (this.previousMessages.length > this.maxMessages)
8036
+ this.previousMessages.splice(0, this.previousMessages.length - this.maxMessages);
7866
8037
  }
8038
+ this.debugMessages();
8039
+ }
8040
+ /**
8041
+ * Show more content
8042
+ * - When in replies mode (newestLast), the content is added at the top
8043
+ * - When in normal mode, the content is added at the bottom
8044
+ * - The current sort order does *not* factor in here, which is why it is showMore() not showEarlier().
8045
+ *
8046
+ * @returns
8047
+ */
8048
+ async showMore() {
8049
+ this.isViewingMore = true;
8050
+ let nextPageSize = this.pageSize;
8051
+ this.isLoadingMore = false;
8052
+ if (this.olderMessages.length > 0) {
8053
+ const storedMessages = this.olderMessages.splice(0, nextPageSize);
8054
+ this.messages = this.messages.concat(storedMessages);
8055
+ nextPageSize -= storedMessages.length;
8056
+ this.hasMore = this.olderMessages.length > 0;
8057
+ }
8058
+ if (nextPageSize > 0) {
8059
+ // Load more from backend
8060
+ this.isLoadingMore = true;
8061
+ let lastMessage;
8062
+ if (this.newestLast) {
8063
+ lastMessage = this.olderMessages[0] ?? this.messages[0];
8064
+ }
8065
+ else {
8066
+ lastMessage = this.olderMessages[this.olderMessages.length - 1] ?? this.messages[this.messages.length - 1];
8067
+ }
8068
+ if (!lastMessage) {
8069
+ this.isLoadingMore = false;
8070
+ this.hasMore = false;
8071
+ return;
8072
+ }
8073
+ let messages = await this.source.loadAfter(lastMessage, nextPageSize);
8074
+ if (this.newestLast)
8075
+ messages = messages.slice().reverse();
8076
+ messages.forEach(m => m.transientState ??= {});
8077
+ // In replies mode (newestLast), we want to put these new messages onto the *top* of the set of visible messages.
8078
+ // Otherwise we want to put them on the *bottom*.
8079
+ if (this.newestLast) {
8080
+ this.messages = messages.concat(this.messages);
8081
+ }
8082
+ else {
8083
+ this.messages = this.messages.concat(messages);
8084
+ }
8085
+ // If we didn't receive any messages at all, there's no more to fetch.
8086
+ if (messages.length === 0) {
8087
+ this.hasMore = false;
8088
+ }
8089
+ this.isLoadingMore = false;
8090
+ }
8091
+ // Extract the messages that do not fit in the maxVisibleMessages buffer.
8092
+ let overflow;
8093
+ if (this.newestLast)
8094
+ overflow = this.messages.splice(this.maxVisibleMessages, this.messages.length);
8095
+ else
8096
+ overflow = this.messages.splice(0, this.maxVisibleMessages);
8097
+ // Regardless of the order (newestLast), newMessages represents the direction that is being pushed, since it's definition
8098
+ // depends on that order. Move overflowing messages into newMessages.
8099
+ this.newMessages = overflow.concat(this.newMessages);
8100
+ this.newMessages.splice(this.maxMessages - this.maxVisibleMessages, this.newMessages.length);
7867
8101
  }
7868
8102
  addMessage(message) {
7869
8103
  if (!message.transientState)
@@ -7872,9 +8106,13 @@ class CommentViewComponent {
7872
8106
  let bucket = this.olderMessages;
7873
8107
  let newestLast = this.newestLast;
7874
8108
  if (this.shouldHoldNewMessages) {
8109
+ this.heldMessages.push(message);
7875
8110
  destination = this.newMessages;
7876
8111
  bucket = null;
7877
8112
  }
8113
+ // If we aren't on the newest sort order, new messages shouldn't be added at all
8114
+ if (this.sourceSortOrder !== CommentsOrder.NEWEST)
8115
+ return;
7878
8116
  if (newestLast) {
7879
8117
  destination.push(message);
7880
8118
  let overflow = destination.splice(this.maxVisibleMessages, destination.length);
@@ -7980,11 +8218,11 @@ class CommentViewComponent {
7980
8218
  return false;
7981
8219
  }
7982
8220
  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"] }] }); }
8221
+ 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
8222
  }
7985
8223
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: CommentViewComponent, decorators: [{
7986
8224
  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"] }]
8225
+ 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
8226
  }], ctorParameters: () => [{ type: ChatBackendBase }, { type: i0.ElementRef }], propDecorators: { source: [{
7989
8227
  type: Input
7990
8228
  }], maxMessages: [{
@@ -8292,7 +8530,7 @@ class CommentFieldComponent {
8292
8530
  return false;
8293
8531
  if (this.signInState === 'signing-in')
8294
8532
  return false;
8295
- if (!['connected', 'restored'].includes(this.source.state))
8533
+ if (!['connected', 'restored'].includes(this.source?.state ?? 'connected'))
8296
8534
  return false;
8297
8535
  if (!this.canComment) {
8298
8536
  // In this case, we want to enable the button because we want to be able to
@@ -8873,8 +9111,13 @@ class BantaCommentsComponent {
8873
9111
  await this.threadViewQuery.changes.pipe(take(1)).toPromise();
8874
9112
  return this.threadView;
8875
9113
  }
9114
+ get sourceState() {
9115
+ if (!this.source)
9116
+ return 'no-source';
9117
+ return this.source.state ?? 'connected';
9118
+ }
8876
9119
  updateLoading() {
8877
- if (this.source?.state && !['connecting', 'lost'].includes(this.source?.state)) {
9120
+ if (this.sourceState && !['connecting', 'lost'].includes(this.sourceState)) {
8878
9121
  clearInterval(this._loadingTimer);
8879
9122
  this.loadingMessage = `Here we go!`;
8880
9123
  setTimeout(() => {
@@ -8883,7 +9126,7 @@ class BantaCommentsComponent {
8883
9126
  }, 750);
8884
9127
  return true;
8885
9128
  }
8886
- console.log(`[Banta/Loader] State=${this.source ? this.source.state : 'no-source'}`);
9129
+ console.log(`[Banta/Loader] State=${this.sourceState}`);
8887
9130
  let messageSwitchTime = 5 * 1000;
8888
9131
  if (this.messageChangedAt + messageSwitchTime < Date.now()) {
8889
9132
  if (this.loadingMessages[this._loadingMessageIndex]) {
@@ -8911,7 +9154,10 @@ class BantaCommentsComponent {
8911
9154
  this.sharedCommentID = null;
8912
9155
  }
8913
9156
  this._source.messages.forEach(m => this.addParticipant(m));
8914
- this._sourceSubscription.add(this._source.connectionStateChanged.subscribe(state => this.connectionState = state));
9157
+ if (this._source.connectionStateChanged)
9158
+ this._sourceSubscription.add(this._source.connectionStateChanged.subscribe(state => this.connectionState = state));
9159
+ else
9160
+ this.connectionState = 'connected';
8915
9161
  this._sourceSubscription.add(this._source.messageReceived.subscribe(m => this.addParticipant(m)));
8916
9162
  this._sourceSubscription.add(this._source.messageSent.subscribe(m => this.addParticipant(m)));
8917
9163
  this._sourceSubscription.add(this._source.messageObserved.subscribe(m => this.addParticipant(m)));
@@ -10916,12 +11162,13 @@ class TweetAttachmentResolver {
10916
11162
  }
10917
11163
 
10918
11164
  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));
11165
+ constructor() {
11166
+ this.chatBackend = inject(ChatBackendBase);
11167
+ this.chatBackend.registerAttachmentScraper(new UrlAttachmentScraper());
11168
+ this.chatBackend.registerAttachmentResolver(new GiphyAttachmentResolver());
11169
+ this.chatBackend.registerAttachmentResolver(new YouTubeAttachmentResolver());
11170
+ this.chatBackend.registerAttachmentResolver(new TweetAttachmentResolver());
11171
+ this.chatBackend.registerAttachmentResolver(new UrlAttachmentResolver(this.chatBackend));
10925
11172
  }
10926
11173
  static configure(options) {
10927
11174
  return {
@@ -10931,11 +11178,11 @@ class BantaSdkModule {
10931
11178
  provide: BANTA_SDK_OPTIONS,
10932
11179
  useValue: options || {}
10933
11180
  },
10934
- { provide: ChatBackendBase, useClass: ChatBackend }
11181
+ { provide: ChatBackendBase, useClass: options?.backendClass ?? ChatBackend }
10935
11182
  ]
10936
11183
  };
10937
11184
  }
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 }); }
11185
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: BantaSdkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
10939
11186
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.9", ngImport: i0, type: BantaSdkModule, declarations: [BantaComponent,
10940
11187
  BantaLogoComponent,
10941
11188
  LiveMessageComponent], imports: [CommonModule,
@@ -11017,7 +11264,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImpor
11017
11264
  EmojiModule
11018
11265
  ]
11019
11266
  }]
11020
- }], ctorParameters: () => [{ type: ChatBackendBase }] });
11267
+ }], ctorParameters: () => [] });
11021
11268
 
11022
11269
  /*
11023
11270
  * Public API Surface of sdk