import { DOCUMENT } from "@angular/common";
import {
  Component,
  ElementRef,
  Inject,
  OnDestroy,
  OnInit,
  ViewChild,
} from "@angular/core";
import { MatDialog } from "@angular/material/dialog";
import {
  DragResourceEvent,
  MenuRecord,
} from "./resource-group/resource-group.component";
import {
  SidebarData,
  ResourceNode,
  ResourceGroupNode,
  ResourceType,
} from "./sidebar-data";
import { marker } from "@biesbjerg/ngx-translate-extract-marker";
import { DeleteGroupDialogComponent } from "src/app/shared/dialogs/delete-group-dialog/delete-group-dialog.component";
import { NewGroupDialogComponent } from "src/app/shared/dialogs/new-group-dialog/new-group-dialog.component";
import { RenameGroupDialogComponent } from "src/app/shared/dialogs/rename-group-dialog/rename-group-dialog.component";
import {
  AppDragDropServiceHelper,
  AppDragDropService,
  AppDragMoveEvent,
} from "src/app/shared/drag/app-drag-drop.service";
import { AccountService } from "src/app/shared/server-services/account.service";
import { DialogueService } from "src/app/shared/server-services/dialogue.service";
import {
  RoomRecord,
  DialogueDataRecord,
} from "src/app/shared/server-services/query-records/room-records";
import {
  WorkspaceSubscriptionResourceGroupEventRecord,
  WorkspaceSubscriptionRoomEventRecord,
  WorkspaceSubscriptionRoomPermissionEventRecord,
  WorkspaceSubscriptionRoomAccountPermissionEventRecord,
  WorkspaceSubscriptionAccountEventRecord,
  WorkspaceQueryRecord,
  WorkspaceQueryRoomRecord,
  WorkspaceQueryDialogueRecord,
  WorkspaceSubscriptionDialogueEventRecord,
  WorkspaceSubscriptionDialogueMessageEventRecord,
  WorkspaceSubscriptionRoomMessageEventRecord,
} from "src/app/shared/server-services/query-records/workspace-records";
import { ResourceGroupService } from "src/app/shared/server-services/resource-group.service";
import {
  RoomData,
  RoomService,
} from "src/app/shared/server-services/room.service";
import {
  SearchResult,
  SearchService,
} from "src/app/shared/server-services/search-service";
import {
  SubscriptionServiceEvent,
  SubscriptionServiceEventType,
} from "src/app/shared/server-services/subscription-event";
import { SubscriptionService } from "src/app/shared/server-services/subscription.service";
import { RouterHandler } from "src/app/shared/services/router-handler.service";
import { CreateRoomDialogComponent } from "src/app/shared/dialogs/create-room-dialog/create-room-dialog.component";
import { DialogService } from "src/app/shared/services/dialog.service";
import { Subject } from "rxjs";
import { AuthService } from "src/app/shared/server-services/auth.service";
import { AudioService, Sounds } from "src/app/shared/services/audio.service";
import { showWorkspaceTabObserver } from "../header/header.component";
import { CordovaService } from "src/app/shared/services/cordova.service";
import { getTimeFromId } from "src/app/shared/crypto/context/timestamp";
import { MeRecord } from "src/app/shared/server-services/query-records/account-records";

export let newsObserver = new Subject<number>();

@Component({
  selector: "app-resources-sidebar",
  templateUrl: "./resources-sidebar.component.html",
  styleUrls: ["./resources-sidebar.component.scss"],
})
export class ResourcesSidebarComponent implements OnInit, OnDestroy {
  // the whole workspace in an object
  public data: SidebarData;

  // rooms where you get an invitation
  public grantedRooms: ResourceNode[] = [];
  public rejectedRooms: ResourceNode[] = [];

  /**
   * pregenerated data object for the ungroup-group on the sidebar
   * Note: you cant use resources directly, use the "additionalResources" feature on the group component
   */
  public ungroupedData: ResourceGroupNode = {
    id: null,
    name: marker("Ungrouped"),
    closed: false,
    ordering: 1,
    error: false,
    resources: [],
  };

  // a group container for grantedRooms
  public grantedData: ResourceGroupNode = {
    id: null,
    name: "Granted Rooms", // TODO multi-language support
    closed: false,
    ordering: 1,
    error: false,
    resources: [],
  };

  public workspaceLoaded = false;

  /**
   * Passed dinamically generated popup menus and handlers for groups (click on settings icon)
   */
  public groupMenu: MenuRecord[] = [
    {
      title: marker("Rename"),
      icon: "edit",
      handler: (group) => {
        this.openEditResourceGroupDialog(group);
      },
    },
    {
      title: marker("Delete"),
      icon: "delete",
      handler: (group) => {
        this.openDeleteResorceGroupDialog(group);
      },
      primary: true,
    },
  ];

  public ungroupMenu: MenuRecord[] = [
    {
      title: marker("Create group"), // TODO multi language support
      icon: "add",
      handler: (group) => {
        this.openNewGroupDialog();
      },
    },
    // under refactor
    /*{
      title: 'Create resource', // TODO multi language support
      icon: 'add', 
      handler: (group)=>{ 
        this.openNewResourceDialog(group); // called with the pregenerated ungroup object
      }
    }*/
  ];

  /**
   * temp array, when you ask for the ordered list of the DATA
   */
  public orderedList: ResourceNode[] = null;

  /**
   * newsList
   */
  public newsList: ResourceNode[] = [];
  public newsNumber = 0;

  /**
   * We will collapse all of the group, when a drag happened on group level
   */
  public isGroupDragStarted: boolean = false;

  /**
   * It's a running time calculated height in px, this define the require space before
   * the elements, when a drag happening on group level. With this the scroller
   * won't jumping up or down.
   */
  public startPlaceFillerHeight = 0;

