Преглед на файлове

cleaned up messages code

tags/v0.8.0
Shaun преди 4 години
родител
ревизия
a1bb637ffd
променени са 4 файла, в които са добавени 356 реда и са изтрити 302 реда
  1. +208
    -0
      src/client/ui/templates/messages/channelPicker.js
  2. +8
    -298
      src/client/ui/templates/messages/messages.js
  3. +123
    -0
      src/client/ui/templates/messages/mobile.js
  4. +17
    -4
      src/client/ui/templates/messages/styles.less

+ 208
- 0
src/client/ui/templates/messages/channelPicker.js Целия файл

@@ -0,0 +1,208 @@
define([
'html!ui/templates/messages/tplTab'
], function (
tplTab
) {
const extensionObj = {
processChat: function (msgConfig) {
const { message, event: keyboardEvent } = msgConfig;
const { key } = keyboardEvent;
const { el, currentChannel } = this;

const optionContainer = this.find('.channelOptions');

if (message.length) {
if (el.hasClass('picking'))
msgConfig.cancel = true;
return;
}
if (key === 'Enter') {
const selectedSubPick = optionContainer.find('.option.selected');
if (selectedSubPick.length) {
this.onPickSubChannel(selectedSubPick.html(), currentChannel);
return;
}
}

//If we're busy picking a sub channel, we can use keyboard nav
const isPicking = el.hasClass('picking');
const currentSelection = optionContainer.find('.option.selected');
if (isPicking && currentSelection.length) {
const delta = {
ArrowUp: -1,
ArrowDown: 1
}[key];

if (delta) {
const options = optionContainer.find('.option');
const currentIndex = currentSelection.eq(0).index();
let nextIndex = (currentIndex + delta) % options.length;
currentSelection.removeClass('selected');
options.eq(nextIndex).addClass('selected');
}
}

const pick = {
'%': 'party',
'!': 'global',
$: 'custom',
'@': 'direct'
}[key];

if (!pick) {
if (isPicking)
msgConfig.cancel = true;

return;
}

if (currentChannel === pick) {
if (pick === 'direct')
this.lastPrivateChannel = null;
else if (pick === 'custom')
this.lastCustomChannel = null;
}

this.onPickChannel(pick, true);
msgConfig.cancel = true;
},

onPickChannel: function (channel, autoPickSub) {
this.currentChannel = channel;
this.currentSubChannel = null;

const showSubChannels = (
['direct', 'custom'].includes(channel) &&
(
!autoPickSub ||
(
channel === 'direct' &&
!this.lastPrivateChannel
) ||
(
channel === 'custom' &&
!this.lastCustomChannel
)
)
);

if (!showSubChannels) {
this.find('.channelOptions').removeClass('active');

let showValue = {
direct: this.lastPrivateChannel,
custom: this.lastCustomChannel
}[channel];

if (channel === 'direct' || channel === 'custom')
this.currentSubChannel = showValue;

showValue = showValue || channel;

this.find('.channelPicker').html(showValue);

this.find('input').focus();

this.el.removeClass('picking');
} else
this.onShowChannelOptions(channel);
},

onPickSubChannel: function (subChannel, channel) {
this.currentSubChannel = subChannel;
this.find('.channelOptions').removeClass('active');
this.find('.channelPicker').html(subChannel);

const elInput = this.find('input');

elInput.focus();

if (channel === 'custom') {
if (subChannel === 'join new') {
elInput.val('/join channelName');
elInput[0].setSelectionRange(6, 17);
} else if (subChannel === 'leave') {
elInput.val('/leave channelName');
elInput[0].setSelectionRange(7, 18);
}
}

this.el.removeClass('picking');
},

onShowChannelOptions: function (currentPick) {
const optionContainer = this.find('.channelOptions')
.addClass('active')
.empty();

const options = [];
let handlerOnClick = this.onPickChannel;

this.el.addClass('picking');

if (!currentPick) {
options.push('global', 'custom');

if (this.privateChannels.length)
options.push('direct');

//Hack...surely we can find a more sane way to do this
if ($('.uiParty .member').length)
options.push('party');
} else {
handlerOnClick = this.onPickSubChannel;
if (currentPick === 'direct')
options.push(...this.privateChannels);
else if (currentPick === 'custom')
options.push(...this.customChannels, 'join new', 'leave');
}

if (!options.length) {
this.onPickChannel('global');
return;
}
let addSelectStyleTo = null;
if (currentPick)
addSelectStyleTo = this.currentSubChannel || options[0];
options.forEach(o => {
const shortcut = {
global: ' (!)',
direct: ' (@)',
party: ' (%)',
custom: ' ($)'
}[o] || '';

const html = `<div class='option' shortcut='${shortcut}'>${o}</div>`;

const el = $(html)
.appendTo(optionContainer)
.on('click', handlerOnClick.bind(this, o, currentPick))
.on('hover', this.stopKeyboardNavForOptions.bind(this));

if (o === addSelectStyleTo)
el.addClass('selected');
});
},

stopKeyboardNavForOptions: function () {
this.find('.channelOptions .option.selected').removeClass('selected');
}
};

return {
init: function () {
$.extend(this, extensionObj);

//This whole hoverFilter business is a filthy hack
this.find('.channelPicker, .channelOptions, .filter:not(.channel)')
.on('mouseover', this.onFilterHover.bind(this, true))
.on('mouseleave', this.onFilterHover.bind(this, false));

this.find('.channelPicker').on('click', this.onShowChannelOptions.bind(this, null));
}
};
});

