Code reorg

This commit is contained in:
Mr. Stallion 2020-03-15 11:23:39 -05:00
parent 99be1aaedd
commit 09c182be3b
24 changed files with 315 additions and 278 deletions

View File

@ -1,9 +1,37 @@
<template>
<div class="bbcode-editor" style="display:flex;flex-wrap:wrap;justify-content:flex-end">
<slot></slot>
<a v-show="hasToolbar" tabindex="0" class="btn btn-light bbcode-btn btn-sm" role="button" @click="showToolbar = true" @blur="showToolbar = false" style="border-bottom-left-radius: 0; border-bottom-right-radius: 0">
<a tabindex="0" class="btn btn-light bbcode-btn btn-sm" role="button" @click="showToolbar = true" @blur="showToolbar = false"
style="border-bottom-left-radius:0;border-bottom-right-radius:0" v-if="hasToolbar">
<i class="fa fa-code"></i>
</a>
<div class="bbcode-toolbar btn-toolbar" role="toolbar" :style="showToolbar ? {display: 'flex'} : undefined" @mousedown.stop.prevent
v-if="hasToolbar" style="flex:1 51%">
<div class="btn-group" style="flex-wrap:wrap">
<div class="btn btn-light btn-sm" v-for="button in buttons" :title="button.title" @click.prevent.stop="apply(button)">
<i :class="(button.class ? button.class : 'fa ') + button.icon"></i>
</div>
<div @click="previewBBCode" class="btn btn-light btn-sm" :class="preview ? 'active' : ''"
:title="preview ? 'Close Preview' : 'Preview'">
<i class="fa fa-eye"></i>
</div>
</div>
<button type="button" class="close" aria-label="Close" style="margin-left:10px" @click="showToolbar = false">&times;</button>
</div>
<div class="bbcode-editor-text-area" style="order:100;width:100%;">
<textarea ref="input" v-model="text" @input="onInput" v-show="!preview" :maxlength="maxlength" :placeholder="placeholder"
:class="finalClasses" @keyup="onKeyUp" :disabled="disabled" @paste="onPaste" @keypress="$emit('keypress', $event)"
:style="hasToolbar ? {'border-top-left-radius': 0} : undefined" @keydown="onKeyDown"></textarea>
<textarea ref="sizer"></textarea>
<div class="bbcode-preview" v-show="preview">
<div class="bbcode-preview-warnings">
<div class="alert alert-danger" v-show="previewWarnings.length">
<li v-for="warning in previewWarnings">{{warning}}</li>
</div>
</div>
<div class="bbcode" ref="preview-element"></div>
</div>
</div>
</div>
</template>
@ -65,7 +93,7 @@
@Hook('created')
created(): void {
console.log('EDITOR', 'created');
// console.log('EDITOR', 'created');
this.parser = new CoreBBCodeParser();
this.resizeListener = () => {
const styles = getComputedStyle(this.element);
@ -76,7 +104,7 @@
@Hook('mounted')
mounted(): void {
console.log('EDITOR', 'mounted');
// console.log('EDITOR', 'mounted');
this.element = <HTMLTextAreaElement>this.$refs['input'];
const styles = getComputedStyle(this.element);
this.maxHeight = parseInt(styles.maxHeight, 10) || 250;

View File

@ -23,7 +23,7 @@
<script lang="ts">
import {Component, Hook, Prop} from '@f-list/vue-ts';
import Vue from 'vue';
import {EventBus} from '../chat/event-bus';
import {EventBus} from '../chat/preview/event-bus';
// import core from './core';
@Component

View File

@ -48,7 +48,7 @@
import l from './localize';
import UserView from './UserView.vue';
import * as _ from 'lodash';
import {EventBus} from './event-bus';
import {EventBus} from './preview/event-bus';
type Options = {
kinks: Kink[],
@ -301,4 +301,4 @@
}
</style>
</style>

View File

@ -122,7 +122,7 @@
import {getStatusIcon} from './UserView.vue';
import UserList from './UserList.vue';
import UserMenu from './UserMenu.vue';
import ImagePreview from './ImagePreview.vue';
import ImagePreview from './preview/ImagePreview.vue';
const unreadClasses = {
[Conversation.UnreadState.None]: '',
@ -458,4 +458,4 @@
}
}
}
</style>
</style>

View File

@ -161,7 +161,7 @@
import {BBCodeView} from '../bbcode/view';
import {isShowing as anyDialogsShown} from '../components/Modal.vue';
import {Keys} from '../keys';
import AdView from './AdView.vue';
import AdView from './ads/AdView.vue';
import {Editor} from './bbcode';
import CommandHelp from './CommandHelp.vue';
import { characterImage, getByteLength, getKey } from './common';

View File

@ -48,7 +48,7 @@
import Vue from 'vue';
import {BBCodeView} from '../bbcode/view';
import Modal from '../components/Modal.vue';
import AdView from './AdView.vue';
import AdView from './ads/AdView.vue';
import {characterImage, errorToString, getByteLength, profileLink} from './common';
import core from './core';
import {Channel, Character} from './interfaces';

View File

@ -8,7 +8,7 @@ import Vue from 'vue';
import {Channel, Character} from '../fchat';
import { Score, Scoring } from '../learn/matcher';
import core from './core';
import { EventBus } from './event-bus';
import { EventBus } from './preview/event-bus';
export function getStatusIcon(status: Character.Status): string {

View File

@ -19,13 +19,13 @@
import * as _ from 'lodash';
import { Component, Hook, Prop, Watch } from '@f-list/vue-ts';
import CustomDialog from '../components/custom_dialog';
import Modal from '../components/Modal.vue';
import { Character } from '../fchat/interfaces';
import { AdCachedPosting } from '../learn/ad-cache';
import core from './core';
import {formatTime} from './common';
import UserView from './UserView.vue';
import CustomDialog from '../../components/custom_dialog';
import Modal from '../../components/Modal.vue';
import { Character } from '../../fchat/interfaces';
import { AdCachedPosting } from '../../learn/ad-cache';
import core from '../core';
import {formatTime} from '../common';
import UserView from '../UserView.vue';
@Component({
components: {modal: Modal, user: UserView}

View File

@ -1,5 +1,5 @@
import core from './core';
import { Conversation } from './interfaces';
import core from '../core';
import { Conversation } from '../interfaces';
import Timer = NodeJS.Timer;
import throat from 'throat';

View File

@ -1,14 +1,14 @@
import {queuedJoin} from '../fchat/channels';
import {decodeHTML} from '../fchat/common';
import { CharacterCacheRecord } from '../learn/profile-cache';
import { AdManager } from './ad-manager';
import { AdManager } from './ads/ad-manager';
import { characterImage, ConversationSettings, EventMessage, Message, messageToString } from './common';
import core from './core';
import {Channel, Character, Conversation as Interfaces} from './interfaces';
import l from './localize';
import {CommandContext, isAction, isCommand, isWarn, parse as parseCommand} from './slash_commands';
import MessageType = Interfaces.Message.Type;
import {EventBus} from '../chat/event-bus';
import {EventBus} from './preview/event-bus';
function createMessage(this: any, type: MessageType, sender: Character, text: string, time?: Date): Message {
if(type === MessageType.Message && isAction(text)) {

View File

@ -2,7 +2,7 @@
import {Connection} from '../fchat';
import {Channel, Character} from '../fchat/interfaces';
import { AdManager } from './ad-manager';
import { AdManager } from './ads/ad-manager';
export {Connection, Channel, Character} from '../fchat/interfaces';
export const userStatuses: ReadonlyArray<Character.Status> = ['online', 'looking', 'away', 'busy', 'dnd'];
export const channelModes: ReadonlyArray<Channel.Mode> = ['chat', 'ads', 'both'];
@ -201,4 +201,4 @@ export interface Notifications {
export interface State {
settings: Settings
hiddenUsers: string[]
}
}

View File

@ -20,59 +20,16 @@
</template>
<script lang="ts">
/*
[url=https://giphy.com/gifs/arianagrande-ariana-grande-thank-u-next-you-uldtLAK6tSOKP5PWw3]Test[/url]
[url=https://media1.tenor.com/images/097ee180965dd336f470b77d064f198f/tenor.gif?itemid=13664909]Test[/url]
[url=https://tenor.com/view/thank-unext-ariana-grande-thank-you-next-wink-winking-gif-13664909]Test[/url]
[url=https://www.sex.com/pin/58497794/]Test[/url]
[url=https://images.sex.com/images/pinporn/2019/09/10/620/21790701.gif]Test[/url]
[url=http://gfycatporn.com/deepthroat.php]Test[/url]
[url=https://imgur.com/LmEyXEM]Test[/url]
[url=https://static1.e621.net/data/6d/bf/6dbf0c369793dbb5a53d9814c17861eb.webm]Test[/url]
[url=https://www.youtube.com/watch?v=_52zdiltkRM]Test[/url]
[url=https://e621.net/post/show/1672753/2018-anthro-antlers-balls-bed-big_penis-black_hair]Test[/url]
[url=https://rule34.xxx/index.php?page=post&s=view&id=3213191]Test[/url]
[url=https://chan.sankakucomplex.com/post/show/6163997]Test[/url]
[url=https://chan.sankakucomplex.com/post/show/5774884]Test[/url]
[url=https://www.sex.com/pin/38152484-she-likes-it-rough/]Test[/url]
[url=https://www.sex.com/pin/57537179-cock-slapping-hungry-tongue/]Test[/url]
[url=https://imgur.com/gallery/ILsb94I]Imgur gallery[/url]
[url=https://imgur.com/CIKv6sA]Imgur image[/url]
[url=https://imgur.com/a/nMafj]Imgur album[/url]
[url=http://i.imgur.com/txEREOg.gifv]Imgur video[/url]
[url=https://www.punishbang.com/videos/1898/tied-redhead-is-on-her-knees-and-can/]Test[/url]
[url=https://www.pornhub.com/view_video.php?viewkey=ph5b2c03dc1e23b]Test[/url]
*/
import * as _ from 'lodash';
import {Component, Hook} from '@f-list/vue-ts';
import Vue from 'vue';
import core from './core';
import core from '../core';
import { EventBus, EventBusEvent } from './event-bus';
import {domain} from '../bbcode/core';
import {domain} from '../../bbcode/core';
import {ImagePreviewMutator} from './image-preview-mutator';
import {ImageUrlMutator} from './image-url-mutator';
import { ExternalImagePreviewHelper, LocalImagePreviewHelper } from './helper';
import {Point, WebviewTag, remote} from 'electron';
import Timer = NodeJS.Timer;
import IpcMessageEvent = Electron.IpcMessageEvent;
@ -91,196 +48,6 @@
}
abstract class ImagePreviewHelper {
protected visible = false;
protected url: string | null = 'about:blank';
protected parent: ImagePreview;
protected debug: boolean;
abstract show(url: string): void;
abstract hide(): void;
abstract match(domainName: string): boolean;
abstract renderStyle(): Record<string, any>;
constructor(parent: ImagePreview) {
if (!parent) {
throw new Error('Empty parent!');
}
this.parent = parent;
this.debug = parent.debug;
}
isVisible(): boolean {
return this.visible;
}
getUrl(): string | null {
return this.url;
}
setDebug(debug: boolean): void {
this.debug = debug;
}
}
class LocalImagePreviewHelper extends ImagePreviewHelper {
hide(): void {
this.visible = false;
this.url = null;
}
show(url: string): void {
this.visible = true;
this.url = url;
}
match(domainName: string): boolean {
return ((domainName === 'f-list.net') || (domainName === 'static.f-list.net'));
}
renderStyle(): Record<string, any> {
return this.isVisible()
? { backgroundImage: `url(${this.getUrl()})`, display: 'block' }
: { display: 'none' };
}
}
class ExternalImagePreviewHelper extends ImagePreviewHelper {
protected lastExternalUrl: string | null = null;
protected allowCachedUrl = true;
protected urlMutator = new ImageUrlMutator(this.parent.debug);
protected ratio: number | null = null;
hide(): void {
const wasVisible = this.visible;
if (this.parent.debug)
console.log('ImagePreview: exec hide mutator');
if (wasVisible) {
const webview = this.parent.getWebview();
if (this.allowCachedUrl) {
webview.executeJavaScript(this.parent.jsMutator.getHideMutator());
} else {
webview.loadURL('about:blank');
}
this.visible = false;
}
}
setRatio(ratio: number): void {
this.ratio = ratio;
}
setDebug(debug: boolean): void {
this.debug = debug;
this.urlMutator.setDebug(debug);
}
show(url: string): void {
const webview = this.parent.getWebview();
if (!this.parent) {
throw new Error('Empty parent v2');
}
if (!webview) {
throw new Error('Empty webview!');
}
// const oldUrl = this.url;
const oldLastExternalUrl = this.lastExternalUrl;
this.url = url;
this.lastExternalUrl = url;
this.visible = true;
try {
if ((this.allowCachedUrl) && ((webview.getURL() === url) || (url === oldLastExternalUrl))) {
if (this.debug)
console.log('ImagePreview: exec re-show mutator');
webview.executeJavaScript(this.parent.jsMutator.getReShowMutator());
} else {
if (this.debug)
console.log('ImagePreview: must load; skip re-show because urls don\'t match', this.url, webview.getURL());
this.ratio = null;
// Broken promise chain on purpose
// tslint:disable-next-line:no-floating-promises
this.urlMutator.resolve(url)
.then((finalUrl: string) => webview.loadURL(finalUrl));
}
} catch (err) {
console.error('ImagePreview: Webview reuse error', err);
}
}
match(domainName: string): boolean {
return !((domainName === 'f-list.net') || (domainName === 'static.f-list.net'));
}
determineScalingRatio(): Record<string, any> {
// ratio = width / height
const ratio = this.ratio;
if (!ratio) {
return {};
}
const ww = window.innerWidth;
const wh = window.innerHeight;
const maxWidth = Math.round(ww * 0.5);
const maxHeight = Math.round(wh * 0.7);
if (ratio >= 1) {
const presumedWidth = maxWidth;
const presumedHeight = presumedWidth / ratio;
return {
width: `${presumedWidth}px`,
height: `${presumedHeight}px`
};
// tslint:disable-next-line:unnecessary-else
} else {
const presumedHeight = maxHeight;
const presumedWidth = presumedHeight * ratio;
return {
width: `${presumedWidth}px`,
height: `${presumedHeight}px`
};
}
}
renderStyle(): Record<string, any> {
return this.isVisible()
? _.merge({ display: 'flex' }, this.determineScalingRatio())
: { display: 'none' };
}
}
@Component
export default class ImagePreview extends Vue {
private readonly MinTimePreviewVisible = 100;
@ -760,9 +527,9 @@
<style lang="scss">
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins/breakpoints";
@import "../../node_modules/bootstrap/scss/functions";
@import "../../node_modules/bootstrap/scss/variables";
@import "../../node_modules/bootstrap/scss/mixins/breakpoints";
.image-preview-wrapper {
z-index: 10000;

View File

@ -1,7 +1,7 @@
import Vue from 'vue';
import { Character } from '../site/character_page/interfaces';
import { Message } from './common';
import { Conversation } from './interfaces';
import { Character } from '../../site/character_page/interfaces';
import { Message } from '../common';
import { Conversation } from '../interfaces';
import ChannelConversation = Conversation.ChannelConversation;
/**

View File

@ -0,0 +1,133 @@
import { ImageUrlMutator } from '../image-url-mutator';
import { ImagePreviewHelper } from './index';
import * as _ from 'lodash';
export class ExternalImagePreviewHelper extends ImagePreviewHelper {
protected lastExternalUrl: string | null = null;
protected allowCachedUrl = true;
protected urlMutator = new ImageUrlMutator(this.parent.debug);
protected ratio: number | null = null;
hide(): void {
const wasVisible = this.visible;
if (this.parent.debug)
console.log('ImagePreview: exec hide mutator');
if (wasVisible) {
const webview = this.parent.getWebview();
if (this.allowCachedUrl) {
webview.executeJavaScript(this.parent.jsMutator.getHideMutator());
} else {
webview.loadURL('about:blank');
}
this.visible = false;
}
}
setRatio(ratio: number): void {
this.ratio = ratio;
}
setDebug(debug: boolean): void {
this.debug = debug;
this.urlMutator.setDebug(debug);
}
show(url: string): void {
const webview = this.parent.getWebview();
if (!this.parent) {
throw new Error('Empty parent v2');
}
if (!webview) {
throw new Error('Empty webview!');
}
// const oldUrl = this.url;
const oldLastExternalUrl = this.lastExternalUrl;
this.url = url;
this.lastExternalUrl = url;
this.visible = true;
try {
if ((this.allowCachedUrl) && ((webview.getURL() === url) || (url === oldLastExternalUrl))) {
if (this.debug)
console.log('ImagePreview: exec re-show mutator');
webview.executeJavaScript(this.parent.jsMutator.getReShowMutator());
} else {
if (this.debug)
console.log('ImagePreview: must load; skip re-show because urls don\'t match', this.url, webview.getURL());
this.ratio = null;
// Broken promise chain on purpose
// tslint:disable-next-line:no-floating-promises
this.urlMutator.resolve(url)
.then((finalUrl: string) => webview.loadURL(finalUrl));
}
} catch (err) {
console.error('ImagePreview: Webview reuse error', err);
}
}
match(domainName: string): boolean {
return !((domainName === 'f-list.net') || (domainName === 'static.f-list.net'));
}
determineScalingRatio(): Record<string, any> {
// ratio = width / height
const ratio = this.ratio;
if (!ratio) {
return {};
}
const ww = window.innerWidth;
const wh = window.innerHeight;
const maxWidth = Math.round(ww * 0.5);
const maxHeight = Math.round(wh * 0.7);
if (ratio >= 1) {
const presumedWidth = maxWidth;
const presumedHeight = presumedWidth / ratio;
return {
width: `${presumedWidth}px`,
height: `${presumedHeight}px`
};
// tslint:disable-next-line:unnecessary-else
} else {
const presumedHeight = maxHeight;
const presumedWidth = presumedHeight * ratio;
return {
width: `${presumedWidth}px`,
height: `${presumedHeight}px`
};
}
}
renderStyle(): Record<string, any> {
return this.isVisible()
? _.merge({ display: 'flex' }, this.determineScalingRatio())
: { display: 'none' };
}
}

View File

@ -0,0 +1,38 @@
import ImagePreview from '../ImagePreview.vue';
export * from './external';
export * from './local';
export abstract class ImagePreviewHelper {
protected visible = false;
protected url: string | null = 'about:blank';
protected parent: ImagePreview;
protected debug: boolean;
abstract show(url: string): void;
abstract hide(): void;
abstract match(domainName: string): boolean;
abstract renderStyle(): Record<string, any>;
constructor(parent: ImagePreview) {
if (!parent) {
throw new Error('Empty parent!');
}
this.parent = parent;
this.debug = parent.debug;
}
isVisible(): boolean {
return this.visible;
}
getUrl(): string | null {
return this.url;
}
setDebug(debug: boolean): void {
this.debug = debug;
}
}

View File

@ -0,0 +1,27 @@
import { ImagePreviewHelper } from './index';
export class LocalImagePreviewHelper extends ImagePreviewHelper {
hide(): void {
this.visible = false;
this.url = null;
}
show(url: string): void {
this.visible = true;
this.url = url;
}
match(domainName: string): boolean {
return ((domainName === 'f-list.net') || (domainName === 'static.f-list.net'));
}
renderStyle(): Record<string, any> {
return this.isVisible()
? { backgroundImage: `url(${this.getUrl()})`, display: 'block' }
: { display: 'none' };
}
}

View File

@ -4,7 +4,7 @@ import * as _ from 'lodash';
import * as urlHelper from 'url';
import { domain as extractDomain } from '../bbcode/core';
import { domain as extractDomain } from '../../bbcode/core';
export interface PreviewMutator {
match: string | RegExp;

View File

@ -0,0 +1,44 @@
[url=https://giphy.com/gifs/arianagrande-ariana-grande-thank-u-next-you-uldtLAK6tSOKP5PWw3]Test[/url]
[url=https://media1.tenor.com/images/097ee180965dd336f470b77d064f198f/tenor.gif?itemid=13664909]Test[/url]
[url=https://tenor.com/view/thank-unext-ariana-grande-thank-you-next-wink-winking-gif-13664909]Test[/url]
[url=https://www.sex.com/pin/58497794/]Test[/url]
[url=https://images.sex.com/images/pinporn/2019/09/10/620/21790701.gif]Test[/url]
[url=http://gfycatporn.com/deepthroat.php]Test[/url]
[url=https://imgur.com/LmEyXEM]Test[/url]
[url=https://static1.e621.net/data/6d/bf/6dbf0c369793dbb5a53d9814c17861eb.webm]Test[/url]
[url=https://www.youtube.com/watch?v=_52zdiltkRM]Test[/url]
[url=https://e621.net/post/show/1672753/2018-anthro-antlers-balls-bed-big_penis-black_hair]Test[/url]
[url=https://rule34.xxx/index.php?page=post&s=view&id=3213191]Test[/url]
[url=https://chan.sankakucomplex.com/post/show/6163997]Test[/url]
[url=https://chan.sankakucomplex.com/post/show/5774884]Test[/url]
[url=https://www.sex.com/pin/38152484-she-likes-it-rough/]Test[/url]
[url=https://www.sex.com/pin/57537179-cock-slapping-hungry-tongue/]Test[/url]
[url=https://imgur.com/gallery/ILsb94I]Imgur gallery[/url]
[url=https://imgur.com/CIKv6sA]Imgur image[/url]
[url=https://imgur.com/a/nMafj]Imgur album[/url]
[url=http://i.imgur.com/txEREOg.gifv]Imgur video[/url]
[url=https://www.punishbang.com/videos/1898/tied-redhead-is-on-her-knees-and-can/]Test[/url]
[url=https://www.pornhub.com/view_video.php?viewkey=ph5b2c03dc1e23b]Test[/url]

View File

@ -19,7 +19,7 @@ import {
} from '../site/character_page/interfaces';
import * as Utils from '../site/utils';
import core from './core';
import { EventBus } from './event-bus';
import { EventBus } from './preview/event-bus';
const parserSettings = {
siteDomain: 'https://www.f-list.net/',

View File

@ -85,7 +85,7 @@
@Hook('mounted')
async mounted(): Promise<void> {
// top bar devtools
browserWindow.webContents.openDevTools();
// browserWindow.webContents.openDevTools( { mode: 'detach' } );
await this.addTab();
@ -168,7 +168,7 @@
get styling(): string {
try {
return `<style>${fs.readFileSync(path.join(__dirname, `themes/${this.settings.theme}.css`, 'utf8')).toString()}</style>`;
return `<style>${fs.readFileSync(path.join(__dirname, `themes/${this.settings.theme}.css`), 'utf8').toString()}</style>`;
} catch(e) {
if((<Error & {code: string}>e).code === 'ENOENT' && this.settings.theme !== 'default') {
this.settings.theme = 'default';

View File

@ -40,7 +40,7 @@ import * as electron from 'electron';
import * as path from 'path';
import * as qs from 'querystring';
import {getKey} from '../chat/common';
import { EventBus } from '../chat/event-bus';
import { EventBus } from '../chat/preview/event-bus';
import {init as initCore} from '../chat/core';
import l from '../chat/localize';
import {setupRaven} from '../chat/vue-raven';

View File

@ -38,10 +38,10 @@ import * as electron from 'electron';
import log from 'electron-log'; //tslint:disable-line:match-default-export-name
import * as fs from 'fs';
import * as path from 'path';
import * as url from 'url';
// import * as url from 'url';
import l from '../chat/localize';
import {defaultHost, GeneralSettings} from './common';
import {ensureDictionary, getAvailableDictionaries} from './dgit ictionaries';
import {ensureDictionary, getAvailableDictionaries} from './dictionaries';
import * as windowState from './window_state';
import BrowserWindow = Electron.BrowserWindow;
import MenuItem = Electron.MenuItem;
@ -128,8 +128,8 @@ function createWindow(): Electron.BrowserWindow | undefined {
};
if(process.platform === 'darwin') {
// windowProperties.titleBarStyle = 'hiddenInset';
windowProperties.frame = true;
windowProperties.titleBarStyle = 'hiddenInset';
// windowProperties.frame = true;
} else {
windowProperties.frame = false;
}

View File

@ -1,6 +1,6 @@
import * as _ from 'lodash';
import core from '../chat/core';
import { ChannelAdEvent, ChannelMessageEvent, CharacterDataEvent, EventBus } from '../chat/event-bus';
import { ChannelAdEvent, ChannelMessageEvent, CharacterDataEvent, EventBus } from '../chat/preview/event-bus';
import { Conversation } from '../chat/interfaces';
import { methods } from '../site/character_page/data_store';
import { Character as ComplexCharacter } from '../site/character_page/interfaces';