3.0.16
This commit is contained in:
		
							parent
							
								
									8b0fe1aaed
								
							
						
					
					
						commit
						200c82499d
					
				@ -79,6 +79,19 @@ export class CoreBBCodeParser extends BBCodeParser {
 | 
				
			|||||||
            element.appendChild(span);
 | 
					            element.appendChild(span);
 | 
				
			||||||
            return element;
 | 
					            return element;
 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
 | 
					        this.addTag(new BBCodeCustomTag('spoiler', (parser, parent) => {
 | 
				
			||||||
 | 
					            const link = parser.createElement('a');
 | 
				
			||||||
 | 
					            const content = parser.createElement('span');
 | 
				
			||||||
 | 
					            link.href = '#';
 | 
				
			||||||
 | 
					            link.onclick = (e) => {
 | 
				
			||||||
 | 
					                const target = e.target as HTMLElement;
 | 
				
			||||||
 | 
					                target.parentElement!.replaceChild(content, target);
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            link.appendChild(document.createTextNode('[click to show spoiler]'));
 | 
				
			||||||
 | 
					            parent.appendChild(link);
 | 
				
			||||||
 | 
					            return content;
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parseEverything(input: string): HTMLElement {
 | 
					    parseEverything(input: string): HTMLElement {
 | 
				
			||||||
 | 
				
			|||||||
@ -89,6 +89,12 @@ export let defaultButtons: ReadonlyArray<EditorButton> = [
 | 
				
			|||||||
        icon: 'fa-smile',
 | 
					        icon: 'fa-smile',
 | 
				
			||||||
        key: Keys.KeyE
 | 
					        key: Keys.KeyE
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        title: 'Spoiler (Ctrl+K)\n\nHidden until explicitly clicked by the viewer.',
 | 
				
			||||||
 | 
					        tag: 'spoiler',
 | 
				
			||||||
 | 
					        icon: 'fa-eye-slash',
 | 
				
			||||||
 | 
					        key: Keys.KeyK
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        title: 'Noparse (Ctrl+N)\n\nAll BBCode placed within this tag will be ignored and treated as text. Great for sharing structure without it being rendered.',
 | 
					        title: 'Noparse (Ctrl+N)\n\nAll BBCode placed within this tag will be ignored and treated as text. Great for sharing structure without it being rendered.',
 | 
				
			||||||
        tag: 'noparse',
 | 
					        tag: 'noparse',
 | 
				
			||||||
 | 
				
			|||||||
@ -77,7 +77,7 @@ export class BBCodeParser {
 | 
				
			|||||||
        const parent = document.createElement('span');
 | 
					        const parent = document.createElement('span');
 | 
				
			||||||
        parent.className = 'bbcode';
 | 
					        parent.className = 'bbcode';
 | 
				
			||||||
        this._currentTag = {tag: '<root>', line: 1, column: 1};
 | 
					        this._currentTag = {tag: '<root>', line: 1, column: 1};
 | 
				
			||||||
        this.parse(input, 0, undefined, parent, () => true);
 | 
					        this.parse(input, 0, undefined, parent, () => true, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //if(process.env.NODE_ENV !== 'production' && this._warnings.length > 0)
 | 
					        //if(process.env.NODE_ENV !== 'production' && this._warnings.length > 0)
 | 
				
			||||||
        //    console.log(this._warnings);
 | 
					        //    console.log(this._warnings);
 | 
				
			||||||
@ -115,7 +115,7 @@ export class BBCodeParser {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private parse(input: string, start: number, self: BBCodeTag | undefined, parent: HTMLElement | undefined,
 | 
					    private parse(input: string, start: number, self: BBCodeTag | undefined, parent: HTMLElement | undefined,
 | 
				
			||||||
                  isAllowed: (tag: string) => boolean): number {
 | 
					                  isAllowed: (tag: string) => boolean, depth: number): number {
 | 
				
			||||||
        let currentTag = this._currentTag;
 | 
					        let currentTag = this._currentTag;
 | 
				
			||||||
        const selfAllowed = self !== undefined ? isAllowed(self.tag) : true;
 | 
					        const selfAllowed = self !== undefined ? isAllowed(self.tag) : true;
 | 
				
			||||||
        if(self !== undefined) {
 | 
					        if(self !== undefined) {
 | 
				
			||||||
@ -157,21 +157,21 @@ export class BBCodeParser {
 | 
				
			|||||||
                        parent.appendChild(document.createTextNode(input.substring(mark, allowed ? tagStart : i + 1)));
 | 
					                        parent.appendChild(document.createTextNode(input.substring(mark, allowed ? tagStart : i + 1)));
 | 
				
			||||||
                        mark = i + 1;
 | 
					                        mark = i + 1;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    if(!allowed || parent === undefined) {
 | 
					                    if(!allowed || parent === undefined || depth > 100) {
 | 
				
			||||||
                        i = this.parse(input, i + 1, tag, parent, isAllowed);
 | 
					                        i = this.parse(input, i + 1, tag, parent, isAllowed, depth + 1);
 | 
				
			||||||
                        mark = i + 1;
 | 
					                        mark = i + 1;
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    let element: HTMLElement | undefined;
 | 
					                    let element: HTMLElement | undefined;
 | 
				
			||||||
                    if(tag instanceof BBCodeTextTag) {
 | 
					                    if(tag instanceof BBCodeTextTag) {
 | 
				
			||||||
                        i = this.parse(input, i + 1, tag, undefined, isAllowed);
 | 
					                        i = this.parse(input, i + 1, tag, undefined, isAllowed, depth + 1);
 | 
				
			||||||
                        element = tag.createElement(this, parent, param, input.substring(mark, input.lastIndexOf('[', i)));
 | 
					                        element = tag.createElement(this, parent, param, input.substring(mark, input.lastIndexOf('[', i)));
 | 
				
			||||||
                        if(element === undefined) parent.appendChild(document.createTextNode(input.substring(tagStart, i + 1)));
 | 
					                        if(element === undefined) parent.appendChild(document.createTextNode(input.substring(tagStart, i + 1)));
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        element = tag.createElement(this, parent, param, '');
 | 
					                        element = tag.createElement(this, parent, param, '');
 | 
				
			||||||
                        if(element === undefined) parent.appendChild(document.createTextNode(input.substring(tagStart, i + 1)));
 | 
					                        if(element === undefined) parent.appendChild(document.createTextNode(input.substring(tagStart, i + 1)));
 | 
				
			||||||
                        if(!tag.noClosingTag)
 | 
					                        if(!tag.noClosingTag)
 | 
				
			||||||
                            i = this.parse(input, i + 1, tag, element !== undefined ? element : parent, isAllowed);
 | 
					                            i = this.parse(input, i + 1, tag, element !== undefined ? element : parent, isAllowed, depth + 1);
 | 
				
			||||||
                        if(element === undefined)
 | 
					                        if(element === undefined)
 | 
				
			||||||
                            parent.appendChild(document.createTextNode(input.substring(input.lastIndexOf('[', i), i + 1)));
 | 
					                            parent.appendChild(document.createTextNode(input.substring(input.lastIndexOf('[', i), i + 1)));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
				
			|||||||
@ -47,7 +47,8 @@
 | 
				
			|||||||
                    class="fa fa-download"></span></button>
 | 
					                    class="fa fa-download"></span></button>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="messages messages-both" style="overflow:auto;overscroll-behavior:none;" ref="messages" tabindex="-1" @scroll="onMessagesScroll">
 | 
					        <div class="messages messages-both" style="overflow:auto;overscroll-behavior:none;" ref="messages" tabindex="-1"
 | 
				
			||||||
 | 
					             @scroll="onMessagesScroll">
 | 
				
			||||||
            <message-view v-for="message in displayedMessages" :message="message" :key="message.id" :logs="true"></message-view>
 | 
					            <message-view v-for="message in displayedMessages" :message="message" :key="message.id" :logs="true"></message-view>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="input-group" style="flex-shrink:0">
 | 
					        <div class="input-group" style="flex-shrink:0">
 | 
				
			||||||
@ -77,8 +78,15 @@
 | 
				
			|||||||
        return format(date, 'YYYY-MM-DD');
 | 
					        return format(date, 'YYYY-MM-DD');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function getLogs(messages: ReadonlyArray<Conversation.Message>): string {
 | 
					    function getLogs(messages: ReadonlyArray<Conversation.Message>, html: boolean): string {
 | 
				
			||||||
        return messages.reduce((acc, x) => acc + messageToString(x, (date) => formatTime(date, true)), '');
 | 
					        const start = html ?
 | 
				
			||||||
 | 
					            `<meta charset="utf-8"><style>body { padding: 10px; }${document.getElementById('themeStyle')!.innerText}</style>` : '';
 | 
				
			||||||
 | 
					        return '<div class="messages bbcode">' + messages.reduce((acc, x) => acc + messageToString(x, (date) => formatTime(date, true),
 | 
				
			||||||
 | 
					            html ? (c) => {
 | 
				
			||||||
 | 
					                const gender = core.characters.get(c).gender;
 | 
				
			||||||
 | 
					                return `<span class="user-view gender-${gender ? gender.toLowerCase() : 'none'}">${c}</span>`;
 | 
				
			||||||
 | 
					            } : undefined,
 | 
				
			||||||
 | 
					            html ? (t) => `${core.bbCodeParser.parseEverything(t).innerHTML}` : undefined), start) + '</div>';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Component({
 | 
					    @Component({
 | 
				
			||||||
@ -187,16 +195,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        downloadDay(): void {
 | 
					        downloadDay(): void {
 | 
				
			||||||
            if(this.selectedConversation === undefined || this.selectedDate === undefined || this.messages.length === 0) return;
 | 
					            if(this.selectedConversation === undefined || this.selectedDate === undefined || this.messages.length === 0) return;
 | 
				
			||||||
            const name = `${this.selectedConversation.name}-${formatDate(new Date(this.selectedDate))}.txt`;
 | 
					            const html = confirm(l('logs.html'));
 | 
				
			||||||
            this.download(name, `data:${encodeURIComponent(name)},${encodeURIComponent(getLogs(this.messages))}`);
 | 
					            const name = `${this.selectedConversation.name}-${formatDate(new Date(this.selectedDate))}.${html ? 'html' : 'txt'}`;
 | 
				
			||||||
 | 
					            this.download(name, `data:${encodeURIComponent(name)},${encodeURIComponent(getLogs(this.messages, html))}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async downloadConversation(): Promise<void> {
 | 
					        async downloadConversation(): Promise<void> {
 | 
				
			||||||
            if(this.selectedConversation === undefined) return;
 | 
					            if(this.selectedConversation === undefined) return;
 | 
				
			||||||
            const zip = new Zip();
 | 
					            const zip = new Zip();
 | 
				
			||||||
 | 
					            const html = confirm(l('logs.html'));
 | 
				
			||||||
            for(const date of this.dates) {
 | 
					            for(const date of this.dates) {
 | 
				
			||||||
                const messages = await core.logs.getLogs(this.selectedCharacter, this.selectedConversation.key, date);
 | 
					                const messages = await core.logs.getLogs(this.selectedCharacter, this.selectedConversation.key, date);
 | 
				
			||||||
                zip.addFile(`${formatDate(date)}.txt`, getLogs(messages));
 | 
					                zip.addFile(`${formatDate(date)}.${html ? 'html' : 'txt'}`, getLogs(messages, html));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            this.download(`${this.selectedConversation.name}.zip`, URL.createObjectURL(zip.build()));
 | 
					            this.download(`${this.selectedConversation.name}.zip`, URL.createObjectURL(zip.build()));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -204,12 +214,13 @@
 | 
				
			|||||||
        async downloadCharacter(): Promise<void> {
 | 
					        async downloadCharacter(): Promise<void> {
 | 
				
			||||||
            if(this.selectedCharacter === '' || !confirm(l('logs.confirmExport', this.selectedCharacter))) return;
 | 
					            if(this.selectedCharacter === '' || !confirm(l('logs.confirmExport', this.selectedCharacter))) return;
 | 
				
			||||||
            const zip = new Zip();
 | 
					            const zip = new Zip();
 | 
				
			||||||
 | 
					            const html = confirm(l('logs.html'));
 | 
				
			||||||
            for(const conv of this.conversations) {
 | 
					            for(const conv of this.conversations) {
 | 
				
			||||||
                zip.addFile(`${conv.name}/`, '');
 | 
					                zip.addFile(`${conv.name}/`, '');
 | 
				
			||||||
                const dates = await core.logs.getLogDates(this.selectedCharacter, conv.key);
 | 
					                const dates = await core.logs.getLogDates(this.selectedCharacter, conv.key);
 | 
				
			||||||
                for(const date of dates) {
 | 
					                for(const date of dates) {
 | 
				
			||||||
                    const messages = await core.logs.getLogs(this.selectedCharacter, conv.key, date);
 | 
					                    const messages = await core.logs.getLogs(this.selectedCharacter, conv.key, date);
 | 
				
			||||||
                    zip.addFile(`${conv.name}/${formatDate(date)}.txt`, getLogs(messages));
 | 
					                    zip.addFile(`${conv.name}/${formatDate(date)}.${html ? 'html' : 'txt'}`, getLogs(messages, html));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            this.download(`${this.selectedCharacter}.zip`, URL.createObjectURL(zip.build()));
 | 
					            this.download(`${this.selectedCharacter}.zip`, URL.createObjectURL(zip.build()));
 | 
				
			||||||
@ -272,9 +283,11 @@
 | 
				
			|||||||
            if(this.lockScroll) return;
 | 
					            if(this.lockScroll) return;
 | 
				
			||||||
            if(list === undefined || ev !== undefined && Math.abs(list.scrollTop - this.lastScroll) < 50) return;
 | 
					            if(list === undefined || ev !== undefined && Math.abs(list.scrollTop - this.lastScroll) < 50) return;
 | 
				
			||||||
            this.lockScroll = true;
 | 
					            this.lockScroll = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            function getTop(index: number): number {
 | 
					            function getTop(index: number): number {
 | 
				
			||||||
                return (<HTMLElement>list!.children[index]).offsetTop;
 | 
					                return (<HTMLElement>list!.children[index]).offsetTop;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            while(this.selectedConversation !== undefined && this.selectedDate === undefined && this.dialog.isShown) {
 | 
					            while(this.selectedConversation !== undefined && this.selectedDate === undefined && this.dialog.isShown) {
 | 
				
			||||||
                const oldHeight = list.scrollHeight, oldTop = list.scrollTop;
 | 
					                const oldHeight = list.scrollHeight, oldTop = list.scrollTop;
 | 
				
			||||||
                const oldFirst = this.displayedMessages[0];
 | 
					                const oldFirst = this.displayedMessages[0];
 | 
				
			||||||
 | 
				
			|||||||
@ -61,12 +61,14 @@ export function formatTime(this: void | never, date: Date, noDate: boolean = fal
 | 
				
			|||||||
    return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
 | 
					    return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function messageToString(this: void | never, msg: Conversation.Message, timeFormatter: (date: Date) => string = formatTime): string {
 | 
					export function messageToString(this: void | never, msg: Conversation.Message, timeFormatter: (date: Date) => string = formatTime,
 | 
				
			||||||
 | 
					                                characterTransform: (str: string) => string = (x) => x,
 | 
				
			||||||
 | 
					                                textTransform: (str: string) => string = (x) => x): string {
 | 
				
			||||||
    let text = `[${timeFormatter(msg.time)}] `;
 | 
					    let text = `[${timeFormatter(msg.time)}] `;
 | 
				
			||||||
    if(msg.type !== Conversation.Message.Type.Event)
 | 
					    if(msg.type !== Conversation.Message.Type.Event)
 | 
				
			||||||
        text += (msg.type === Conversation.Message.Type.Action ? '*' : '') + msg.sender.name +
 | 
					        text += (msg.type === Conversation.Message.Type.Action ? '*' : '') + characterTransform(msg.sender.name) +
 | 
				
			||||||
            (msg.type === Conversation.Message.Type.Message ? ':' : '');
 | 
					            (msg.type === Conversation.Message.Type.Message ? ':' : '');
 | 
				
			||||||
    return `${text} ${msg.text}\r\n`;
 | 
					    return `${text} ${textTransform(msg.text)}\r\n`;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getKey(e: KeyboardEvent): Keys {
 | 
					export function getKey(e: KeyboardEvent): Keys {
 | 
				
			||||||
 | 
				
			|||||||
@ -44,12 +44,6 @@ const vue = <Vue & VueState>new Vue({
 | 
				
			|||||||
        characters: undefined,
 | 
					        characters: undefined,
 | 
				
			||||||
        conversations: undefined,
 | 
					        conversations: undefined,
 | 
				
			||||||
        state
 | 
					        state
 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    watch: {
 | 
					 | 
				
			||||||
        'state.hiddenUsers': async(newValue: string[], oldValue: string[]) => {
 | 
					 | 
				
			||||||
            if(data.settingsStore !== undefined && newValue !== oldValue)
 | 
					 | 
				
			||||||
                await data.settingsStore.set('hiddenUsers', newValue);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -86,6 +80,9 @@ export function init(this: void, connection: Connection, logsClass: new() => Log
 | 
				
			|||||||
    data.register('characters', Characters(connection));
 | 
					    data.register('characters', Characters(connection));
 | 
				
			||||||
    data.register('channels', Channels(connection, core.characters));
 | 
					    data.register('channels', Channels(connection, core.characters));
 | 
				
			||||||
    data.register('conversations', Conversations());
 | 
					    data.register('conversations', Conversations());
 | 
				
			||||||
 | 
					    data.watch(() => state.hiddenUsers, async(newValue) => {
 | 
				
			||||||
 | 
					        if(data.settingsStore !== undefined) await data.settingsStore.set('hiddenUsers', newValue);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    connection.onEvent('connecting', async() => {
 | 
					    connection.onEvent('connecting', async() => {
 | 
				
			||||||
        await data.reloadSettings();
 | 
					        await data.reloadSettings();
 | 
				
			||||||
        data.bbCodeParser = createBBCodeParser();
 | 
					        data.bbCodeParser = createBBCodeParser();
 | 
				
			||||||
 | 
				
			|||||||
@ -92,6 +92,7 @@ const strings: {[key: string]: string | undefined} = {
 | 
				
			|||||||
    'logs.corruption.mobile.success': 'Your logs have been fixed.',
 | 
					    'logs.corruption.mobile.success': 'Your logs have been fixed.',
 | 
				
			||||||
    'logs.corruption.mobile.error': 'Unable to fix corrupted logs. Please clear the application data or reinstall the app.',
 | 
					    'logs.corruption.mobile.error': 'Unable to fix corrupted logs. Please clear the application data or reinstall the app.',
 | 
				
			||||||
    'logs.corruption.web': 'Error reading logs from browser storage. If this issue persists, please clear your stored browser data for F-Chat.',
 | 
					    'logs.corruption.web': 'Error reading logs from browser storage. If this issue persists, please clear your stored browser data for F-Chat.',
 | 
				
			||||||
 | 
					    'logs.html': 'Would you like to export these logs as HTML with formatting? Otherwise, they will be exported as plain text.',
 | 
				
			||||||
    'user.profile': 'Profile',
 | 
					    'user.profile': 'Profile',
 | 
				
			||||||
    'user.message': 'Open conversation',
 | 
					    'user.message': 'Open conversation',
 | 
				
			||||||
    'user.messageJump': 'View conversation',
 | 
					    'user.messageJump': 'View conversation',
 | 
				
			||||||
 | 
				
			|||||||
@ -256,7 +256,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        get styling(): string {
 | 
					        get styling(): string {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                return `<style>${fs.readFileSync(path.join(__dirname, `themes/${this.settings.theme}.css`))}</style>`;
 | 
					                return `<style id="themeStyle">${fs.readFileSync(path.join(__dirname, `themes/${this.settings.theme}.css`))}</style>`;
 | 
				
			||||||
            } catch(e) {
 | 
					            } catch(e) {
 | 
				
			||||||
                if((<Error & {code: string}>e).code === 'ENOENT' && this.settings.theme !== 'default') {
 | 
					                if((<Error & {code: string}>e).code === 'ENOENT' && this.settings.theme !== 'default') {
 | 
				
			||||||
                    this.settings.theme = 'default';
 | 
					                    this.settings.theme = 'default';
 | 
				
			||||||
 | 
				
			|||||||
@ -93,7 +93,7 @@ require('electron-packager')({
 | 
				
			|||||||
                const args = [appPaths[0], 'fchat.AppImage', '-u', 'zsync|https://client.f-list.net/fchat.AppImage.zsync'];
 | 
					                const args = [appPaths[0], 'fchat.AppImage', '-u', 'zsync|https://client.f-list.net/fchat.AppImage.zsync'];
 | 
				
			||||||
                if(process.argv.length > 2) args.push('-s', '--sign-key', process.argv[2]);
 | 
					                if(process.argv.length > 2) args.push('-s', '--sign-key', process.argv[2]);
 | 
				
			||||||
                else console.warn('Warning: Creating unsigned AppImage');
 | 
					                else console.warn('Warning: Creating unsigned AppImage');
 | 
				
			||||||
                if(process.argv.length > 3) args.push('--sign-args', `--no-tty --passphrase=${process.argv[3]}`);
 | 
					                if(process.argv.length > 3) args.push('--sign-args', `--no-tty  --pinentry-mode loopback --yes --passphrase=${process.argv[3]}`);
 | 
				
			||||||
                fs.chmodSync(downloaded, 0o755);
 | 
					                fs.chmodSync(downloaded, 0o755);
 | 
				
			||||||
                child_process.spawn(downloaded, ['--appimage-extract'], {cwd: distDir}).on('close', () => {
 | 
					                child_process.spawn(downloaded, ['--appimage-extract'], {cwd: distDir}).on('close', () => {
 | 
				
			||||||
                    const child = child_process.spawn(path.join(distDir, 'squashfs-root', 'AppRun'), args, {cwd: distDir, env: {ARCH: 'x86_64'}});
 | 
					                    const child = child_process.spawn(path.join(distDir, 'squashfs-root', 'AppRun'), args, {cwd: distDir, env: {ARCH: 'x86_64'}});
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "fchat",
 | 
					  "name": "fchat",
 | 
				
			||||||
  "version": "3.0.13",
 | 
					  "version": "3.0.16",
 | 
				
			||||||
  "author": "The F-List Team",
 | 
					  "author": "The F-List Team",
 | 
				
			||||||
  "description": "F-List.net Chat Client",
 | 
					  "description": "F-List.net Chat Client",
 | 
				
			||||||
  "main": "main.js",
 | 
					  "main": "main.js",
 | 
				
			||||||
 | 
				
			|||||||
@ -127,7 +127,7 @@
 | 
				
			|||||||
        get styling(): string {
 | 
					        get styling(): string {
 | 
				
			||||||
            if(window.NativeView !== undefined) window.NativeView.setTheme(this.settings.theme);
 | 
					            if(window.NativeView !== undefined) window.NativeView.setTheme(this.settings.theme);
 | 
				
			||||||
            //tslint:disable-next-line:no-require-imports
 | 
					            //tslint:disable-next-line:no-require-imports
 | 
				
			||||||
            return `<style>${require('../scss/fa.scss')}${require(`../scss/themes/chat/${this.settings.theme}.scss`)}</style>`;
 | 
					            return `<style id="themeStyle">${require('../scss/fa.scss')}${require(`../scss/themes/chat/${this.settings.theme}.scss`)}</style>`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async login(): Promise<void> {
 | 
					        async login(): Promise<void> {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								mobile/android/.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/android/.idea/misc.xml
									
									
									
										generated
									
									
									
								
							@ -1,4 +1,4 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
<project version="4">
 | 
					<project version="4">
 | 
				
			||||||
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
 | 
					  <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
 | 
				
			||||||
</project>
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										4
									
								
								mobile/android/.idea/modules.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								mobile/android/.idea/modules.xml
									
									
									
										generated
									
									
									
								
							@ -2,8 +2,8 @@
 | 
				
			|||||||
<project version="4">
 | 
					<project version="4">
 | 
				
			||||||
  <component name="ProjectModuleManager">
 | 
					  <component name="ProjectModuleManager">
 | 
				
			||||||
    <modules>
 | 
					    <modules>
 | 
				
			||||||
      <module fileurl="file://$PROJECT_DIR$/.idea/android.iml" filepath="$PROJECT_DIR$/.idea/android.iml" />
 | 
					      <module fileurl="file://$PROJECT_DIR$/.idea/modules/app/android.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/android.app.iml" />
 | 
				
			||||||
      <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
 | 
					      <module fileurl="file://$PROJECT_DIR$/mobile.android.iml" filepath="$PROJECT_DIR$/mobile.android.iml" />
 | 
				
			||||||
    </modules>
 | 
					    </modules>
 | 
				
			||||||
  </component>
 | 
					  </component>
 | 
				
			||||||
</project>
 | 
					</project>
 | 
				
			||||||
@ -3,13 +3,13 @@ apply plugin: 'kotlin-android'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
android {
 | 
					android {
 | 
				
			||||||
	compileSdkVersion 28
 | 
						compileSdkVersion 28
 | 
				
			||||||
	buildToolsVersion "28.0.3"
 | 
						buildToolsVersion "29.0.3"
 | 
				
			||||||
	defaultConfig {
 | 
						defaultConfig {
 | 
				
			||||||
		applicationId "net.f_list.fchat"
 | 
							applicationId "net.f_list.fchat"
 | 
				
			||||||
		minSdkVersion 21
 | 
							minSdkVersion 21
 | 
				
			||||||
		targetSdkVersion 27
 | 
							targetSdkVersion 27
 | 
				
			||||||
		versionCode 25
 | 
							versionCode 28
 | 
				
			||||||
		versionName "3.0.12"
 | 
							versionName "3.0.16"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	buildTypes {
 | 
						buildTypes {
 | 
				
			||||||
		release {
 | 
							release {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
 | 
					// Top-level build file where you can add configuration options common to all sub-projects/modules.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
buildscript {
 | 
					buildscript {
 | 
				
			||||||
	ext.kotlin_version = '1.2.30'
 | 
						ext.kotlin_version = '1.3.60'
 | 
				
			||||||
	repositories {
 | 
						repositories {
 | 
				
			||||||
		jcenter()
 | 
							jcenter()
 | 
				
			||||||
		google()
 | 
							google()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	dependencies {
 | 
						dependencies {
 | 
				
			||||||
		classpath 'com.android.tools.build:gradle:3.1.3'
 | 
							classpath 'com.android.tools.build:gradle:3.5.0'
 | 
				
			||||||
		classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 | 
							classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// NOTE: Do not place your application dependencies here; they belong
 | 
							// NOTE: Do not place your application dependencies here; they belong
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
distributionBase=GRADLE_USER_HOME
 | 
					distributionBase=GRADLE_USER_HOME
 | 
				
			||||||
distributionPath=wrapper/dists
 | 
					distributionPath=wrapper/dists
 | 
				
			||||||
 | 
					distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
 | 
				
			||||||
zipStoreBase=GRADLE_USER_HOME
 | 
					zipStoreBase=GRADLE_USER_HOME
 | 
				
			||||||
zipStorePath=wrapper/dists
 | 
					zipStorePath=wrapper/dists
 | 
				
			||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -112,11 +112,12 @@
 | 
				
			|||||||
			isa = PBXProject;
 | 
								isa = PBXProject;
 | 
				
			||||||
			attributes = {
 | 
								attributes = {
 | 
				
			||||||
				LastSwiftUpdateCheck = 0920;
 | 
									LastSwiftUpdateCheck = 0920;
 | 
				
			||||||
				LastUpgradeCheck = 0920;
 | 
									LastUpgradeCheck = 1130;
 | 
				
			||||||
				ORGANIZATIONNAME = "F-List";
 | 
									ORGANIZATIONNAME = "F-List";
 | 
				
			||||||
				TargetAttributes = {
 | 
									TargetAttributes = {
 | 
				
			||||||
					6CA94BA71FEFEE7800183A1A = {
 | 
										6CA94BA71FEFEE7800183A1A = {
 | 
				
			||||||
						CreatedOnToolsVersion = 9.2;
 | 
											CreatedOnToolsVersion = 9.2;
 | 
				
			||||||
 | 
											LastSwiftMigration = 1130;
 | 
				
			||||||
						ProvisioningStyle = Automatic;
 | 
											ProvisioningStyle = Automatic;
 | 
				
			||||||
						SystemCapabilities = {
 | 
											SystemCapabilities = {
 | 
				
			||||||
							com.apple.BackgroundModes = {
 | 
												com.apple.BackgroundModes = {
 | 
				
			||||||
@ -218,6 +219,7 @@
 | 
				
			|||||||
				CLANG_WARN_BOOL_CONVERSION = YES;
 | 
									CLANG_WARN_BOOL_CONVERSION = YES;
 | 
				
			||||||
				CLANG_WARN_COMMA = YES;
 | 
									CLANG_WARN_COMMA = YES;
 | 
				
			||||||
				CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
									CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 | 
				
			||||||
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
									CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
				
			||||||
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
									CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
				
			||||||
				CLANG_WARN_EMPTY_BODY = YES;
 | 
									CLANG_WARN_EMPTY_BODY = YES;
 | 
				
			||||||
@ -225,6 +227,7 @@
 | 
				
			|||||||
				CLANG_WARN_INFINITE_RECURSION = YES;
 | 
									CLANG_WARN_INFINITE_RECURSION = YES;
 | 
				
			||||||
				CLANG_WARN_INT_CONVERSION = YES;
 | 
									CLANG_WARN_INT_CONVERSION = YES;
 | 
				
			||||||
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
									CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 | 
				
			||||||
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
									CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
				
			||||||
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
									CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
				
			||||||
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
									CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
				
			||||||
@ -275,6 +278,7 @@
 | 
				
			|||||||
				CLANG_WARN_BOOL_CONVERSION = YES;
 | 
									CLANG_WARN_BOOL_CONVERSION = YES;
 | 
				
			||||||
				CLANG_WARN_COMMA = YES;
 | 
									CLANG_WARN_COMMA = YES;
 | 
				
			||||||
				CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
									CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 | 
				
			||||||
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
									CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
				
			||||||
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
									CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
				
			||||||
				CLANG_WARN_EMPTY_BODY = YES;
 | 
									CLANG_WARN_EMPTY_BODY = YES;
 | 
				
			||||||
@ -282,6 +286,7 @@
 | 
				
			|||||||
				CLANG_WARN_INFINITE_RECURSION = YES;
 | 
									CLANG_WARN_INFINITE_RECURSION = YES;
 | 
				
			||||||
				CLANG_WARN_INT_CONVERSION = YES;
 | 
									CLANG_WARN_INT_CONVERSION = YES;
 | 
				
			||||||
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
									CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
				
			||||||
 | 
									CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 | 
				
			||||||
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
									CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
				
			||||||
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
									CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
				
			||||||
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
									CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
				
			||||||
@ -321,7 +326,7 @@
 | 
				
			|||||||
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 | 
									LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 | 
				
			||||||
				PRODUCT_BUNDLE_IDENTIFIER = "net.f-list.F-Chat";
 | 
									PRODUCT_BUNDLE_IDENTIFIER = "net.f-list.F-Chat";
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SWIFT_VERSION = 4.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
									TARGETED_DEVICE_FAMILY = "1,2";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Debug;
 | 
								name = Debug;
 | 
				
			||||||
@ -336,7 +341,7 @@
 | 
				
			|||||||
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 | 
									LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 | 
				
			||||||
				PRODUCT_BUNDLE_IDENTIFIER = "net.f-list.F-Chat";
 | 
									PRODUCT_BUNDLE_IDENTIFIER = "net.f-list.F-Chat";
 | 
				
			||||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
									PRODUCT_NAME = "$(TARGET_NAME)";
 | 
				
			||||||
				SWIFT_VERSION = 4.0;
 | 
									SWIFT_VERSION = 5.0;
 | 
				
			||||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
									TARGETED_DEVICE_FAMILY = "1,2";
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			name = Release;
 | 
								name = Release;
 | 
				
			||||||
 | 
				
			|||||||
@ -2,11 +2,9 @@ import UIKit
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@UIApplicationMain
 | 
					@UIApplicationMain
 | 
				
			||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
 | 
					class AppDelegate: UIResponder, UIApplicationDelegate {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    var window: UIWindow?
 | 
					    var window: UIWindow?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
 | 
				
			||||||
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
 | 
					 | 
				
			||||||
        // Override point for customization after application launch.
 | 
					        // Override point for customization after application launch.
 | 
				
			||||||
        return true
 | 
					        return true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -32,7 +30,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 | 
				
			|||||||
    func applicationWillTerminate(_ application: UIApplication) {
 | 
					    func applicationWillTerminate(_ application: UIApplication) {
 | 
				
			||||||
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
 | 
					        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ class Background: NSObject, WKScriptMessageHandler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    override init() {
 | 
					    override init() {
 | 
				
			||||||
        let session = AVAudioSession.sharedInstance();
 | 
					        let session = AVAudioSession.sharedInstance();
 | 
				
			||||||
        try! session.setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)
 | 
					        try! session.setCategory(AVAudioSession.Category.playback, options: .mixWithOthers)
 | 
				
			||||||
        player.volume = 0
 | 
					        player.volume = 0
 | 
				
			||||||
        player.numberOfLoops = -1;
 | 
					        player.numberOfLoops = -1;
 | 
				
			||||||
        player.play()
 | 
					        player.play()
 | 
				
			||||||
 | 
				
			|||||||
@ -49,7 +49,7 @@ class Notification: NSObject, WKScriptMessageHandler, UNUserNotificationCenterDe
 | 
				
			|||||||
        let content = UNMutableNotificationContent()
 | 
					        let content = UNMutableNotificationContent()
 | 
				
			||||||
        content.title = title
 | 
					        content.title = title
 | 
				
			||||||
        if(sound != nil) {
 | 
					        if(sound != nil) {
 | 
				
			||||||
            content.sound = UNNotificationSound(named: Bundle.main.path(forResource: "www/sounds/" + sound!, ofType: "wav")!)
 | 
					            content.sound = UNNotificationSound(named: UNNotificationSoundName(Bundle.main.path(forResource: "www/sounds/" + sound!, ofType: "wav")!))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        content.body = text
 | 
					        content.body = text
 | 
				
			||||||
        content.userInfo["data"] = data
 | 
					        content.userInfo["data"] = data
 | 
				
			||||||
 | 
				
			|||||||
@ -29,13 +29,12 @@ class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
 | 
				
			|||||||
        webView.uiDelegate = self
 | 
					        webView.uiDelegate = self
 | 
				
			||||||
        webView.navigationDelegate = self
 | 
					        webView.navigationDelegate = self
 | 
				
			||||||
        view = webView
 | 
					        view = webView
 | 
				
			||||||
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
 | 
					        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
 | 
				
			||||||
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardDidShow), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
 | 
					        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardDidShow), name: UIResponder.keyboardDidShowNotification, object: nil)
 | 
				
			||||||
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
 | 
					        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
 | 
				
			||||||
        webView.scrollView.contentInsetAdjustmentBehavior = .never
 | 
					        webView.scrollView.contentInsetAdjustmentBehavior = .never
 | 
				
			||||||
        webView.scrollView.bounces = false
 | 
					        webView.scrollView.bounces = false
 | 
				
			||||||
        UIApplication.shared.statusBarStyle = .lightContent
 | 
					        UIApplication.shared.statusBarStyle = .lightContent
 | 
				
			||||||
        (UIApplication.shared.value(forKey: "statusBar") as! UIView).backgroundColor = UIColor(white: 0, alpha: 0.5)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override func viewDidLoad() {
 | 
					    override func viewDidLoad() {
 | 
				
			||||||
@ -47,8 +46,8 @@ class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @objc func keyboardWillShow(notification: NSNotification) {
 | 
					    @objc func keyboardWillShow(notification: NSNotification) {
 | 
				
			||||||
        let info = notification.userInfo!
 | 
					        let info = notification.userInfo!
 | 
				
			||||||
        let newHeight = view.window!.frame.height - (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height
 | 
					        let newHeight = view.window!.frame.height - (info[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.height
 | 
				
			||||||
        UIView.animate(withDuration: (info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue, animations: {
 | 
					        UIView.animate(withDuration: (info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue, animations: {
 | 
				
			||||||
            self.webView.frame = CGRect(x: 0, y: 0, width: self.webView.frame.width, height: newHeight)
 | 
					            self.webView.frame = CGRect(x: 0, y: 0, width: self.webView.frame.width, height: newHeight)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -59,7 +58,7 @@ class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @objc func keyboardWillHide(notification: NSNotification) {
 | 
					    @objc func keyboardWillHide(notification: NSNotification) {
 | 
				
			||||||
        let info = notification.userInfo!
 | 
					        let info = notification.userInfo!
 | 
				
			||||||
        UIView.animate(withDuration: (info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue, animations: {
 | 
					        UIView.animate(withDuration: (info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue, animations: {
 | 
				
			||||||
            self.webView.frame = UIApplication.shared.windows[0].frame
 | 
					            self.webView.frame = UIApplication.shared.windows[0].frame
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -88,7 +87,7 @@ class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
 | 
				
			|||||||
        decisionHandler(.cancel)
 | 
					        decisionHandler(.cancel)
 | 
				
			||||||
        let str = url.absoluteString
 | 
					        let str = url.absoluteString
 | 
				
			||||||
        if(url.scheme == "data") {
 | 
					        if(url.scheme == "data") {
 | 
				
			||||||
            let start = str.index(of: ",")!
 | 
					            let start = str.firstIndex(of: ",")!
 | 
				
			||||||
            let file = FileManager.default.temporaryDirectory.appendingPathComponent(str[str.index(str.startIndex, offsetBy: 5)..<start].removingPercentEncoding!)
 | 
					            let file = FileManager.default.temporaryDirectory.appendingPathComponent(str[str.index(str.startIndex, offsetBy: 5)..<start].removingPercentEncoding!)
 | 
				
			||||||
            try! str.suffix(from: str.index(after: start)).removingPercentEncoding!.write(to: file, atomically: false, encoding: .utf8)
 | 
					            try! str.suffix(from: str.index(after: start)).removingPercentEncoding!.write(to: file, atomically: false, encoding: .utf8)
 | 
				
			||||||
            let controller = UIActivityViewController(activityItems: [file], applicationActivities: nil)
 | 
					            let controller = UIActivityViewController(activityItems: [file], applicationActivities: nil)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "net.f_list.fchat",
 | 
					  "name": "net.f_list.fchat",
 | 
				
			||||||
  "version": "3.0.12",
 | 
					  "version": "3.0.16",
 | 
				
			||||||
  "displayName": "F-Chat",
 | 
					  "displayName": "F-Chat",
 | 
				
			||||||
  "author": "The F-List Team",
 | 
					  "author": "The F-List Team",
 | 
				
			||||||
  "description": "F-List.net Chat Client",
 | 
					  "description": "F-List.net Chat Client",
 | 
				
			||||||
 | 
				
			|||||||
@ -69,7 +69,7 @@ window.addEventListener('beforeunload', (e) => {
 | 
				
			|||||||
    return l('chat.confirmLeave');
 | 
					    return l('chat.confirmLeave');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require(`../scss/themes/chat/${chatSettings.theme}.scss`);
 | 
					require(`!style-loader?{"attrs":{"id":"themeStyle"}}!css-loader!sass-loader!../scss/themes/chat/${chatSettings.theme}.scss`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
new Chat({ //tslint:disable-line:no-unused-expression
 | 
					new Chat({ //tslint:disable-line:no-unused-expression
 | 
				
			||||||
    el: '#app',
 | 
					    el: '#app',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "net.f_list.fchat",
 | 
					  "name": "net.f_list.fchat",
 | 
				
			||||||
  "version": "3.0.12",
 | 
					  "version": "3.0.16",
 | 
				
			||||||
  "displayName": "F-Chat",
 | 
					  "displayName": "F-Chat",
 | 
				
			||||||
  "author": "The F-List Team",
 | 
					  "author": "The F-List Team",
 | 
				
			||||||
  "description": "F-List.net Chat Client",
 | 
					  "description": "F-List.net Chat Client",
 | 
				
			||||||
 | 
				
			|||||||
@ -36,8 +36,8 @@ const config = {
 | 
				
			|||||||
            {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader'},
 | 
					            {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader'},
 | 
				
			||||||
            {test: /\.(wav|mp3|ogg)$/, loader: 'file-loader?name=sounds/[name].[ext]'},
 | 
					            {test: /\.(wav|mp3|ogg)$/, loader: 'file-loader?name=sounds/[name].[ext]'},
 | 
				
			||||||
            {test: /\.(png|html)$/, loader: 'file-loader?name=[name].[ext]'},
 | 
					            {test: /\.(png|html)$/, loader: 'file-loader?name=[name].[ext]'},
 | 
				
			||||||
            {test: /\.scss/, use: ['vue-style-loader', 'css-loader', 'sass-loader']},
 | 
					            {test: /\.scss$/, use: ['vue-style-loader', 'css-loader', 'sass-loader']},
 | 
				
			||||||
            {test: /\.css/, use: ['vue-style-loader', 'css-loader']}
 | 
					            {test: /\.css$/, use: ['vue-style-loader', 'css-loader']},
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    plugins: [
 | 
					    plugins: [
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user