  /**
   * We fix the height of the container when drag happened on group level. This
   * compels the scroller to stay the same position
   */
  public expPanelHeight = "auto";

  /**
   * A handler connected to the dragMove event in AppDragDropService
   */
  public scrollMouseMoveHandler;

  /**
   * We want to autoscroll, when the dragged element waits over the beginning or ending
   * of the container. This timer defines this waiting via window.setTimeOut
   */
  private isCursorOnScrollPositionTimer; // for settimeout

  /**
   * We only need the component level facilities of the API
   */
  public dragDropService: AppDragDropServiceHelper;

  /**
   * Height of the groups in pixel when it is collapsed.
   */
  static GROUP_BOX_HEIGHT = 53;

  /**
   * The space in pixel where the auto scroller is triggered when you drag an element over it
   */
  static SCROLL_DISTANCE_SENSITIVITY = 100;

  /**
   * The space in pixel over and below of the container, when you drag and scroll an element to
   * the container's edges. It must be 0 or higher.
   */
  static SCROLL_OVERFLOW = 30;

  /**
   * The scroll height in pixel, when you drag an element to the container's edges
   */
  static SCROLL_SPEED = 50;

  /**
   * Defined css animation speed in angular:  EXPANSION_PANEL_ANIMATION_TIMING
   * We can not change it, so we have to syncronize our animations to it.
   * Note: When you have to change this, make sure you changed it in the css as well:
   * resources-diebar.component.css: mat-accordio, .start-place-filler
   */
  static ANGULAR_EXPANSION_PANEL_ANIMATION_SPEED = 225;

  /**
   * is this the first time, the sidebar is loaded
   */
  private initLoad: boolean = true;

  @ViewChild("expPanelContainer") expPanelNode: ElementRef;
  @ViewChild("expScrollWindow") expScrollWindow: ElementRef;

  public activePanel = "inorder";

  public me: MeRecord;

  // handlers for api-subscription ---------------------------------------------------
  // group events
  private onCreateResourceGroup = (
    data: WorkspaceSubscriptionResourceGroupEventRecord
  ) => {
    //console.log('server>client create group', data);

    let group = {
      id: data.id,
      name: <string>data.name,
      closed: data.closed,
      ordering: data.ordering,
      error: data.error,
      resources: [],
    };

    let pos = this.data.pushGroupBefore(group);

    if (this.isPositionNeedToOptimize(pos)) {
      this.optimizeGroupOrdering();
    }
  };

  private onDeleteResourceGroup = (
    data: WorkspaceSubscriptionResourceGroupEventRecord
  ) => {
    this.data.removeGroup(data.id);
  };

  private onEditResourceGroup = (
    data: WorkspaceSubscriptionResourceGroupEventRecord
  ) => {
    if ("name" in data) {
      this.data.editGroupName(data.id, data.name);
      //this.appRef.tick();
    }
    if ("ordering" in data) {
      this.data.editGroupOrdering(data.id, data.ordering);
    }
    if ("closed" in data) {
      this.data.editGroupClosed(data.id, data.closed);
    }

    if ("error" in data) {
      this.data.setGroupError(data.id, data.error === true);
    }
  };

  private getSidebarRoom(roomId): ResourceNode {
    let roomInData = this.data.getResourceById(roomId);
    if (roomInData) return roomInData;

    for (let i = 0; i < this.rejectedRooms.length; i++) {
      if (this.rejectedRooms[i].id == roomId) {
        return this.rejectedRooms[i];
      }
    }

    for (let i = 0; i < this.grantedRooms.length; i++) {
      if (this.grantedRooms[i].id == roomId) {
        return this.grantedRooms[i];
      }
    }
  }

  // resources
  private onCreateRoom = (data: WorkspaceSubscriptionRoomEventRecord) => {
    console.log("server>client create res", data);

    this.roomService.getData(data.id).then((data) => {
      console.log("create data", data);
      let resource = {
        id: data.id,
        name: (<RoomData>data.data)?.name,
        avatar: (<RoomData>data.data)?.avatar,
        owner: data.ownerAccountId,
        news: data.seenMessageId != data.topMessageId,
        lastSeenMessageId: data.seenMessageId,
        topMessageId: data.topMessageId,
        ordering: data.ordering,
        type: data.type,
        error: data.error,
        nanoSessions: {},
      };
      this.data.pushResource(null, resource);

      for (let i = 0; i < this.grantedRooms.length; i++) {
        if (this.grantedRooms[i].id == data.id) {
          this.grantedRooms.splice(i, 1);
          this.calcNews();
          return;
        }
      }
      for (let i = 0; i < this.rejectedRooms.length; i++) {
        if (this.rejectedRooms[i].id == data.id) {
          this.rejectedRooms.splice(i, 1);
          return;
        }
      }

      this.calcNews();
    });
  };

  // if you remove the room from your workspace
  private onDeleteRoom = (data: WorkspaceSubscriptionRoomEventRecord) => {
    console.log("server>client delete res", data);

    let roomData = this.data.getResourceById(data.id);
    if (roomData) {
      if (data.rejected) {
        // leaved room
        this.rejectedRooms.push(roomData);
      }
      this.data.removeResource(data.id);

      this.calcNews();
    } else {
      // deleted room, not just leaved
      if (!data.rejected) return;

      for (let i = 0; i < this.grantedRooms.length; i++) {
        if (this.grantedRooms[i].id == data.id) {
          this.grantedRooms.splice(i, 1);
          break;
        }
      }

      // check if it is already in the list (for client duplicated leave or something)
      for (let i = 0; i < this.rejectedRooms.length; i++) {
        if (this.rejectedRooms[i].id == data.id) {
          // it is already in the list, so there is nothing to do
          return;
        }
      }

      this.roomService.getData(data.id).then((roomData) => {
        this.rejectedRooms.push({
          id: roomData.id,
          name: (<RoomData>roomData.data)?.name,
          avatar: (<RoomData>roomData.data)?.avatar,
          owner: roomData.ownerAccountId,
          news: roomData.seenMessageId != roomData.topMessageId,
          lastSeenMessageId: roomData.seenMessageId,
          topMessageId: roomData.topMessageId,
          ordering: roomData.ordering,
          type: roomData.type,
          error: roomData.error,
          nanoSessions: roomData.nanoSessions,
        });
      });
    }
  };

