server/services/windows.js

Summary

Maintainability
C
1 day
Test Coverage
//
//   Copyright 2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

import UserGId from '../lib/userGId';

const assert = require('assert');
const notification = require('../lib/notification');
const log = require('../lib/log');
const conf = require('../lib/conf');
const User = require('../models/user');
const Window = require('../models/window');
const Settings = require('../models/settings');
const Conversation = require('../models/conversation');
const ConversationMember = require('../models/conversationMember');
const ConversationMessage = require('../models/conversationMessage');
const MissedMessage = require('../models/missedMessage');
const nicksService = require('./nicks');
const conversaionsService = require('./conversations');

exports.findOrCreate = async function findOrCreate(user, conversation) {
  let window = await Window.findFirst({ userId: user.id, conversationId: conversation.id });

  if (!window) {
    window = await createWindow(user, conversation);
  }

  return window;
};

exports.create = async function create(user, conversation) {
  return createWindow(user, conversation);
};

exports.remove = async function remove(user, conversation) {
  const window = await Window.findFirst({ userId: user.id, conversationId: conversation.id });

  if (window) {
    log.info(user, `Removing window, id: ${window.id}`);

    await notification.broadcast(user, {
      type: 'DELETE_WINDOW',
      windowId: window.id
    });

    await window.delete();
  }
};

exports.isValidDesktop = async function isValidDesktop(user, desktop) {
  const windows = await Window.find({ userId: user.id });

  return windows.some(window => window.get('desktop') === desktop);
};

exports.getWindowsForNetwork = async function getWindowsForNetwork(user, network) {
  const windows = await Window.find({ userId: user.id });
  const matchingWindows = [];

  for (const window of windows) {
    const conversation = await Conversation.fetch(window.get('conversationId'));

    if (!conversation) {
      log.warn(user, `Conversation missing, id: ${conversation.id}`);
    } else if (conversation.get('network') === network) {
      matchingWindows.push(window);
    }
  }

  return matchingWindows;
};

exports.scanMentions = async function scanMentions(conversation, message) {
  if (!message.get('userGId') || message.get('userGId') === 'i0' || !message.get('body')) {
    return;
  }

  const users = [];
  const srcUserGId = UserGId.create(message.get('userGId'));
  const network = conversation.get('network');

  if (conversation.get('type') === 'group') {
    const mentions = message.get('body').match(/(?:^| )@\S+(?=$| )/g);

    if (!mentions) {
      return;
    }

    for (const mention of mentions) {
      const user = await nicksService.getUser(mention.substring(1), network);

      if (user) {
        users.push(user);
      }
    }

    if (users.length === 0) {
      return;
    }
  } else {
    const peerMember = await conversaionsService.getPeerMember(conversation, srcUserGId);
    const peerMemberGId = UserGId.create(peerMember.get('userGId'));

    if (peerMemberGId.isMASUser) {
      const user = await User.fetch(peerMemberGId.id);

      if (user) {
        users.push(user);
      }
    }
  }

  for (const user of users) {
    if (user.get('deleted')) {
      continue;
    }

    const online = await user.isOnline();

    if (online) {
      continue; // Mentioned user is online
    }

    const window = await Window.findFirst({ userId: user.id, conversationId: conversation.id });

    if (window && window.get('emailAlert')) {
      await MissedMessage.create({
        userId: user.id,
        conversationId: conversation.id,
        msgUserGId: message.get('userGId'),
        msgBody: message.get('body'),
        msgTs: new Date()
      });
    }
  }
};

async function createWindow(user, conversation) {
  let peerMember = null;

  assert(conversation);

  let settings = await Settings.findFirst({ userId: user.id });

  if (!settings) {
    log.warn(user, "User doesn't have settings. Fixing.");
    settings = await Settings.create({ userId: user.id });
  }

  const window = await Window.create({
    userId: user.id,
    conversationId: conversation.id,
    desktop: settings.get('activeDesktop')
  });

  if (conversation.get('type') === '1on1') {
    const members = await ConversationMember.find({ conversationId: conversation.id });
    peerMember = members.find(member => member.get('userGId') !== user.gIdString);
  }

  // TODO: Copy paste code
  await notification.broadcast(user, {
    type: 'ADD_WINDOW',
    windowId: window.id,
    name: conversation.get('name'),
    userId: peerMember && peerMember.get('userGId'),
    windowType: conversation.get('type'),
    network: conversation.get('network'),
    password: conversation.get('password') || null,
    topic: conversation.get('topic'),
    alerts: {
      email: window.get('emailAlert'),
      notification: window.get('notificationAlert'),
      sound: window.get('soundAlert'),
      title: window.get('titleAlert')
    },
    row: window.get('row'),
    column: window.get('column'),
    minimizedNamesList: window.get('minimizedNamesList'),
    desktop: window.get('desktop'),
    role: 'u' // Everybody starts as a normal user
  });

  const maxBacklogLines = conf.get('session:max_backlog');
  const messages = await ConversationMessage.find({ conversationId: conversation.id });

  for (const message of messages.slice(-1 * maxBacklogLines)) {
    const ntf = message.convertToNtf();
    ntf.type = 'ADD_MESSAGE';
    ntf.windowId = window.id;

    await notification.broadcast(user, ntf);
  }

  return window;
}