+ 8
- 298
src/client/ui/templates/messages/messages.js Целия файл

@@ -3,6 +3,8 @@ define([
'html!ui/templates/messages/template',
'html!ui/templates/messages/tplTab',
'css!ui/templates/messages/styles',
'ui/templates/messages/mobile',
'ui/templates/messages/channelPicker',
'js/input',
'js/system/client',
'js/config'
@@ -11,6 +13,8 @@ define([
template,
tplTab,
styles,
messagesMobile,
channelPicker,
input,
client,
config
@@ -44,27 +48,13 @@ define([
'onKeyUp'
].forEach(e => this.onEvent(e, this[e].bind(this)));

//This whole hoverFilter business is a filthy hack
this.find('.channelPicker, .channelOptions, .filter:not(.channel)')
.on('mouseover', this.onFilterHover.bind(this, true))
.on('mouseleave', this.onFilterHover.bind(this, false));

this.find('.channelPicker').on('click', this.onShowChannelOptions.bind(this, null));

this.find('.filter:not(.channel)').on('click', this.onClickFilter.bind(this));

if (isMobile) {
this.kbUpper = 0;
channelPicker.init.call(this);

this.el.on('click', this.toggle.bind(this, true));
this.renderKeyboard();

$(tplTab)
.appendTo(this.find('.filters'))
.addClass('btnClose')
.html('x')
.on('click', this.toggle.bind(this, false, true));
} else {
if (isMobile)
messagesMobile.init.call(this);
else {
this.find('input')
.on('keydown', this.sendChat.bind(this))
.on('input', this.enforceMaxMsgLength.bind(this))
@@ -90,105 +80,6 @@ define([
elTime.html(timeString);
},

renderKeyboard: function () {
this.find('.keyboard').remove();

let container = $('<div class="keyboard"></div>')
.appendTo(this.el);

let keyboard = {
0: 'qwertyuiop|asdfghjkl|zxcvbnm',
1: 'QWERTYUIOP|ASDFGHJKL|ZXCVBNM',
2: '1234567890|@#&*-+=()|_$"\';/'
}[this.kbUpper].split('');

//Hacky: Insert control characters in correct positions
//Backspace goes after 'm'
if (this.kbUpper === 0) {
keyboard.splice(keyboard.indexOf('z'), 0, 'caps');
keyboard.splice(keyboard.indexOf('m') + 1, 0, '<<');
} else if (this.kbUpper === 1) {
keyboard.splice(keyboard.indexOf('Z'), 0, 'caps');
keyboard.splice(keyboard.indexOf('M') + 1, 0, '<<');
} else if (this.kbUpper === 2)
keyboard.splice(keyboard.indexOf('/') + 1, 0, '<<');

keyboard.push(...['|', '123', ',', 'space', '.', 'send']);

let row = 0;
keyboard.forEach(k => {
if (k === '|') {
row++;

const postGapCount = row === 4 ? 0 : row - 1;
for (let i = 0; i < postGapCount; i++)
$('<div class="gap" />').appendTo(container);
$('<div class="newline" />').appendTo(container);
const preGapCount = row === 3 ? 0 : row;
for (let i = 0; i < preGapCount; i++)
$('<div class="gap" />').appendTo(container);

return;
}

let className = (k.length === 1) ? 'key' : 'key special';
if (k === ' ') {
k = '.';
className = 'key hidden';
}

className += ' ' + k;

let elKey = $(`<div class="${className}">${k}</div>`)
.appendTo(container);

if (!className.includes('hidden'))
elKey.on('click', this.clickKey.bind(this, k));
});
},

clickKey: function (key) {
window.navigator.vibrate(20);

let elInput = this.find('input');

const handler = {
caps: () => {
this.kbUpper = (this.kbUpper + 1) % 2;
this.renderKeyboard();
},

123: () => {
this.kbUpper = (this.kbUpper === 2) ? 0 : 2;
this.renderKeyboard();
},

space: () => this.clickKey(' '),

'<<': () => {
elInput.val(elInput.val().slice(0, -1));
this.find('.input').html(elInput.val());
},

send: () => {
this.sendChat({ which: 13 });
this.find('.input').html('');
this.find('input').val('');
}
}[key];
if (handler) {
handler();
return;
}

elInput.val(elInput.val() + key);
this.enforceMaxMsgLength();

this.find('.input').html(elInput.val());
},

enforceMaxMsgLength: function () {
let textbox = this.find('input');
let val = textbox.val();
@@ -425,71 +316,6 @@ define([
e.stopPropagation();
},

processChat: function (msgConfig) {
const { message, event: keyboardEvent } = msgConfig;
const { key } = keyboardEvent;
const { el, currentChannel } = this;

const optionContainer = this.find('.channelOptions');

if (message.length) {
if (el.hasClass('picking'))
msgConfig.cancel = true;
return;
}
if (key === 'Enter') {
const selectedSubPick = optionContainer.find('.option.selected');
if (selectedSubPick.length) {
this.onPickSubChannel(selectedSubPick.html(), currentChannel);
return;
}
}

//If we're busy picking a sub channel, we can use keyboard nav
const isPicking = el.hasClass('picking');
const currentSelection = optionContainer.find('.option.selected');
if (isPicking && currentSelection.length) {
const delta = {
ArrowUp: -1,
ArrowDown: 1
}[key];

if (delta) {
const options = optionContainer.find('.option');
const currentIndex = currentSelection.eq(0).index();
let nextIndex = (currentIndex + delta) % options.length;
currentSelection.removeClass('selected');
options.eq(nextIndex).addClass('selected');
}
}

const pick = {
'%': 'party',
'!': 'global',
$: 'custom',
'@': 'direct'
}[key];

if (!pick) {
if (isPicking)
msgConfig.cancel = true;

return;
}

if (currentChannel === pick) {
if (pick === 'direct')
this.lastPrivateChannel = null;
else if (pick === 'custom')
this.lastCustomChannel = null;
}

this.onPickChannel(pick, true);
msgConfig.cancel = true;
},

sendChat: function (e) {
let textbox = this.find('input');
let msgConfig = {
@@ -538,122 +364,6 @@ define([
});

this.toggle();
},

onPickChannel: function (channel, autoPickSub) {
this.currentChannel = channel;
this.currentSubChannel = null;

const showSubChannels = (
['direct', 'custom'].includes(channel) &&
(
!autoPickSub ||
(
channel === 'direct' &&
!this.lastPrivateChannel
) ||
(
channel === 'custom' &&
!this.lastCustomChannel
)
)
);

if (!showSubChannels) {
this.find('.channelOptions').removeClass('active');

let showValue = {
direct: this.lastPrivateChannel,
custom: this.lastCustomChannel
}[channel];

if (channel === 'direct' || channel === 'custom')
this.currentSubChannel = showValue;

showValue = showValue || channel;

this.find('.channelPicker').html(showValue);

this.find('input').focus();

this.el.removeClass('picking');
} else
this.onShowChannelOptions(channel);
},

onPickSubChannel: function (subChannel, channel) {
this.currentSubChannel = subChannel;
this.find('.channelOptions').removeClass('active');
this.find('.channelPicker').html(subChannel);

const elInput = this.find('input');

elInput.focus();

if (channel === 'custom') {
if (subChannel === 'join new') {
elInput.val('/join channelName');
elInput[0].setSelectionRange(6, 17);
} else if (subChannel === 'leave') {
elInput.val('/leave channelName');
elInput[0].setSelectionRange(7, 18);
}
}

this.el.removeClass('picking');
},

onShowChannelOptions: function (currentPick) {
const optionContainer = this.find('.channelOptions')
.addClass('active')
.empty();

const options = [];
let handlerOnClick = this.onPickChannel;

this.el.addClass('picking');

if (!currentPick) {
options.push('global', 'custom');

if (this.privateChannels.length)
options.push('direct');

//Hack...surely we can find a more sane way to do this
if ($('.uiParty .member').length)
options.push('party');
} else {
handlerOnClick = this.onPickSubChannel;
if (currentPick === 'direct')
options.push(...this.privateChannels);
else if (currentPick === 'custom')
options.push(...this.customChannels, 'join new', 'leave');
}

if (!options.length) {
this.onPickChannel('global');
return;
}
let addSelectStyleTo = null;
if (currentPick)
addSelectStyleTo = this.currentSubChannel || options[0];
options.forEach(o => {
const html = `<div class='option'>${o}</div>`;

const el = $(html)
.appendTo(optionContainer)
.on('click', handlerOnClick.bind(this, o, currentPick))
.on('hover', this.stopKeyboardNavForOptions.bind(this));

if (o === addSelectStyleTo)
el.addClass('selected');
});
},

stopKeyboardNavForOptions: function () {
this.find('.channelOptions .option.selected').removeClass('selected');
}
};
});

+ 123
- 0
src/client/ui/templates/messages/mobile.js Целия файл

@@ -0,0 +1,123 @@
define([
'html!ui/templates/messages/tplTab'
], function (
tplTab
) {
const extensionObj = {
renderKeyboard: function () {
this.find('.keyboard').remove();

let container = $('<div class="keyboard"></div>')
.appendTo(this.el);

let keyboard = {
0: 'qwertyuiop|asdfghjkl|zxcvbnm',
1: 'QWERTYUIOP|ASDFGHJKL|ZXCVBNM',
2: '1234567890|@#&*-+=()|_$"\';/'
}[this.kbUpper].split('');

//Hacky: Insert control characters in correct positions
//Backspace goes after 'm'
if (this.kbUpper === 0) {
keyboard.splice(keyboard.indexOf('z'), 0, 'caps');
keyboard.splice(keyboard.indexOf('m') + 1, 0, '<<');
} else if (this.kbUpper === 1) {
keyboard.splice(keyboard.indexOf('Z'), 0, 'caps');
keyboard.splice(keyboard.indexOf('M') + 1, 0, '<<');
} else if (this.kbUpper === 2)
keyboard.splice(keyboard.indexOf('/') + 1, 0, '<<');

keyboard.push(...['|', '123', ',', 'space', '.', 'send']);

let row = 0;
keyboard.forEach(k => {
if (k === '|') {
row++;

const postGapCount = row === 4 ? 0 : row - 1;
for (let i = 0; i < postGapCount; i++)
$('<div class="gap" />').appendTo(container);
$('<div class="newline" />').appendTo(container);
const preGapCount = row === 3 ? 0 : row;
for (let i = 0; i < preGapCount; i++)
$('<div class="gap" />').appendTo(container);

return;
}

let className = (k.length === 1) ? 'key' : 'key special';
if (k === ' ') {
k = '.';
className = 'key hidden';
}

className += ' ' + k;

let elKey = $(`<div class="${className}">${k}</div>`)
.appendTo(container);

if (!className.includes('hidden'))
elKey.on('click', this.clickKey.bind(this, k));
});
},

clickKey: function (key) {
window.navigator.vibrate(20);

let elInput = this.find('input');

const handler = {
caps: () => {
this.kbUpper = (this.kbUpper + 1) % 2;
this.renderKeyboard();
},

123: () => {
this.kbUpper = (this.kbUpper === 2) ? 0 : 2;
this.renderKeyboard();
},

space: () => this.clickKey(' '),

'<<': () => {
elInput.val(elInput.val().slice(0, -1));
this.find('.input').html(elInput.val());
},

send: () => {
this.sendChat({ which: 13 });
this.find('.input').html('');
this.find('input').val('');
}
}[key];
if (handler) {
handler();
return;
}

elInput.val(elInput.val() + key);
this.enforceMaxMsgLength();

this.find('.input').html(elInput.val());
}
};

return {
init: function () {
$.extend(this, extensionObj);

this.kbUpper = 0;

this.el.on('click', this.toggle.bind(this, true));
this.renderKeyboard();

$(tplTab)
.appendTo(this.find('.filters'))
.addClass('btnClose')
.html('x')
.on('click', this.toggle.bind(this, false, true));
}
};
});

+ 17
- 4
src/client/ui/templates/messages/styles.less Целия файл

@@ -198,14 +198,21 @@
display: flex;
justify-content: center;
align-items: center;
color: @blueA;
color: @grayB;
background-color: @blackA;
cursor: pointer;
padding: 0px 10px;

&:after {
content: attr(shortcut);
margin-left: 10px;
color: @grayC;
}

&:hover,
&.selected {
background-color: @grayD;
color: @white;
}

}
@@ -267,9 +274,15 @@
}

&.picking {
> *:not(.channelOptions) {
opacity: 0.4;
pointer-events: none;
&:before {
position: absolute;
content: '';
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background-color: @blackD;
opacity: 0.7;
}

}


Зареждане…
Отказ
Запис