  private onEditRoom = (
    data:
      | WorkspaceSubscriptionRoomEventRecord
      | WorkspaceSubscriptionDialogueEventRecord
  ) => {
    let roomNode = this.getSidebarRoom(data.id);
    if (roomNode) {
      console.log("server>client edit res", data);
      // we will merge these callbacks, you will have ordering and groupId

      if ("ordering" in data) {
        let groupId = this.data.getResourceParentIdByResourceId(data.id);

        this.data.editResourceOrdering(data.id, groupId, data.ordering);
      }

      if ("groupId" in data) {
        let resource = this.data.getResourceById(data.id);

        this.data.editResourceOrdering(
          data.id,
          data.groupId,
          resource.ordering
        );
      }

      if ("seenMessageId" in data) {
        //roomNode.lastSeenMessageId = data.seenMessageId;
        this.data.editResourceLastSeenMessage(data.id, data.seenMessageId);
        this.calcNews();
      }

      if ("topMessageId" in data) {
        //roomNode.topMessageId = data.topMessageId;
        this.data.editResourceTopMessageId(data.id, data.topMessageId);
        setTimeout(() => {
          this.calcNews();
        }, 100);
      }

      if (
        "nanoSessionsAdded" in data ||
        "nanoSessionsRemoved" in data ||
        "nanoSessionsReset" in data
      ) {
        this.roomService.getData(data.id).then((roomData) => {
          this.data.setResourceNanoSession(data.id, roomData.nanoSessions);
        });
      }

      if ("data" in data) {
        roomNode.error = data.error;
        roomNode.name = data.error
          ? data.errorMessage
          : (<RoomData>data.data)?.name;
        roomNode.avatar = (<RoomData>data.data)?.avatar;
      }
    }
  };

  /*
    got invite or my permission is changed: roomAccPerm: C, roomPerm: M
    kicked me: roomAccPerm: D
    room disappear (delete): roomPerm: D
    room settings or any other member permission is changed: roomPerm: M
   */

  private onCreateDialogue = (
    data: WorkspaceSubscriptionDialogueEventRecord
  ) => {
    if (!this.data.getResourceById(data.id)) {
      this.dialogueService.getData(data.id).then((dialogueData) => {
        this.data.pushResource(
          null,
          this.makeSidebarDataFromWorkspaceRecord(dialogueData)
        );
      });
    }
  };

  private onDeleteDialogue = (
    data: WorkspaceSubscriptionDialogueEventRecord
  ) => {
    this.data.removeResource(data.id);
  };

  private onCreateRoomPermission = (
    data: WorkspaceSubscriptionRoomPermissionEventRecord
  ) => {
    console.log("on create perm", data);
  };

  private onModifyRoomPermission = (
    data: WorkspaceSubscriptionRoomPermissionEventRecord
  ) => {
    console.log("on edit perm", data);
  };

  private onDeleteRoomPermission = (
    data: WorkspaceSubscriptionRoomPermissionEventRecord
  ) => {
    console.log("on delete perm", data);

    this.roomService.getData(data.id).then((roomData) => {
      if (this.data.getResourceById(data.id)) {
        this.data.setResourceError(data.id, roomData.error);
        this.data.editResourceName(
          data.id,
          roomData.error
            ? roomData.errorMessage
            : (<RoomData>roomData.data).name
        );
      } else {
        for (let i = 0; i < this.rejectedRooms.length; i++) {
          if (this.rejectedRooms[i].id == data.id) {
            this.rejectedRooms[i].error = roomData.error;
            this.rejectedRooms[i].name = roomData.error
              ? roomData.errorMessage
              : (<RoomData>roomData.data).name;
            return;
          }
        }

        for (let i = 0; i < this.grantedRooms.length; i++) {
          if (this.grantedRooms[i].id == data.id) {
            // removed the grant for the room
            this.grantedRooms.splice(i, 1);
            return;
          }
        }
      }
    });
  };

  private onCreateRoomAccountPermission = (
    data: WorkspaceSubscriptionRoomAccountPermissionEventRecord
  ) => {
    console.log("on create acc perm", data);

    this.accountService.getMe().then((me) => {
      if (data.accountId == me.id) {
        this.roomService.getData(data.id).then((roomData) => {

          if (data.ownerAccountId != me.id){ // do not notify if i just created the room
            this.cordovaService.callNotification({
              title: "Room invite",
              message: marker(
                "You have been invited to join a room: " +
                  (<RoomData>roomData?.data)?.name
              ),
            });
          }
          

          let wsResource = this.data.getResourceById(data.id);
          if (wsResource) {
            this.data.setResourceError(data.id, roomData.error);
            this.data.editResourceName(
              data.id,
              roomData.error
                ? roomData.errorMessage
                : (<RoomData>roomData?.data)?.name
            );
          } else {
            // look for it in the rejectedRooms
            for (let i = 0; i < this.rejectedRooms.length; i++) {
              if (this.rejectedRooms[i].id == data.id) {
                // before you got permission for this room maybe
                // could not load all of the room data like
                // pinnedKey, name, or even the room itself
                // so load the room again
                this.rejectedRooms[i] =
                  this.makeSidebarDataFromWorkspaceRecord(roomData);
                return;
              }
            }

            // look for it in the grantedRooms, but it shouldn't be there
            for (let i = 0; i < this.grantedRooms.length; i++) {
              if (this.grantedRooms[i].id == data.id) {
                this.grantedRooms[i].error = roomData.error;
                this.grantedRooms[i].name = roomData.error
                  ? roomData.errorMessage
                  : (<RoomData>roomData.data)?.name;
                return;
              }
            }

            // else it is just a new room appering in the ws, so add it
            this.grantedRooms.push(
              this.makeSidebarDataFromWorkspaceRecord(roomData)
            );
            this.calcNews();
          }
        });
      }
    });
  };

