0.2.4 - also fix theme building
This commit is contained in:
parent
6d9fd35805
commit
cf015bd4b7
|
@ -137,7 +137,7 @@
|
|||
|
||||
onKeyDown(e: KeyboardEvent): void {
|
||||
const key = getKey(e);
|
||||
if(e.ctrlKey && !e.shiftKey && key !== 'Control') { //tslint:disable-line:curly
|
||||
if((e.metaKey || e.ctrlKey) && !e.shiftKey && key !== 'Control' && key !== 'Meta') { //tslint:disable-line:curly
|
||||
for(const button of this.buttons)
|
||||
if(button.key === key) {
|
||||
e.stopPropagation();
|
||||
|
|
|
@ -16,10 +16,15 @@
|
|||
<option :value="setting.False">{{l('conversationSettings.false')}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="defaultHighlights">
|
||||
<input type="checkbox" id="defaultHighlights" v-model="defaultHighlights"/>
|
||||
{{l('settings.defaultHighlights')}}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" :for="'highlightWords' + conversation.key">{{l('settings.highlightWords')}}</label>
|
||||
<input :id="'highlightWords' + conversation.key" class="form-control" v-model="highlightWords"
|
||||
:disabled="highlight == setting.Default"/>
|
||||
<input :id="'highlightWords' + conversation.key" class="form-control" v-model="highlightWords"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" :for="'joinMessages' + conversation.key">{{l('settings.joinMessages')}}</label>
|
||||
|
@ -52,6 +57,7 @@
|
|||
highlight: Conversation.Setting;
|
||||
highlightWords: string;
|
||||
joinMessages: Conversation.Setting;
|
||||
defaultHighlights: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -64,6 +70,7 @@
|
|||
this.highlight = settings.highlight;
|
||||
this.highlightWords = settings.highlightWords.join(',');
|
||||
this.joinMessages = settings.joinMessages;
|
||||
this.defaultHighlights = settings.defaultHighlights;
|
||||
};
|
||||
|
||||
@Watch('conversation')
|
||||
|
@ -76,7 +83,8 @@
|
|||
notify: this.notify,
|
||||
highlight: this.highlight,
|
||||
highlightWords: this.highlightWords.split(',').filter((x) => x.length),
|
||||
joinMessages: this.joinMessages
|
||||
joinMessages: this.joinMessages,
|
||||
defaultHighlights: this.defaultHighlights
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@
|
|||
const touch = e instanceof TouchEvent ? e.changedTouches[0] : e;
|
||||
let node = <Node & {character?: Character, channel?: Channel}>touch.target;
|
||||
while(node !== document.body) {
|
||||
if(e.type === 'touchstart' && node === this.$refs['menu']) return;
|
||||
if(node.character !== undefined || node.parentNode === null) break;
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ export class ConversationSettings implements Conversation.Settings {
|
|||
highlight = Conversation.Setting.Default;
|
||||
highlightWords: string[] = [];
|
||||
joinMessages = Conversation.Setting.Default;
|
||||
defaultHighlights = true;
|
||||
}
|
||||
|
||||
export function formatTime(this: void | never, date: Date): string {
|
||||
|
|
|
@ -439,19 +439,33 @@ export default function(this: void): Interfaces.State {
|
|||
for(const item of state.pinned.private) state.getPrivate(core.characters.get(item));
|
||||
queuedJoin(state.pinned.channels.slice());
|
||||
});
|
||||
core.channels.onEvent((type, channel) => {
|
||||
core.channels.onEvent((type, channel, member) => {
|
||||
const key = channel.id.toLowerCase();
|
||||
if(type === 'join') {
|
||||
const conv = new ChannelConversation(channel);
|
||||
state.channelMap[key] = conv;
|
||||
state.channelConversations.push(conv);
|
||||
state.addRecent(conv);
|
||||
} else {
|
||||
if(type === 'join')
|
||||
if(member === undefined) {
|
||||
const conv = new ChannelConversation(channel);
|
||||
state.channelMap[key] = conv;
|
||||
state.channelConversations.push(conv);
|
||||
state.addRecent(conv);
|
||||
} else {
|
||||
const conv = state.channelMap[channel.id]!;
|
||||
if(conv.settings.joinMessages === Interfaces.Setting.False || conv.settings.joinMessages === Interfaces.Setting.Default &&
|
||||
!core.state.settings.joinMessages) return;
|
||||
const text = l('events.channelJoin', `[user]${member.character.name}[/user]`);
|
||||
conv.addMessage(new EventMessage(text));
|
||||
}
|
||||
else if(member === undefined) {
|
||||
const conv = state.channelMap[key]!;
|
||||
state.channelConversations.splice(state.channelConversations.indexOf(conv), 1);
|
||||
delete state.channelMap[key];
|
||||
state.savePinned();
|
||||
if(state.selectedConversation === conv) state.show(state.consoleTab);
|
||||
} else {
|
||||
const conv = state.channelMap[channel.id]!;
|
||||
if(conv.settings.joinMessages === Interfaces.Setting.False || conv.settings.joinMessages === Interfaces.Setting.Default &&
|
||||
!core.state.settings.joinMessages) return;
|
||||
const text = l('events.channelLeave', `[user]${member.character.name}[/user]`);
|
||||
conv.addMessage(new EventMessage(text));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -469,14 +483,10 @@ export default function(this: void): Interfaces.State {
|
|||
const message = createMessage(MessageType.Message, char, decodeHTML(data.message), time);
|
||||
conversation.addMessage(message);
|
||||
|
||||
let words: string[];
|
||||
if(conversation.settings.highlight !== Interfaces.Setting.Default) {
|
||||
words = conversation.settings.highlightWords.slice();
|
||||
if(conversation.settings.highlight === Interfaces.Setting.True) words.push(core.connection.character);
|
||||
} else {
|
||||
words = core.state.settings.highlightWords.slice();
|
||||
if(core.state.settings.highlight) words.push(core.connection.character);
|
||||
}
|
||||
const words = conversation.settings.highlightWords.slice();
|
||||
if(conversation.settings.defaultHighlights) words.push(...core.state.settings.highlightWords);
|
||||
if(conversation.settings.highlight === Interfaces.Setting.Default && core.state.settings.highlight ||
|
||||
conversation.settings.highlight === Interfaces.Setting.True) words.push(core.connection.character);
|
||||
//tslint:disable-next-line:no-null-keyword
|
||||
const results = words.length > 0 ? message.text.match(new RegExp(`\\b(${words.join('|')})\\b`, 'i')) : null;
|
||||
if(results !== null) {
|
||||
|
@ -523,7 +533,7 @@ export default function(this: void): Interfaces.State {
|
|||
const message = new EventMessage(l('events.login', `[user]${data.identity}[/user]`), time);
|
||||
if(isOfInterest(core.characters.get(data.identity))) addEventMessage(message);
|
||||
const conv = state.privateMap[data.identity.toLowerCase()];
|
||||
if(conv !== undefined && core.state.settings.eventMessages && conv !== state.selectedConversation) conv.addMessage(message);
|
||||
if(conv !== undefined && (!core.state.settings.eventMessages || conv !== state.selectedConversation)) conv.addMessage(message);
|
||||
});
|
||||
connection.onMessage('FLN', (data, time) => {
|
||||
const message = new EventMessage(l('events.logout', `[user]${data.character}[/user]`), time);
|
||||
|
@ -531,7 +541,7 @@ export default function(this: void): Interfaces.State {
|
|||
const conv = state.privateMap[data.character.toLowerCase()];
|
||||
if(conv === undefined) return;
|
||||
conv.typingStatus = 'clear';
|
||||
if(core.state.settings.eventMessages && conv !== state.selectedConversation) conv.addMessage(message);
|
||||
if(!core.state.settings.eventMessages || conv !== state.selectedConversation) conv.addMessage(message);
|
||||
});
|
||||
connection.onMessage('TPN', (data) => {
|
||||
const conv = state.privateMap[data.character.toLowerCase()];
|
||||
|
@ -660,22 +670,6 @@ export default function(this: void): Interfaces.State {
|
|||
state.selectedConversation.infoText = data.message;
|
||||
addEventMessage(new EventMessage(data.message, time));
|
||||
});
|
||||
connection.onMessage('JCH', (data, time) => {
|
||||
if(data.character.identity === core.connection.character) return;
|
||||
const conv = state.channelMap[data.channel.toLowerCase()]!;
|
||||
if(conv.settings.joinMessages === Interfaces.Setting.False || conv.settings.joinMessages === Interfaces.Setting.Default &&
|
||||
!core.state.settings.joinMessages) return;
|
||||
const text = l('events.channelJoin', `[user]${data.character.identity}[/user]`);
|
||||
conv.addMessage(new EventMessage(text, time));
|
||||
});
|
||||
connection.onMessage('LCH', (data, time) => {
|
||||
if(data.character === core.connection.character) return;
|
||||
const conv = state.channelMap[data.channel.toLowerCase()]!;
|
||||
if(conv.settings.joinMessages === Interfaces.Setting.False || conv.settings.joinMessages === Interfaces.Setting.Default &&
|
||||
!core.state.settings.joinMessages) return;
|
||||
const text = l('events.channelLeave', `[user]${data.character}[/user]`);
|
||||
conv.addMessage(new EventMessage(text, time));
|
||||
});
|
||||
connection.onMessage('ZZZ', (data, time) => {
|
||||
state.selectedConversation.infoText = data.message;
|
||||
addEventMessage(new EventMessage(data.message, time));
|
||||
|
|
|
@ -92,6 +92,7 @@ export namespace Conversation {
|
|||
readonly highlight: Setting;
|
||||
readonly highlightWords: ReadonlyArray<string>;
|
||||
readonly joinMessages: Setting;
|
||||
readonly defaultHighlights: boolean;
|
||||
}
|
||||
|
||||
export const enum UnreadState { None, Unread, Mention }
|
||||
|
|
|
@ -8,7 +8,7 @@ const strings: {[key: string]: string | undefined} = {
|
|||
'action.copyLink': 'Copy Link',
|
||||
'action.suggestions': 'Suggestions',
|
||||
'action.open': 'Show',
|
||||
'action.quit': 'Exit',
|
||||
'action.quit': 'Quit',
|
||||
'action.updateAvailable': 'UPDATE AVAILABLE',
|
||||
'action.update': 'Restart now!',
|
||||
'action.cancel': 'Cancel',
|
||||
|
@ -18,6 +18,7 @@ const strings: {[key: string]: string | undefined} = {
|
|||
'help.rules': 'F-List Rules',
|
||||
'help.faq': 'F-List FAQ',
|
||||
'help.report': 'How to report a user',
|
||||
'help.changelog': 'Changelog',
|
||||
'title': 'FChat 3.0',
|
||||
'version': 'Version {0}',
|
||||
'filter': 'Type to filter...',
|
||||
|
@ -130,6 +131,7 @@ Are you sure?`,
|
|||
'settings.theme': 'Theme',
|
||||
'settings.logMessages': 'Log messages',
|
||||
'settings.logAds': 'Log ads',
|
||||
'settings.defaultHighlights': 'Use global highlight words',
|
||||
'conversationSettings.title': 'Settings',
|
||||
'conversationSettings.action': 'Edit settings for {0}',
|
||||
'conversationSettings.default': 'Default',
|
||||
|
@ -344,7 +346,7 @@ Are you sure?`,
|
|||
'status.dnd': 'Do Not Disturb',
|
||||
'status.idle': 'Idle',
|
||||
'status.offline': 'Offline',
|
||||
'status.crown': 'Rewarded by Admin',
|
||||
'status.crown': 'Rewarded',
|
||||
'importer.importGeneral': 'slimCat data has been detected on your computer.\nWould you like to import general settings?',
|
||||
'importer.importCharacter': 'slimCat data for this character has been detected on your computer.\nWould you like to import settings and logs?\nThis may take a while.\nAny existing FChat 3.0 data for this character will be overwritten.',
|
||||
'importer.importing': 'Importing data',
|
||||
|
|
|
@ -42,7 +42,7 @@ export function parse(this: void | never, input: string, context: CommandContext
|
|||
switch(param.type) {
|
||||
case ParamType.String:
|
||||
if(i === command.params.length - 1) values[i] = args.substr(index);
|
||||
continue;
|
||||
break;
|
||||
case ParamType.Enum:
|
||||
if((param.options !== undefined ? param.options : []).indexOf(value) === -1)
|
||||
return l('commands.invalidParam', l(`commands.${name}.param${i}`));
|
||||
|
@ -62,7 +62,7 @@ export function parse(this: void | never, input: string, context: CommandContext
|
|||
const char = core.characters.get(value);
|
||||
if(char.status === 'offline') return l('commands.invalidCharacter');
|
||||
}
|
||||
index = endIndex + 1;
|
||||
index = endIndex === -1 ? args.length : endIndex + 1;
|
||||
}
|
||||
if(command.context !== undefined)
|
||||
return function(this: Conversation): void {
|
||||
|
|
|
@ -44,7 +44,8 @@ const UserView = Vue.extend({
|
|||
else rankIcon = '';
|
||||
} else rankIcon = '';
|
||||
|
||||
const html = (props.showStatus !== undefined ? `<span class="fa fa-fw ${getStatusIcon(character.status)}"></span>` : '') +
|
||||
const html = (props.showStatus !== undefined || character.status === 'crown'
|
||||
? `<span class="fa fa-fw ${getStatusIcon(character.status)}"></span>` : '') +
|
||||
(rankIcon !== '' ? `<span class="fa ${rankIcon}"></span>` : '') + character.name;
|
||||
return createElement('span', {
|
||||
attrs: {class: `user-view gender-${character.gender !== undefined ? character.gender.toLowerCase() : 'none'}`},
|
||||
|
|
|
@ -59,6 +59,10 @@
|
|||
import {GeneralSettings, getGeneralSettings, Logs, setGeneralSettings, SettingsStore} from './filesystem';
|
||||
import Notifications from './notifications';
|
||||
|
||||
function confirmBack(): void {
|
||||
if(confirm(l('chat.confirmLeave'))) (<Navigator & {app: {exitApp(): void}}>navigator).app.exitApp();
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: {chat: Chat, modal: Modal}
|
||||
})
|
||||
|
@ -105,9 +109,11 @@
|
|||
const connection = new Connection(Socket, this.settings!.account, this.settings!.password);
|
||||
connection.onEvent('connected', () => {
|
||||
Raven.setUserContext({username: core.connection.character});
|
||||
document.addEventListener('backbutton', confirmBack);
|
||||
});
|
||||
connection.onEvent('closed', () => {
|
||||
Raven.setUserContext();
|
||||
document.removeEventListener('backbutton', confirmBack);
|
||||
});
|
||||
initCore(connection, Logs, SettingsStore, Notifications);
|
||||
this.characters = data.characters.sort();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
</description>
|
||||
<author email="maya@f-list.net" href="https://www.f-list.net">The F-list Team</author>
|
||||
<content src="index.html" />
|
||||
<icon src="../electron/build/icon.png" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
|
|
|
@ -27,7 +27,7 @@ export class GeneralSettings {
|
|||
account = '';
|
||||
password = '';
|
||||
host = 'wss://chat.f-list.net:9799';
|
||||
theme = 'dark';
|
||||
theme = 'default';
|
||||
}
|
||||
|
||||
type Index = {[key: string]: {name: string, index: {[key: number]: number | undefined}} | undefined};
|
||||
|
@ -99,12 +99,13 @@ function serializeMessage(message: Conversation.Message): Blob {
|
|||
dv.setUint8(5, senderLength);
|
||||
const textLength = getByteLength(message.text);
|
||||
dv.setUint16(6, textLength);
|
||||
return new Blob([buffer, name, message.text, String.fromCharCode(senderLength + textLength + 10)]);
|
||||
const length = senderLength + textLength + 8;
|
||||
return new Blob([buffer, name, message.text, String.fromCharCode(length >> 255), String.fromCharCode(length % 255)]);
|
||||
}
|
||||
|
||||
function deserializeMessage(buffer: ArrayBuffer): {message: Conversation.Message, end: number} {
|
||||
const dv = new DataView(buffer, 0, 8);
|
||||
const time = dv.getUint32(0);
|
||||
const time = dv.getUint32(0) * 1000;
|
||||
const type = dv.getUint8(4);
|
||||
const senderLength = dv.getUint8(5);
|
||||
const messageLength = dv.getUint16(6);
|
||||
|
@ -183,7 +184,7 @@ export class Logs implements Logging.Persistent {
|
|||
let messages = new Array<Conversation.Message>(count);
|
||||
let pos = file.size;
|
||||
while(pos > 0 && count > 0) {
|
||||
const length = new DataView(await readAsArrayBuffer(file)).getUint16(0);
|
||||
const length = new DataView(await readAsArrayBuffer(file.slice(pos - 2, pos))).getUint16(0);
|
||||
pos = pos - length - 2;
|
||||
messages[--count] = deserializeMessage(await readAsArrayBuffer(file.slice(pos, pos + length))).message;
|
||||
}
|
||||
|
|
|
@ -88,7 +88,6 @@
|
|||
{label: l('action.open'), click: () => mainWindow!.show()},
|
||||
{
|
||||
label: l('action.quit'),
|
||||
role: 'quit',
|
||||
click: () => {
|
||||
isClosing = true;
|
||||
mainWindow!.close();
|
||||
|
@ -201,7 +200,13 @@
|
|||
},
|
||||
{type: 'separator'},
|
||||
{role: 'minimize'},
|
||||
{role: 'quit'}
|
||||
process.platform === 'darwin' ? {role: 'quit'} : {
|
||||
label: l('action.quit'),
|
||||
click(): void {
|
||||
isClosing = true;
|
||||
mainWindow!.close();
|
||||
}
|
||||
}
|
||||
];
|
||||
electron.remote.Menu.setApplicationMenu(electron.remote.Menu.buildFromTemplate(appMenu));
|
||||
|
||||
|
@ -220,6 +225,9 @@
|
|||
electron.ipcRenderer.send('install-update');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: l('help.changelog'),
|
||||
click: () => electron.shell.openExternal('https://wiki.f-list.net/F-Chat_3.0#Changelog')
|
||||
}])
|
||||
}));
|
||||
electron.remote.Menu.setApplicationMenu(menu);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "fchat",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.4",
|
||||
"author": "The F-List Team",
|
||||
"description": "F-List.net Chat Client",
|
||||
"main": "main.js",
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 969 B |
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
|
@ -98,7 +98,7 @@ function createMessage(line: string, ownCharacter: string, name: string, isChann
|
|||
let endIndex = line.indexOf('[', lineIndex += 6);
|
||||
if(endIndex - lineIndex > 20) endIndex = lineIndex + 20;
|
||||
sender = line.substring(lineIndex, endIndex);
|
||||
text = line.substring(endIndex + 6, 65535);
|
||||
text = line.substring(endIndex + 6, 50000);
|
||||
} else {
|
||||
if(lineIndex + ownCharacter.length <= line.length && line.substr(lineIndex, ownCharacter.length) === ownCharacter)
|
||||
sender = ownCharacter;
|
||||
|
@ -117,7 +117,7 @@ function createMessage(line: string, ownCharacter: string, name: string, isChann
|
|||
lineIndex += 3;
|
||||
}
|
||||
} else type = Conversation.Message.Type.Action;
|
||||
text = line.substr(lineIndex, 65535);
|
||||
text = line.substr(lineIndex, 50000);
|
||||
}
|
||||
return {type, sender: {name: sender}, text, time: addMinutes(date, h * 60 + m)};
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ function sortMember(this: void | never, array: Interfaces.Member[], member: Inte
|
|||
if(member.character.isChatOp && !other.character.isChatOp) break;
|
||||
if(other.rank > member.rank) continue;
|
||||
if(member.rank > other.rank) break;
|
||||
if(other.character.isFriend && !member.character.isFriend) continue;
|
||||
if(member.character.isFriend && !other.character.isFriend) break;
|
||||
if(other.character.isBookmarked && !member.character.isBookmarked) continue;
|
||||
if(member.character.isBookmarked && !other.character.isBookmarked) break;
|
||||
if(name < other.character.name) break;
|
||||
}
|
||||
array.splice(i, 0, member);
|
||||
|
@ -37,6 +41,7 @@ class Channel implements Interfaces.Channel {
|
|||
addMember(member: Interfaces.Member): void {
|
||||
this.members[member.character.name] = member;
|
||||
sortMember(this.sortedMembers, member);
|
||||
for(const handler of state.handlers) handler('join', this, member);
|
||||
}
|
||||
|
||||
removeMember(name: string): void {
|
||||
|
@ -44,6 +49,7 @@ class Channel implements Interfaces.Channel {
|
|||
if(member !== undefined) {
|
||||
delete this.members[name];
|
||||
this.sortedMembers.splice(this.sortedMembers.indexOf(member), 1);
|
||||
for(const handler of state.handlers) handler('leave', this, member);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,12 +111,17 @@ let state: State;
|
|||
export default function(this: void, connection: Connection, characters: Character.State): Interfaces.State {
|
||||
state = new State(connection);
|
||||
let getChannelTimer: NodeJS.Timer | undefined;
|
||||
connection.onEvent('connecting', () => {
|
||||
let rejoin: string[] | undefined;
|
||||
connection.onEvent('connecting', (isReconnect) => {
|
||||
if(isReconnect) rejoin = Object.keys(state.joinedMap);
|
||||
state.joinedChannels = [];
|
||||
state.joinedMap = {};
|
||||
});
|
||||
connection.onEvent('connected', (isReconnect) => {
|
||||
if(isReconnect) queuedJoin(Object.keys(state.joinedChannels));
|
||||
connection.onEvent('connected', () => {
|
||||
if(rejoin !== undefined) {
|
||||
queuedJoin(rejoin);
|
||||
rejoin = undefined;
|
||||
}
|
||||
const getChannels = () => {
|
||||
connection.send('CHA');
|
||||
connection.send('ORS');
|
||||
|
@ -152,7 +163,8 @@ export default function(this: void, connection: Connection, characters: Characte
|
|||
if(item !== undefined) item.isJoined = true;
|
||||
} else {
|
||||
const channel = state.getChannel(data.channel)!;
|
||||
channel.addMember(channel.createMember(characters.get(data.character.identity)));
|
||||
const member = channel.createMember(characters.get(data.character.identity));
|
||||
channel.addMember(member);
|
||||
if(item !== undefined) item.memberCount++;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -128,22 +128,28 @@ export default function(this: void, connection: Connection): Interfaces.State {
|
|||
char.isChatOp = false;
|
||||
});
|
||||
connection.onMessage('RTB', (data) => {
|
||||
if(data.type !== 'trackadd' && data.type !== 'trackrem' && data.type !== 'friendadd' && data.type !== 'friendremove') return;
|
||||
const character = state.get(data.name);
|
||||
switch(data.type) {
|
||||
case 'trackadd':
|
||||
state.bookmarkList.push(data.name);
|
||||
state.get(data.name).isBookmarked = true;
|
||||
character.isBookmarked = true;
|
||||
if(character.status !== 'offline') state.bookmarks.push(character);
|
||||
break;
|
||||
case 'trackrem':
|
||||
state.bookmarkList.splice(state.bookmarkList.indexOf(data.name), 1);
|
||||
state.get(data.name).isBookmarked = false;
|
||||
character.isBookmarked = false;
|
||||
if(character.status !== 'offline') state.bookmarks.splice(state.bookmarks.indexOf(character), 1);
|
||||
break;
|
||||
case 'friendadd':
|
||||
state.friendList.push(data.name);
|
||||
state.get(data.name).isFriend = true;
|
||||
character.isFriend = true;
|
||||
if(character.status !== 'offline') state.friends.push(character);
|
||||
break;
|
||||
case 'friendremove':
|
||||
state.friendList.splice(state.friendList.indexOf(data.name), 1);
|
||||
state.get(data.name).isFriend = false;
|
||||
character.isFriend = false;
|
||||
if(character.status !== 'offline') state.friends.splice(state.friends.indexOf(character), 1);
|
||||
}
|
||||
});
|
||||
return state;
|
||||
|
|
|
@ -180,7 +180,7 @@ export namespace Character {
|
|||
export type Character = Character.Character;
|
||||
|
||||
export namespace Channel {
|
||||
export type EventHandler = (type: 'join' | 'leave', channel: Channel) => void;
|
||||
export type EventHandler = (type: 'join' | 'leave', channel: Channel, member?: Member) => void;
|
||||
|
||||
export interface State {
|
||||
readonly officialChannels: {readonly [key: string]: (ListItem | undefined)};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bootstrap": "^3.3.7",
|
||||
"font-awesome": "^4.7.0",
|
||||
"less": "^2.7.2",
|
||||
"less-plugin-npm-import": "^2.1.0"
|
||||
},
|
|
@ -115,6 +115,10 @@ fast-deep-equal@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
|
||||
|
||||
font-awesome@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
|
||||
|
||||
forever-agent@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
|
@ -31,9 +31,9 @@ See https://electron.atom.io/docs/tutorial/application-distribution/
|
|||
|
||||
## Building a custom theme
|
||||
See [the wiki](https://wiki.f-list.net/F-Chat_3.0/Themes) for instructions on how to create a custom theme.
|
||||
- Change into the `less/themes/chat` directory.
|
||||
- Change into the `less` directory.
|
||||
- Run `yarn install`.
|
||||
- Run `yarn build {name}.less {name}.css`.
|
||||
- Run `yarn build themes/chat/{name}.less {name}.css`.
|
||||
|
||||
## Dependencies
|
||||
Note: Adding *and upgrading* dependencies should only be done with prior consideration and subsequent testing.
|
||||
|
|
Loading…
Reference in New Issue