  private onModifyRoomAccountPermission = (
    data: WorkspaceSubscriptionRoomAccountPermissionEventRecord
  ) => {
    console.log("on edit acc perm", data);
  };

  private onDeleteRoomAccountPermission = (
    data: WorkspaceSubscriptionRoomAccountPermissionEventRecord
  ) => {
    console.log("on delete acc perm", data);

    this.accountService.getMe().then((me) => {
      if (data.accountId == me.id) {
        this.roomService.getData(data.id).then((roomData) => {
          if (this.data.getResourceById(data.id)) {
            this.data.setResourceError(data.id, roomData.error);
            this.data.editResourceName(
              data.id,
              roomData.error
                ? roomData.errorMessage
                : (<RoomData>roomData.data).name
            );
          } else {
            for (let i = 0; i < this.rejectedRooms.length; i++) {
              if (this.rejectedRooms[i].id == data.id) {
                this.rejectedRooms[i].error = roomData.error;
                this.rejectedRooms[i].name = roomData.error
                  ? roomData.errorMessage
                  : (<RoomData>roomData.data).name;
                return;
              }
            }

            for (let i = 0; i < this.grantedRooms.length; i++) {
              if (this.grantedRooms[i].id == data.id) {
                // removed the grant for the room
                this.grantedRooms.splice(i, 1);
                return;
              }
            }
          }
        });
      }
    });
  };

  private onRoomMessageCreate = (
    msg: WorkspaceSubscriptionRoomMessageEventRecord
  ) => {
    let r = this.routerHandler.getRoute();
    if (
      this.me &&
      this.me.id != msg.posterId 
      // lets try the sound anyway
      //&& (r.url != "room/" + msg.id + "/chat/room" 
      //|| !this.cordovaService.isAppInForeground())
    ) {
      this.roomService.getData(msg.id).then((room) => {
        this.cordovaService.callNotification({
          id: getTimeFromId(room.id),
          title: (<RoomData>room.data).name,
          message: <string>msg.content,
          callback: (data) => {
            this.routerHandler.navigate([data.navigate]);
          },
          callbackData: {
            navigate: "room/" + msg.id + "/chat/room",
          },
        });
      });
    }
  };

  private onDialogueMessageCreate = (
    msg: WorkspaceSubscriptionDialogueMessageEventRecord
  ) => {
    let r = this.routerHandler.getRoute();
    if (
      this.me &&
      this.me.id != msg.posterId 
      // lets try the sound anyway
      //&& (r.url != "room/" + msg.posterId + "/chat/private" || !this.cordovaService.isAppInForeground())
    ) {
      this.accountService.getAccount(msg.posterId).then((acc) => {
        //let imgSrc = this.environmentService.url_api + "/r/avatar/" + acc.id + "/" + acc.avatarImageKey;

        this.cordovaService.callNotification({
          id: getTimeFromId(msg.posterId),
          title: acc.avatarName,
          message: <string>msg.content,
          callback: (data) => {
            this.routerHandler.navigate([data.navigate]);
          },
          callbackData: {
            navigate: "room/" + msg.posterId + "/chat/private",
          },
        });
      });
    }
  };

  // account
  private onEditAccount = (data: WorkspaceSubscriptionAccountEventRecord) => {
    console.log("server>client edit acc", data);
    this.accountService.getAccount(data.id).then((acc) => {
      if ("avatarName" in data) {
        acc.avatarName = data.avatarName;
      }
    });

    this.accountService.getMe().then((me) => {
      if ("avatarName" in data) {
        me.avatarName = data.avatarName;
      }
    });
  };

  // direct api-calls --------------------------------------------------------
  // resource groups
  public createResourceGroup(name: string) {
    //console.log('client>server, create group: name', name);
    return this.resourceGroupService.createResourceGroup(name).then((res) => {
      // TODO handle false
      showWorkspaceTabObserver.next();
    });
  }

  public deleteResourceGroup(id) {
    //console.log('client>server, delete group: id', id);
    return this.resourceGroupService.deleteResourceGroup(id).then((res) => {
      // TODO handle false
    });
  }

  public editResourceGroupName(id, name: string) {
    //console.log('client>server, edit group name: id, name', id, name);
    return this.resourceGroupService
      .editResourceGroupName(id, name)
      .then((res) => {
        // TODO handle false
      })
      .catch(err => {
        console.log("rename error", err);
        if(err.message === "Message error: 33") //TODO this is ugly, need better server error objects
          this.dialogService.showSnackbar(marker("Group name too long!"));
      });
  }

  public editResourceGroupOrdering(id, ordering: number) {
    //console.log('client>server, edit group ordering: id, ordering', id, ordering);
    return this.resourceGroupService
      .editResourceGroupOrdering(id, ordering)
      .then((res) => {
        // TODO handle false
        // watch out, if you reorder the new created group, you shouldnt popup the same message like during the grab-reorder function
      });
  }

  public editResourceGroupClose(id, closed: boolean) {
    //console.log('client>server, edit group close: id, closed', id, closed);
    return this.resourceGroupService
      .editResourceGroupClosed(id, closed)
      .then((res) => {
        // TODO handle false
      });
  }

  public editResourceOrdering(
    resource: ResourceNode,
    groupId,
    ordering: number
  ) {
    //console.log('editResourceOrdering', Object.assign({}, resource), groupId, ordering)
    if (resource.type == ResourceType.ROOM) {
      return this.roomService
        .editPlacement(resource.id, groupId, ordering)
        .then((res) => {
          // TODO handle false
        });
    } else if (resource.type == ResourceType.DIALOGUE) {
      return this.dialogueService
        .editPlacement(resource.owner, groupId, ordering)
        .then((res) => {
          // TODO handle false
        });
    } else {
      throw "Not supported resource type: " + resource.type;
    }
  }

  public searchResourceId;
  public searchQuery;
  public searchResult: Map<string, SearchResult[]> = new Map<
    string,
    SearchResult[]
  >();

  private searchHandler() {
    /*if (
      (this.searchResourceId == SearchService.QUERY_ALL ||
        this.searchResourceId == resourceId) &&
      this.searchQuery == query
    ) {*/
    this.searchResult.clear();

    let allResult = this.searchService.getResults();

    allResult.forEach((result) => {
      if (this.searchResult.has(result.resourceId)) {
        this.searchResult.set(
          result.resourceId,
          this.searchResult.get(result.resourceId).concat(result)
        );
      } else {
        this.searchResult.set(result.resourceId, [result]);
      }
    });
    //}
  }

  public onRouterChangeSearch = (res) => {
    //this.callSearch(res.params.resourceId, res.fragment?.search);
    this.showSearch();
  };

  private onWorkspaceLoaded = (res) => {
    let oldData: { [key: string]: ResourceNode } = {};

    if (this.data) {
      this.data.forEachResources((el) => {
        oldData[el.id] = el;
      });
    }

    this.data = new SidebarData();
    this.workspaceLoaded = false;

    if (!this.authService.isAnonym()) {
      console.log("conn :7 - load workspace data");
      // load workspace
      this.subscriptionService
        .queryWorkspace()
        .then(
          // query
          (ws: WorkspaceQueryRecord) => {
            console.log("conn :8 - workspace loaded");

            let allRes = ws.ungroupedResources.concat();

            // fill the ws with data -------
            ws.resourceGroups.forEach((group) => {
              this.data.pushGroup({
                id: group.id,
                name: group.error ? group.errorMessage : group.name,
                closed: group.closed,
                ordering: group.ordering,
                error: group.error === true,
                resources: [],
              });

              allRes = allRes.concat(group.resources);

              group.resources.forEach((res) => {
                this.data.pushResource(
                  group.id,
                  this.makeSidebarDataFromWorkspaceRecord(res)
                );
              });
            });

            ws.ungroupedResources.forEach((res) => {
              this.data.pushResource(
                null,
                this.makeSidebarDataFromWorkspaceRecord(res)
              );
            });

            let oldGrantedRoomsId = [];
            this.grantedRooms.forEach((el) => {
              oldGrantedRoomsId.push(el.id);
            });
            this.grantedRooms = [];
            ws.grantedRooms.forEach((res) => {
              let roomData = this.makeSidebarDataFromWorkspaceRecord(res);
              this.grantedRooms.push(roomData);
            });
            // check if there is new after the reset
            if (!this.initLoad) {
              this.grantedRooms.forEach((res) => {
                if (!oldGrantedRoomsId.includes(res.id)) {
                  this.cordovaService.callNotification({
                    id: getTimeFromId(res.id),
                    title: res.name,
                    message: marker("New room invite: ") + res.name,
                  });
                }
              });
            }

            this.rejectedRooms = [];
            ws.rejectedRooms.forEach((res) => {
              let roomData = this.makeSidebarDataFromWorkspaceRecord(res);
              this.rejectedRooms.push(roomData);
            });

            this.workspaceLoaded = true;

            // fill the news
            this.calcNews();


            // handle new msg after reconnect
            this.data.forEachResources((res) => {
              if (
                !this.initLoad &&
                res.topMessageId != res.lastSeenMessageId &&
                (!oldData[res.id] ||
                  oldData[res.id].topMessageId != res.topMessageId)
              ) {
                if (res.type == ResourceType.ROOM) {
                  this.cordovaService.callNotification({
                    id: getTimeFromId(res.id),
                    title: res.name,
                    message: marker("New message from") + " " + res.name,
                    callback: (data) => {
                      this.routerHandler.navigate([data.navigate]);
                    },
                    callbackData: {
                      navigate: "room/" + res.id + "/chat/room",
                    },
                  });
                } else {
                  this.cordovaService.callNotification({
                    id: getTimeFromId(res.id),
                    title: res.name,
                    message: marker("New message from") + " " + res.name,
                    callback: (data) => {
                      this.routerHandler.navigate([data.navigate]);
                    },
                    callbackData: {
                      navigate: "room/" + res.id + "/chat/private",
                    },
                  });
                }
              }
            });

            
            this.initLoad = false;
          }
        )
        .catch((e) => {
          console.log("sidebar ws error", e);
        });
    }
  };

  /**
   * 1, fill the workspace with data
   * 2, subscribe all of the api-side subscriptions
   * 3, init drag-n-drop
   */
  constructor(
    public dialog: MatDialog,
    private appDragDropService: AppDragDropService,
    @Inject(DOCUMENT) private document: Document,
    private searchService: SearchService,
    private routerHandler: RouterHandler,
    private roomService: RoomService,
    private dialogueService: DialogueService,
    private accountService: AccountService, //private appRef: ApplicationRef
    private resourceGroupService: ResourceGroupService,
    private subscriptionService: SubscriptionService,
    private dialogService: DialogService,
    private authService: AuthService,
    private cordovaService: CordovaService
  ) {
    // register to search
    this.searchService.subscribe(this.searchHandler.bind(this));
    this.routerHandler.subscribe("search", this.onRouterChangeSearch);
    if (this.routerHandler.isOnPage("search")) {
      this.onRouterChangeSearch(this.routerHandler.getRoute());
      this.searchHandler();
    }

    this.accountService.getMe().then((me) => {
      this.me = me;
    });

    console.log("sidebar constructor");
    this.subscriptionService.subscribeWorkspaceLoaded(this.onWorkspaceLoaded);

    // subscriptions --------
    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateResourceGroup
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditResourceGroup
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteResourceGroup
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateRoom
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditRoom
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateDialogue
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditRoom
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteDialogue
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteRoom
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ACCOUNT_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditAccount
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_PERMISSION_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateRoomPermission
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_PERMISSION_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onModifyRoomPermission
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_PERMISSION_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteRoomPermission
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateRoomAccountPermission
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onModifyRoomAccountPermission
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteRoomAccountPermission
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onRoomMessageCreate
    );

    this.subscriptionService.subscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onDialogueMessageCreate
    );

    // drag n drop --------

    // init, after query done
    this.dragDropService = appDragDropService;

    // this event subscribed to the dragMove
    this.scrollMouseMoveHandler = (pos, comp) => {
      // this even can be triggered by mousemove, and window.settimeout, and we don't want to run it twice in the same time, so first of all, clear the timer
      clearTimeout(this.isCursorOnScrollPositionTimer);
      if (!this.dragDropService.isDragOn()) return;

      let cont = this.expScrollWindow.nativeElement;
      let bond = cont.getBoundingClientRect();

      let x = pos.x;
      let y = pos.y;

      if (x > bond.left && x < bond.left + bond.width) {
        if (
          y > bond.top &&
          y < bond.top + ResourcesSidebarComponent.SCROLL_DISTANCE_SENSITIVITY
        ) {
          // check the top empty range, we dont want to scroll over the emptiness
          // you can overflow by 30 pixel, to see you reached the last element
          // only when you dragged a group
          if (
            comp.getDropZone() == "sidebar-groups" &&
            cont.scrollTop <=
              this.startPlaceFillerHeight +
                (ResourcesSidebarComponent.SCROLL_DISTANCE_SENSITIVITY -
                  ResourcesSidebarComponent.SCROLL_OVERFLOW)
          ) {
            cont.scrollTo({
              top:
                this.startPlaceFillerHeight -
                ResourcesSidebarComponent.SCROLL_OVERFLOW,
              left: 0,
              behavior: "smooth",
            });
          } else {
            // top edge
            cont.scrollBy({
              top: -1 * ResourcesSidebarComponent.SCROLL_SPEED,
              left: 0,
              behavior: "smooth",
            });
          }

          this.isCursorOnScrollPositionTimer = setTimeout(() => {
            this.scrollMouseMoveHandler(pos, comp);
          }, 100);
        } else if (
          y < bond.top + bond.height &&
          y >
            bond.top +
              bond.height -
              ResourcesSidebarComponent.SCROLL_DISTANCE_SENSITIVITY
        ) {
          /**
           * Watch out! You have to reach the height of the parent container to start scrolling. So when you reached the scrollTop 100px, you are on the containerHeight+100px
           */
          if (
            comp.getDropZone() == "sidebar-groups" &&
            cont.scrollTop >=
              this.startPlaceFillerHeight +
                (this.expPanelNode.nativeElement.childElementCount - 1) *
                  ResourcesSidebarComponent.GROUP_BOX_HEIGHT -
                bond.height -
                (ResourcesSidebarComponent.SCROLL_DISTANCE_SENSITIVITY -
                  ResourcesSidebarComponent.SCROLL_OVERFLOW)
          ) {
            cont.scrollTo({
              top:
                this.startPlaceFillerHeight +
                (this.expPanelNode.nativeElement.childElementCount - 1) *
                  ResourcesSidebarComponent.GROUP_BOX_HEIGHT -
                bond.height +
                ResourcesSidebarComponent.SCROLL_OVERFLOW,
              left: 0,
              behavior: "smooth",
            });
          } else {
            // bottom edge
            cont.scrollBy({
              top: ResourcesSidebarComponent.SCROLL_SPEED,
              left: 0,
              behavior: "smooth",
            });
          }

          this.isCursorOnScrollPositionTimer = setTimeout(() => {
            this.scrollMouseMoveHandler(pos, comp);
          }, 100);
        }
      }
    };

    this.dragDropService.subscribeDragMove(this.scrollMouseMoveHandler);

    showWorkspaceTabObserver.subscribe(() => this.showInorder());
  }

  private makeSidebarDataFromWorkspaceRecord(
    res:
      | WorkspaceQueryRoomRecord
      | WorkspaceQueryDialogueRecord
      | RoomRecord
      | DialogueDataRecord
  ): ResourceNode {
    if (res.type == ResourceType.ROOM) {
      let roomRes = <WorkspaceQueryRoomRecord>res;
      return {
        id: roomRes.id,
        name: roomRes.error ? roomRes.errorMessage : roomRes.data?.name,
        avatar: roomRes.data?.avatar,
        owner: roomRes.ownerAccountId,
        news:
          roomRes.topMessageId !== null &&
          roomRes.seenMessageId != roomRes.topMessageId,
        lastSeenMessageId: roomRes.seenMessageId,
        topMessageId: roomRes.topMessageId,
        ordering: roomRes.ordering,
        type: roomRes.type,
        error: roomRes.error === true,
        nanoSessions: roomRes.nanoSessions,
      };
    } else {
      let dialogueRes = <WorkspaceQueryDialogueRecord>res;

      return {
        id: dialogueRes.id,
        name: dialogueRes.account
          ? dialogueRes.account.avatarName
          : dialogueRes.errorMessage,
        avatar: dialogueRes.account,
        owner: dialogueRes.account.id,
        news:
          dialogueRes.topMessageId !== null &&
          dialogueRes.seenMessageId != dialogueRes.topMessageId,
        lastSeenMessageId: dialogueRes.seenMessageId,
        topMessageId: dialogueRes.topMessageId,
        ordering: dialogueRes.ordering,
        type: dialogueRes.type,
        error: !dialogueRes.account,
        nanoSessions: {},
      };
    }
  }

  ngOnInit(): void {}

  // dont forget to unsubscribe the subscriptions
  ngOnDestroy(): void {
    this.subscriptionService.unsubscribeWorkspaceLoaded(this.onWorkspaceLoaded);
    this.appDragDropService.unsubscribeDragMove(this.scrollMouseMoveHandler);

    clearTimeout(this.isCursorOnScrollPositionTimer); // just to be clear

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateResourceGroup
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditResourceGroup
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteResourceGroup
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.RESOURCE_GROUP_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteResourceGroup
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateRoom
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditRoom
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteRoom
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ACCOUNT_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onEditAccount
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_PERMISSION_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateRoomPermission
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_PERMISSION_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onModifyRoomPermission
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_PERMISSION_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteRoomPermission
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onCreateRoomAccountPermission
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.MODIFY,
      this.onModifyRoomAccountPermission
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_ACCOUNT_PERMISSION_EVENT,
      SubscriptionServiceEventType.DELETE,
      this.onDeleteRoomAccountPermission
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.ROOM_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onRoomMessageCreate
    );

    this.subscriptionService.unsubscribe(
      SubscriptionServiceEvent.DIALOGUE_MESSAGE_EVENT,
      SubscriptionServiceEventType.CREATE,
      this.onDialogueMessageCreate
    );

    this.searchService.unsubscribe(this.searchHandler);
    this.routerHandler.unsubscribe("search", this.onRouterChangeSearch);
  }

  // drag n drop handlers ------------------------------------

  public dragStarted(event) {
    let container = this.expPanelNode.nativeElement;
    this.expPanelHeight = container.getBoundingClientRect().height + "px";

    // calc the children DOM node before the dragged element
    let childNumberBefore = 0;
    let el: any = event.srcComponent.getDOMNode().previousSibling;
    while ((el = el.previousSibling) != null) {
      childNumberBefore++;
    }

    this.startPlaceFillerHeight =
      event.srcComponent.getDOMNode().offsetTop -
      childNumberBefore * ResourcesSidebarComponent.GROUP_BOX_HEIGHT;
    console.log("startplacefiller", this.startPlaceFillerHeight);

    this.isGroupDragStarted = true;
  }

  /**
   *
   * @param ev contains the new position of the group (from 0 to array length-1): ev.pos
   * @param group the current dragged group
   */
  public groupDragEnded(ev: AppDragMoveEvent, group: ResourceGroupNode) {
    this.isGroupDragStarted = false;

    this.startPlaceFillerHeight = 0;

    let ordering = this.data.moveGroup(group.id, ev.pos);
    this.editResourceGroupOrdering(group.id, ordering);

    if (this.isPositionNeedToOptimize(ordering)) {
      this.optimizeGroupOrdering();
    }

    window.setTimeout(() => {
      this.expPanelHeight = "auto";
    }, ResourcesSidebarComponent.ANGULAR_EXPANSION_PANEL_ANIMATION_SPEED);

    /*this.data.forEach((el)=>{
      console.log('= '+el.name);
      el.resources.forEach((r)=>{
        console.log('=='+r.name);
      });
    });*/
  }

  /**
   *
   * @param ev contains the new position of the resource
   * @param group the group, where the resource was earlier
   */
  public resourceDragEnded(ev: DragResourceEvent, group: ResourceGroupNode) {
    let destGroup;

    //group.resources.splice(group.resources.indexOf(ev.resource), 1);

    if ("group-" + group.id != ev.appDrag.containerId) {
      let newGroupId = ev.appDrag.containerId.substring(6); // cut the "group-" prefix
      if (newGroupId === "") {
        destGroup = this.ungroupedData;
      } else {
        destGroup = this.data.getGroupById(ev.appDrag.containerId.substring(6)); // cut the "group-" prefix
      }

      if (destGroup == null) {
        console.error(
          "destination group not found while dragging resources, id:" +
            "group-" +
            ev.appDrag.containerId
        );
        return;
      }
    } else {
      destGroup = group;
    }

    let pos = this.data.moveResource(
      ev.resource.id,
      destGroup.id,
      ev.appDrag.pos
    );

    this.editResourceOrdering(ev.resource, destGroup.id, pos);

    if (this.isPositionNeedToOptimize(pos)) {
      this.optimizeResourceOrdering(destGroup.id);
    }
  }

  // helper functions ---------------------------------

  /**
   * When we reorder an element, we set the ordering property halfway between its new neighbours.
   * It is fast and simply, but after a lot of reorder it can generate bad floats. This function
   * checks if the actual generated position is a too long float number.
   * @param pos float
   * @returns boolean - is it needed to optimize this ordering number or not
   */
  private isPositionNeedToOptimize(pos: number): boolean {
    return pos <= 0 || pos.toString().length > 10;
  }

  /**
   * when the isPositionNeedToOptimize return with a too long float position, we should regenarate
   * their ordering property with a nice and rounded numbers.
   */
  private optimizeGroupOrdering() {
    let copyGroups = [];

    let allGroup = this.data.getData();

    allGroup.forEach((g) => {
      copyGroups.push({ id: g.id, ordering: g.ordering });
    });

    let last = Math.ceil(copyGroups[copyGroups.length - 1].ordering);

    // make round number
    for (let i = copyGroups.length - 1; i >= 0; i--) {
      this.data.editGroupOrdering(copyGroups[i].id, last + i);
      this.editResourceGroupOrdering(copyGroups[i].id, i + 1);
    }

    // switch them to 1
    if (last > 100000000) {
      for (let i = 0; i < copyGroups.length - 1; i++) {
        this.editResourceGroupOrdering(copyGroups[i].id, i + 1);
      }
    }
  }

  /**
   * same as optimizeGroupOrdering but with resource in a group
   * @param groupId must be valid id
   */
  private optimizeResourceOrdering(groupId) {
    let resCopy: ResourceNode[] = [];
    let allRes: ResourceNode[] = [];

    if (groupId == null) {
      allRes = this.data.getUngroupedData();
    } else {
      allRes = this.data.getGroupById(groupId).resources;
    }

    allRes.forEach((r) => {
      resCopy.push(Object.assign({}, r));
    });

    let last = Math.ceil(resCopy[resCopy.length - 1].ordering);

    // make round number
    for (let i = resCopy.length - 1; i >= 0; i--) {
      this.editResourceOrdering(resCopy[i], groupId, last + i);
    }

    // switch them to 1
    if (last > 100000000) {
      for (let i = 0; i < resCopy.length; i++) {
        this.editResourceOrdering(resCopy[i], groupId, i + 1);
      }
    }
  }

  /**
   *
   * @param resources where to search ('all' for everywhere)
   * @param query search query
   */
  /*private callSearch(resourceId: string, query: string) {
    if (!this.workspaceLoaded && resourceId == SearchService.QUERY_ALL) {
      return window.setTimeout(() => {
        this.callSearch(resourceId, query);
      }, 100);
    }

    // do not repeat the same query if its only a route change
    if (!(resourceId == this.searchResourceId && query == this.searchQuery)) {
      let callResources = [];
      this.searchResourceId = resourceId;
      this.searchQuery = query;
      this.searchResult.clear();

      if (resourceId == SearchService.QUERY_ALL) {
        this.data.forEachResources((res) => {
          callResources.push(res.id);
        });
      } else {
        callResources.push(resourceId);
      }

      callResources.forEach((id) => {
        this.searchService.search(id, query, "en"); // TODO language
      });
    }

    
  }*/

  // dialogs and menu states ------------------------------
  openNewGroupDialog(): void {
    const dialogRef = this.dialog.open(NewGroupDialogComponent, {
      data: { name: "" },
    });

    dialogRef.afterClosed().subscribe((result) => {
      if(result){
        this.createResourceGroup(result);
      }
    });
  }

  openNewRoomDialog(): void {
    const dialogRef = this.dialog.open(CreateRoomDialogComponent, {
      data: { name: "" },
    });

    dialogRef.afterClosed().subscribe((result) => {
      if (!result) return;
      console.log('room create', result)
      this.roomService.newRoom(result).then(()=>{
        this.dialogService.showSnackbar(marker("Room created successfully!"));
        showWorkspaceTabObserver.next();
      }).catch((error)=>{

        if(error.message === "Own room limit is reached (200)"){
          this.dialogService.openAlertDialog(
            marker("Room limit"),
            marker("Own room limit is reached.")
          );
        }
        else{
          this.dialogService.openAlertDialog(
            marker("Create room"),
            marker("Could not create room.")
          );
        }
        console.error("Room creation error", error);
      });
    });
  }

  openEditResourceGroupDialog(group) {
    const dialogRef = this.dialog.open(RenameGroupDialogComponent, {
      data: { name: group.name },
      hasBackdrop: false,
    });

    dialogRef.afterClosed().subscribe((result) => {
      if (result) {
        this.editResourceGroupName(group.id, result);
      }
    });
  }

  openDeleteResorceGroupDialog(group) {
    const dialogRef = this.dialog.open(DeleteGroupDialogComponent, {
      data: { name: group.name },
    });

    dialogRef.afterClosed().subscribe((result) => {
      if (result) {
        this.deleteResourceGroup(group.id);
      }
    });
  }

  // TODO do we need this?
  onSearch(s: string) {
    if (s.length > 0) {
      this.orderedList = [];
      this.data.getData().forEach((group) => {
        this.orderedList = this.orderedList.concat(
          group.resources.filter((resource) =>
            resource.name.toUpperCase().includes(s.toUpperCase())
          )
        );
      });

      this.orderedList = this.orderedList.sort();
    } else {
      this.orderedList = null;
    }
  }

  showNews() {
    this.calcNews();
    this.activePanel = "news";
  }

  public calcNews() {
    this.newsList = [];
    this.newsNumber = 0;

    this.data.getData().forEach((group) => {
      this.newsList = this.newsList.concat(group.resources);
      group.resources.forEach((res) => {
        if (res.news) {
          this.newsNumber++;
        }
      });
    });

    this.data.getUngroupedData().forEach((res) => {
      this.newsList.push(res);

      if (res.news) {
        this.newsNumber++;
      }
    });

    this.newsNumber += this.grantedRooms.length;

    this.newsList = this.newsList.sort((a, b) => {
      if ((a.news && b.news) || (!a.news && !b.news)) {
        if (!b.topMessageId || a.topMessageId > b.topMessageId) {
          return -1;
        } else if (!a.topMessageId || a.topMessageId < b.topMessageId) {
          return 1;
        } else {
          return 0;
        }
      } else if (a.news) {
        return -1;
      } else if (b.news) {
        return 1;
      }

      return 0;
    });

    newsObserver.next(this.newsNumber);
  }

  showInorder() {
    this.orderedList = null;
    this.activePanel = "inorder";
  }

  showSearch() {
    this.activePanel = "search";
  }

  public joinRoom(resource: ResourceNode) {
    this.roomService.joinRoom(resource.id).then((res) => {
      console.log("done", res);
    });
  }

  public leaveRoom(resource: ResourceNode) {
    this.roomService.leaveRoom(resource.id).then((res) => {
      console.log("done", res);
    });
  }
}
