Better caching for character data
This commit is contained in:
parent
379604bfb1
commit
c35136c6dd
|
@ -48,11 +48,12 @@
|
|||
</div>
|
||||
</modal>
|
||||
<modal :buttons="false" ref="profileViewer" dialogClass="profile-viewer">
|
||||
<character-page :authenticated="true" :oldApi="true" :name="profileName" :image-preview="true"></character-page>
|
||||
<character-page :authenticated="true" :oldApi="true" :name="profileName" :image-preview="true" ref="characterPage"></character-page>
|
||||
<template slot="title">
|
||||
{{profileName}}
|
||||
<a class="btn" @click="openProfileInBrowser"><i class="fa fa-external-link-alt"/></a>
|
||||
<a class="btn" @click="openConversation"><i class="fa fa-comment"></i></a>
|
||||
<a class="btn" @click="reloadCharacter"><i class="fa fa-sync" /></a>
|
||||
</template>
|
||||
</modal>
|
||||
<modal :action="l('fixLogs.action')" ref="fixLogsModal" @submit="fixLogs" buttonClass="btn-danger">
|
||||
|
@ -280,6 +281,12 @@
|
|||
(this.$refs.profileViewer as any).hide();
|
||||
}
|
||||
|
||||
|
||||
reloadCharacter(): void {
|
||||
// tslint:disable-next-line: no-any no-unsafe-any
|
||||
(this.$refs.characterPage as any).reload();
|
||||
}
|
||||
|
||||
get styling(): string {
|
||||
try {
|
||||
return `<style>${fs.readFileSync(path.join(__dirname, `themes/${this.settings.theme}.css`))}</style>`;
|
||||
|
|
|
@ -588,7 +588,7 @@ export class Matcher {
|
|||
if ((theirAge < 16) && (ageplayScore === null))
|
||||
return Matcher.formatKinkScore(KinkPreference.No, `ages of ${theirAge}`);
|
||||
|
||||
if ((theirAge < 18) && (underageScore !== null))
|
||||
if ((theirAge < 18) && (theirAge >= 16) && (underageScore !== null))
|
||||
return Matcher.formatKinkScore(underageScore, `ages of ${theirAge}`);
|
||||
|
||||
const yourAge = this.yourAnalysis.age;
|
||||
|
@ -742,8 +742,7 @@ export class Matcher {
|
|||
}
|
||||
);
|
||||
|
||||
// tslint:disable-next-line: strict-type-predicates
|
||||
return (foundSpeciesId === null) ? null : parseInt(foundSpeciesId, 10);
|
||||
return foundSpeciesId;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
import * as _ from 'lodash';
|
||||
|
||||
import core from '../chat/core';
|
||||
import { Character as ComplexCharacter } from '../site/character_page/interfaces';
|
||||
import {Character as ComplexCharacter, CharacterFriend, CharacterGroup, GuestbookState} from '../site/character_page/interfaces';
|
||||
import { AsyncCache } from './async-cache';
|
||||
import { Matcher, Score, Scoring } from './matcher';
|
||||
import { PermanentIndexedStore } from './store/sql-store';
|
||||
import {CharacterImage} from '../interfaces';
|
||||
|
||||
|
||||
export interface MetaRecord {
|
||||
images: CharacterImage[] | null;
|
||||
groups: CharacterGroup[] | null;
|
||||
friends: CharacterFriend[] | null;
|
||||
guestbook: GuestbookState | null;
|
||||
lastFetched: Date | null;
|
||||
}
|
||||
|
||||
|
||||
export interface CountRecord {
|
||||
groupCount: number | null;
|
||||
|
@ -18,7 +29,8 @@ export interface CharacterCacheRecord {
|
|||
lastFetched: Date;
|
||||
added: Date;
|
||||
matchScore: number;
|
||||
counts?: CountRecord;
|
||||
// counts?: CountRecord;
|
||||
meta?: MetaRecord;
|
||||
}
|
||||
|
||||
export class ProfileCache extends AsyncCache<CharacterCacheRecord> {
|
||||
|
@ -63,18 +75,42 @@ export class ProfileCache extends AsyncCache<CharacterCacheRecord> {
|
|||
cacheRecord.lastFetched = new Date(pd.lastFetched * 1000);
|
||||
cacheRecord.added = new Date(pd.firstSeen * 1000);
|
||||
|
||||
cacheRecord.counts = {
|
||||
cacheRecord.meta = {
|
||||
lastFetched: pd.lastMetaFetched ? new Date(pd.lastMetaFetched) : null,
|
||||
groups: pd.groups,
|
||||
friends: pd.friends,
|
||||
images: pd.images,
|
||||
guestbook: pd.guestbook
|
||||
};
|
||||
|
||||
/* cacheRecord.counts = {
|
||||
lastCounted: pd.lastCounted,
|
||||
groupCount: pd.groupCount,
|
||||
friendCount: pd.friendCount,
|
||||
guestbookCount: pd.guestbookCount
|
||||
};
|
||||
}; */
|
||||
|
||||
return cacheRecord;
|
||||
}
|
||||
|
||||
|
||||
async registerCount(name: string, counts: CountRecord): Promise<void> {
|
||||
// async registerCount(name: string, counts: CountRecord): Promise<void> {
|
||||
// const record = await this.get(name);
|
||||
//
|
||||
// if (!record) {
|
||||
// // coward's way out
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// record.counts = counts;
|
||||
//
|
||||
// if (this.store) {
|
||||
// await this.store.updateProfileCounts(name, counts.guestbookCount, counts.friendCount, counts.groupCount);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
async registerMeta(name: string, meta: MetaRecord): Promise<void> {
|
||||
const record = await this.get(name);
|
||||
|
||||
if (!record) {
|
||||
|
@ -82,10 +118,10 @@ export class ProfileCache extends AsyncCache<CharacterCacheRecord> {
|
|||
return;
|
||||
}
|
||||
|
||||
record.counts = counts;
|
||||
record.meta = meta;
|
||||
|
||||
if (this.store) {
|
||||
await this.store.updateProfileCounts(name, counts.guestbookCount, counts.friendCount, counts.groupCount);
|
||||
await this.store.updateProfileMeta(name, meta.images, meta.guestbook, meta.friends, meta.groups);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import * as _ from 'lodash';
|
||||
|
||||
import { Character as ComplexCharacter } from '../../site/character_page/interfaces';
|
||||
import {Character as ComplexCharacter, CharacterFriend, CharacterGroup, GuestbookState} from '../../site/character_page/interfaces';
|
||||
import { CharacterAnalysis } from '../matcher';
|
||||
import { PermanentIndexedStore, ProfileRecord } from './sql-store';
|
||||
import {CharacterImage} from '../../interfaces';
|
||||
|
||||
|
||||
async function promisifyRequest<T>(req: IDBRequest): Promise<T> {
|
||||
|
@ -82,14 +83,21 @@ export class IndexedStore implements PermanentIndexedStore {
|
|||
age: ca.age,
|
||||
domSubRole: null, // domSubRole
|
||||
position: null, // position
|
||||
lastCounted: null,
|
||||
guestbookCount: null,
|
||||
friendCount: null,
|
||||
groupCount: null
|
||||
|
||||
lastMetaFetched: null,
|
||||
guestbook: null,
|
||||
images: null,
|
||||
friends: null,
|
||||
groups: null
|
||||
|
||||
// lastCounted: null,
|
||||
// guestbookCount: null,
|
||||
// friendCount: null,
|
||||
// groupCount: null
|
||||
};
|
||||
|
||||
return (existing)
|
||||
? _.merge(existing, data, _.pick(existing, ['firstSeen', 'lastCounted', 'guestbookCount', 'friendCount', 'groupCount']))
|
||||
? _.merge(existing, data, _.pick(existing, ['firstSeen', 'lastMetaFetched', 'guestbook', 'images', 'friends', 'groups']))
|
||||
: data;
|
||||
}
|
||||
|
||||
|
@ -140,6 +148,41 @@ export class IndexedStore implements PermanentIndexedStore {
|
|||
// console.log('IDX update counts', name, data);
|
||||
}
|
||||
|
||||
|
||||
async updateProfileMeta(
|
||||
name: string,
|
||||
images: CharacterImage[] | null,
|
||||
guestbook: GuestbookState | null,
|
||||
friends: CharacterFriend[] | null,
|
||||
groups: CharacterGroup[] | null
|
||||
): Promise<void> {
|
||||
const existing = await this.getProfile(name);
|
||||
|
||||
if (!existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = _.merge(
|
||||
existing,
|
||||
{
|
||||
lastFetched: Math.round(Date.now() / 1000),
|
||||
guestbook,
|
||||
friends,
|
||||
groups,
|
||||
images
|
||||
}
|
||||
);
|
||||
|
||||
const tx = this.db.transaction(IndexedStore.STORE_NAME, 'readwrite');
|
||||
const store = tx.objectStore(IndexedStore.STORE_NAME);
|
||||
const putRequest = store.put(data);
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
await promisifyRequest<any>(putRequest);
|
||||
|
||||
// console.log('IDX update counts', name, data);
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
// empty
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// tslint:disable-next-line:no-duplicate-imports
|
||||
import * as path from 'path';
|
||||
import core from '../../chat/core';
|
||||
// import * as path from 'path';
|
||||
// import core from '../../chat/core';
|
||||
|
||||
import { Orientation, Gender, FurryPreference, Species, CharacterAnalysis } from '../matcher';
|
||||
import { Character as ComplexCharacter } from '../../site/character_page/interfaces';
|
||||
import { Orientation, Gender, FurryPreference, Species } from '../matcher';
|
||||
import {Character as ComplexCharacter, CharacterFriend, CharacterGroup, GuestbookState} from '../../site/character_page/interfaces';
|
||||
import {CharacterImage} from '../../interfaces';
|
||||
|
||||
export interface ProfileRecord {
|
||||
id: string;
|
||||
|
@ -18,10 +19,17 @@ export interface ProfileRecord {
|
|||
age: number | null;
|
||||
domSubRole: number | null;
|
||||
position: number | null;
|
||||
lastCounted: number | null;
|
||||
guestbookCount: number | null;
|
||||
friendCount: number | null;
|
||||
groupCount: number | null;
|
||||
|
||||
// lastCounted: number | null;
|
||||
// guestbookCount: number | null;
|
||||
// friendCount: number | null;
|
||||
// groupCount: number | null;
|
||||
|
||||
lastMetaFetched: number | null;
|
||||
guestbook: GuestbookState | null;
|
||||
images: CharacterImage[] | null;
|
||||
friends: CharacterFriend[] | null;
|
||||
groups: CharacterGroup[] | null;
|
||||
}
|
||||
|
||||
// export type Statement = any;
|
||||
|
@ -30,66 +38,83 @@ export interface ProfileRecord {
|
|||
export interface PermanentIndexedStore {
|
||||
getProfile(name: string): Promise<ProfileRecord | undefined>;
|
||||
storeProfile(c: ComplexCharacter): Promise<void>;
|
||||
updateProfileCounts(name: string, guestbookCount: number | null, friendCount: number | null, groupCount: number | null): Promise<void>;
|
||||
|
||||
updateProfileMeta(
|
||||
name: string,
|
||||
images: CharacterImage[] | null,
|
||||
guestbook: GuestbookState | null,
|
||||
friends: CharacterFriend[] | null,
|
||||
groups: CharacterGroup[] | null
|
||||
): Promise<void>;
|
||||
|
||||
start(): Promise<void>;
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
export abstract class SqlStore implements PermanentIndexedStore {
|
||||
protected dbFile: string;
|
||||
protected checkpointTimer: NodeJS.Timer | null = null;
|
||||
|
||||
constructor(dbName: string = 'fchat-ascending.sqlite') {
|
||||
this.dbFile = path.join(core.state.generalSettings!.logDirectory, dbName);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: prefer-function-over-method
|
||||
protected toProfileId(name: string): string {
|
||||
return name.toLowerCase();
|
||||
}
|
||||
|
||||
abstract getProfile(name: string): Promise<ProfileRecord | undefined>;
|
||||
|
||||
abstract start(): Promise<void>;
|
||||
abstract stop(): Promise<void>;
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
protected abstract run(statementName: 'stmtStoreProfile' | 'stmtUpdateCounts', data: any[]): Promise<void>;
|
||||
|
||||
|
||||
async storeProfile(c: ComplexCharacter): Promise<void> {
|
||||
const ca = new CharacterAnalysis(c.character);
|
||||
|
||||
const data = [
|
||||
this.toProfileId(c.character.name),
|
||||
c.character.name,
|
||||
JSON.stringify(c),
|
||||
Math.round(Date.now() / 1000),
|
||||
Math.round(Date.now() / 1000),
|
||||
ca.gender,
|
||||
ca.orientation,
|
||||
ca.furryPreference,
|
||||
ca.species,
|
||||
ca.age,
|
||||
null, // domSubRole
|
||||
null // position
|
||||
];
|
||||
|
||||
await this.run('stmtStoreProfile', data);
|
||||
}
|
||||
|
||||
async updateProfileCounts(
|
||||
name: string,
|
||||
guestbookCount: number | null,
|
||||
friendCount: number | null,
|
||||
groupCount: number | null
|
||||
): Promise<void> {
|
||||
await this.run(
|
||||
'stmtUpdateCounts',
|
||||
[Math.round(Date.now() / 1000), guestbookCount, friendCount, groupCount, this.toProfileId(name)]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// export abstract class SqlStore implements PermanentIndexedStore {
|
||||
// protected dbFile: string;
|
||||
// protected checkpointTimer: NodeJS.Timer | null = null;
|
||||
//
|
||||
// constructor(dbName: string = 'fchat-ascending.sqlite') {
|
||||
// this.dbFile = path.join(core.state.generalSettings!.logDirectory, dbName);
|
||||
// }
|
||||
//
|
||||
// // tslint:disable-next-line: prefer-function-over-method
|
||||
// protected toProfileId(name: string): string {
|
||||
// return name.toLowerCase();
|
||||
// }
|
||||
//
|
||||
// abstract getProfile(name: string): Promise<ProfileRecord | undefined>;
|
||||
//
|
||||
// abstract start(): Promise<void>;
|
||||
// abstract stop(): Promise<void>;
|
||||
//
|
||||
// // tslint:disable-next-line no-any
|
||||
// protected abstract run(statementName: 'stmtStoreProfile' | 'stmtUpdateCounts', data: any[]): Promise<void>;
|
||||
//
|
||||
//
|
||||
// async storeProfile(c: ComplexCharacter): Promise<void> {
|
||||
// const ca = new CharacterAnalysis(c.character);
|
||||
//
|
||||
// const data = [
|
||||
// this.toProfileId(c.character.name),
|
||||
// c.character.name,
|
||||
// JSON.stringify(c),
|
||||
// Math.round(Date.now() / 1000),
|
||||
// Math.round(Date.now() / 1000),
|
||||
// ca.gender,
|
||||
// ca.orientation,
|
||||
// ca.furryPreference,
|
||||
// ca.species,
|
||||
// ca.age,
|
||||
// null, // domSubRole
|
||||
// null // position
|
||||
// ];
|
||||
//
|
||||
// await this.run('stmtStoreProfile', data);
|
||||
// }
|
||||
//
|
||||
// async updateProfileMeta(
|
||||
// name: string,
|
||||
// images: CharacterImage[] | null,
|
||||
// guestbook: GuestbookState | null,
|
||||
// friends: CharacterFriend[] | null,
|
||||
// groups: CharacterGroup[] | null
|
||||
// ): Promise<void> {
|
||||
// throw new Error('Not implemented');
|
||||
// }
|
||||
//
|
||||
// // async updateProfileCounts(
|
||||
// // name: string,
|
||||
// // guestbookCount: number | null,
|
||||
// // friendCount: number | null,
|
||||
// // groupCount: number | null
|
||||
// // ): Promise<void> {
|
||||
// // await this.run(
|
||||
// // 'stmtUpdateCounts',
|
||||
// // [Math.round(Date.now() / 1000), guestbookCount, friendCount, groupCount, this.toProfileId(name)]
|
||||
// // );
|
||||
// // }
|
||||
// }
|
||||
//
|
||||
|
|
43
readme.md
43
readme.md
|
@ -1,24 +1,31 @@
|
|||
# F-Chat Ascending
|
||||
# F-Chat Rising
|
||||
|
||||
This repository contains a modified version of the mainline F-Chat 3.0 client.
|
||||
This repository contains a heavily customized version of the mainline F-Chat 3.0 client.
|
||||
|
||||
|
||||
## Key Differences
|
||||
|
||||
1. **Profile matching** automatically compares your profile with others to determine with whom you are compatible.
|
||||
1. **Automatic ad posting** repeatedly posts and rotates ads on selected channels.
|
||||
1. **Link previews** pop up to show a preview of the image when you hover your mouse over a link.
|
||||
|
||||
|
||||
### Details
|
||||
|
||||
* Ads view
|
||||
* Highlight ads from characters most interesting to you
|
||||
* View a character's recent ads
|
||||
* View characters' recent ads
|
||||
* Ad auto-posting
|
||||
* Manage channel's ad settings via "Tab Settings"
|
||||
* Manage channel ad settings via "Tab Settings"
|
||||
* Automatically re-post ads every 11-18 minutes (randomized) for up to 180 minutes
|
||||
* Rotate multiple ads on a single channel
|
||||
* Rotate multiple ads on a single channel by saving multiple ads in "Tab Settings"
|
||||
* Ad ratings
|
||||
* LFP ads are automatically rated and matched against your profile
|
||||
* Link previews
|
||||
* Hover cursor over any `[url]` to see a preview of it
|
||||
* Middle click any `[url]` to turn the preview into a sticky / interactive mode
|
||||
* Profile
|
||||
* Kinks are auto-compared when profile is loaded
|
||||
* Kinks are auto-compared when viewing character profile
|
||||
* Custom kink explanations can be expanded inline
|
||||
* Custom kinks are highlighted
|
||||
* Gender, anthro/human preference, age, and sexual preference are highlighted if compatible or incompatible
|
||||
|
@ -28,12 +35,26 @@ This repository contains a modified version of the mainline F-Chat 3.0 client.
|
|||
* Less informative side bar details (views, contact) are separated and shown in a less prominent way
|
||||
* Cleaner guestbook view
|
||||
* Character Search
|
||||
* Display pairing score on search results
|
||||
* Current search filters are listed on the search dialog
|
||||
* Search results are sorted based on match scores
|
||||
* Display match score in search results
|
||||
* Current search filters are listed in the search dialog
|
||||
* Search filters can be reset
|
||||
* General
|
||||
* Character profiles, guestbooks, friend lists, and image lists are cached for faster access.
|
||||
* Open conversation dialog for typing in a character name
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
1. Non-binary gender preference matches rely on kink preferences.
|
||||
1. 'Underage' kink is considered to apply to characters aged 16 or above.
|
||||
1. 'Ageplay' kink is considered to apply to characters aged 16 or below.
|
||||
1. 'Older characters' and 'younger characters' kink preferences are interpreted as age difference of 5+ years.
|
||||
|
||||
|
||||
## Todo / Ideas
|
||||
|
||||
* Collect data on ads / responses to determine which ads work best
|
||||
* Preview mode should allow detaching from the main window
|
||||
* Split chat view
|
||||
* Improvements to log browsing
|
||||
|
@ -41,11 +62,13 @@ This repository contains a modified version of the mainline F-Chat 3.0 client.
|
|||
* Which channels my chart partner is on?
|
||||
* Reposition ad settings and toggle
|
||||
* Cache image list, guestbook pages
|
||||
* Save character status messages
|
||||
* Bug: Invalid Ticket
|
||||
* Bug: Posting on the same second
|
||||
* Bug: Images tab count is off
|
||||
|
||||
|
||||
|
||||
# F-List Exported
|
||||
This repository contains the open source parts of F-list and F-Chat 3.0.
|
||||
All necessary files to build F-Chat 3.0 as an Electron, mobile or web application are included.
|
||||
|
@ -66,9 +89,7 @@ All necessary files to build F-Chat 3.0 as an Electron, mobile or web applicatio
|
|||
### Building on Windows
|
||||
|
||||
```
|
||||
npm install --global --production windows-build-tools
|
||||
npm install --global --production --vs2015 --add-python-to-path windows-build-tools
|
||||
npm install --global --production --add-python-to-path windows-build-tools node-gyp
|
||||
npm install --global --production --vs2015 --add-python-to-path windows-build-tools node-gyp
|
||||
```
|
||||
|
||||
### Packaging
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
<tabs class="card-header-tabs" v-model="tab">
|
||||
<span>Overview</span>
|
||||
<span>Info</span>
|
||||
<span v-if="!oldApi">Groups <span class="tab-count" v-if="groupCount !== null">({{ groupCount }})</span></span>
|
||||
<span v-if="!oldApi">Groups <span class="tab-count" v-if="groups !== null">({{ groups.length }})</span></span>
|
||||
<span>Images <span class="tab-count">({{ character.character.image_count }})</span></span>
|
||||
<span v-if="character.settings.guestbook">Guestbook <span class="tab-count" v-if="guestbookPostCount !== null">({{ guestbookPostCount }})</span></span>
|
||||
<span v-if="character.is_self || character.settings.show_friends">Friends <span class="tab-count" v-if="friendCount !== null">({{ friendCount }})</span></span>
|
||||
<span v-if="character.settings.guestbook">Guestbook <span class="tab-count" v-if="guestbook !== null">({{ guestbook.posts.length }})</span></span>
|
||||
<span v-if="character.is_self || character.settings.show_friends">Friends <span class="tab-count" v-if="friends !== null">({{ friends.length }})</span></span>
|
||||
</tabs>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
@ -75,7 +75,7 @@
|
|||
import { CharacterCacheRecord } from '../../learn/profile-cache';
|
||||
import * as Utils from '../utils';
|
||||
import {methods, Store} from './data_store';
|
||||
import {Character, SharedStore} from './interfaces';
|
||||
import {Character, CharacterFriend, CharacterGroup, GuestbookState, SharedStore} from './interfaces';
|
||||
|
||||
import DateDisplay from '../../components/date_display.vue';
|
||||
import Tabs from '../../components/tabs';
|
||||
|
@ -89,9 +89,10 @@
|
|||
import core from '../../chat/core';
|
||||
import { Matcher, MatchReport } from '../../learn/matcher';
|
||||
import MatchReportView from './match-report.vue';
|
||||
import {CharacterImage} from '../../interfaces';
|
||||
|
||||
const CHARACTER_CACHE_EXPIRE = 7 * 24 * 60 * 60 * 1000;
|
||||
const CHARACTER_COUNT_CACHE_EXPIRE = 10 * 24 * 60 * 60 * 1000;
|
||||
const CHARACTER_CACHE_EXPIRE = 7 * 24 * 60 * 60 * 1000; // 7 days (milliseconds)
|
||||
const CHARACTER_META_CACHE_EXPIRE = 10 * 24 * 60 * 60 * 1000; // 10 days (milliseconds)
|
||||
|
||||
interface ShowableVueTab extends Vue {
|
||||
show?(): void
|
||||
|
@ -127,10 +128,14 @@
|
|||
error = '';
|
||||
tab = '0';
|
||||
|
||||
guestbookPostCount: number | null = null;
|
||||
/* guestbookPostCount: number | null = null;
|
||||
friendCount: number | null = null;
|
||||
groupCount: number | null = null;
|
||||
groupCount: number | null = null; */
|
||||
|
||||
guestbook: GuestbookState | null = null;
|
||||
friends: CharacterFriend[] | null = null;
|
||||
groups: CharacterGroup[] | null = null;
|
||||
images: CharacterImage[] | null = null;
|
||||
|
||||
selfCharacter: Character | undefined;
|
||||
|
||||
|
@ -178,7 +183,13 @@
|
|||
);
|
||||
}
|
||||
|
||||
async load(mustLoad: boolean = true): Promise<void> {
|
||||
|
||||
async reload(): Promise<void> {
|
||||
await this.load(true, true);
|
||||
}
|
||||
|
||||
|
||||
async load(mustLoad: boolean = true, skipCache: boolean = false): Promise<void> {
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
|
||||
|
@ -194,7 +205,7 @@
|
|||
due.push(this.loadSelfCharacter());
|
||||
|
||||
if((mustLoad) || (this.character === undefined))
|
||||
due.push(this._getCharacter());
|
||||
due.push(this._getCharacter(skipCache));
|
||||
|
||||
await Promise.all(due);
|
||||
} catch(e) {
|
||||
|
@ -208,73 +219,83 @@
|
|||
}
|
||||
|
||||
|
||||
async countGuestbookPosts(): Promise<void> {
|
||||
async updateGuestbook(): Promise<void> {
|
||||
try {
|
||||
if ((!this.character) || (!_.get(this.character, 'settings.guestbook'))) {
|
||||
this.guestbookPostCount = null;
|
||||
this.guestbook = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const guestbookState = await methods.guestbookPageGet(this.character.character.id, 1, false);
|
||||
|
||||
this.guestbookPostCount = guestbookState.posts.length;
|
||||
// `${guestbookState.posts.length}${guestbookState.nextPage ? '+' : ''}`;
|
||||
this.guestbook = await methods.guestbookPageGet(this.character.character.id, 1, false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.guestbookPostCount = null;
|
||||
this.guestbook = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async countGroups(): Promise<void> {
|
||||
async updateGroups(): Promise<void> {
|
||||
try {
|
||||
if ((!this.character) || (this.oldApi)) {
|
||||
this.groupCount = null;
|
||||
this.groups = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const groups = await methods.groupsGet(this.character.character.id);
|
||||
|
||||
this.groupCount = groups.length; // `${groups.length}`;
|
||||
this.groups = await methods.groupsGet(this.character.character.id);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.groupCount = null;
|
||||
console.error('Update groups', err);
|
||||
this.groups = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async countFriends(): Promise<void> {
|
||||
async updateFriends(): Promise<void> {
|
||||
try {
|
||||
if (
|
||||
(!this.character)
|
||||
|| (!this.character.is_self) && (!this.character.settings.show_friends)
|
||||
) {
|
||||
this.friendCount = null;
|
||||
this.friends = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const friends = await methods.friendsGet(this.character.character.id);
|
||||
|
||||
this.friendCount = friends.length; // `${friends.length}`;
|
||||
this.friends = await methods.friendsGet(this.character.character.id);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.friendCount = null;
|
||||
console.error('Update friends', err);
|
||||
this.friends = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async updateCounts(name: string): Promise<void> {
|
||||
await this.countGuestbookPosts();
|
||||
await this.countFriends();
|
||||
await this.countGroups();
|
||||
async updateImages(): Promise<void> {
|
||||
try {
|
||||
if (!this.character) {
|
||||
this.images = null;
|
||||
return;
|
||||
}
|
||||
|
||||
await core.cache.profileCache.registerCount(
|
||||
this.images = await methods.imagesGet(this.character.character.id);
|
||||
} catch (err) {
|
||||
console.error('Update images', err);
|
||||
this.images = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async updateMeta(name: string): Promise<void> {
|
||||
await this.updateImages();
|
||||
await this.updateGuestbook();
|
||||
await this.updateFriends();
|
||||
await this.updateGroups();
|
||||
|
||||
await core.cache.profileCache.registerMeta(
|
||||
name,
|
||||
{
|
||||
lastCounted: Date.now() / 1000,
|
||||
groupCount: this.groupCount,
|
||||
friendCount: this.friendCount,
|
||||
guestbookCount: this.guestbookPostCount
|
||||
lastFetched: new Date(),
|
||||
groups: this.groups,
|
||||
friends: this.friends,
|
||||
guestbook: this.guestbook,
|
||||
images: this.images
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -318,11 +339,12 @@
|
|||
return null;
|
||||
}
|
||||
|
||||
private async _getCharacter(): Promise<void> {
|
||||
private async _getCharacter(skipCache: boolean = false): Promise<void> {
|
||||
this.character = undefined;
|
||||
this.friendCount = null;
|
||||
this.groupCount = null;
|
||||
this.guestbookPostCount = null;
|
||||
this.friends = null;
|
||||
this.groups = null;
|
||||
this.guestbook = null;
|
||||
this.images = null;
|
||||
|
||||
if (!this.name) {
|
||||
return;
|
||||
|
@ -330,7 +352,7 @@
|
|||
|
||||
const cache = await this.fetchCharacterCache();
|
||||
|
||||
this.character = (cache)
|
||||
this.character = (cache && !skipCache)
|
||||
? cache.character
|
||||
: await methods.characterData(this.name, this.characterid, false);
|
||||
|
||||
|
@ -338,18 +360,19 @@
|
|||
standardParser.inlines = this.character.character.inlines;
|
||||
|
||||
if (
|
||||
(cache)
|
||||
&& (cache.counts)
|
||||
&& (cache.counts.lastCounted)
|
||||
&& ((Date.now() / 1000) - cache.counts.lastCounted < CHARACTER_COUNT_CACHE_EXPIRE)
|
||||
(cache && !skipCache)
|
||||
&& (cache.meta)
|
||||
&& (cache.meta.lastFetched)
|
||||
&& (Date.now() - cache.meta.lastFetched.getTime() < CHARACTER_META_CACHE_EXPIRE)
|
||||
) {
|
||||
this.guestbookPostCount = cache.counts.guestbookCount;
|
||||
this.friendCount = cache.counts.friendCount;
|
||||
this.groupCount = cache.counts.groupCount;
|
||||
this.guestbook = cache.meta.guestbook;
|
||||
this.friends = cache.meta.friends;
|
||||
this.groups = cache.meta.groups;
|
||||
this.images = cache.meta.images;
|
||||
} else {
|
||||
// No awaits on these on purpose:
|
||||
// tslint:disable-next-line no-floating-promises
|
||||
this.updateCounts(this.name);
|
||||
this.updateMeta(this.name);
|
||||
}
|
||||
|
||||
// console.log('LoadChar', this.name, this.character);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import * as Utils from '../utils';
|
||||
import {methods} from './data_store';
|
||||
import {Character, CharacterFriend} from './interfaces';
|
||||
import core from '../../chat/core';
|
||||
|
||||
@Component
|
||||
export default class FriendsView extends Vue {
|
||||
|
@ -35,7 +36,7 @@
|
|||
this.error = '';
|
||||
this.shown = true;
|
||||
this.loading = true;
|
||||
this.friends = await methods.friendsGet(this.character.character.id);
|
||||
this.friends = await this.resolveFriends();
|
||||
} catch(e) {
|
||||
this.shown = false;
|
||||
if(Utils.isJSONError(e))
|
||||
|
@ -44,5 +45,15 @@
|
|||
}
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
async resolveFriends(): Promise<CharacterFriend[]> {
|
||||
const c = await core.cache.profileCache.get(this.character.character.name);
|
||||
|
||||
if ((c) && (c.meta) && (c.meta.friends)) {
|
||||
return c.meta.friends;
|
||||
}
|
||||
|
||||
return methods.friendsGet(this.character.character.id);
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -16,6 +16,7 @@
|
|||
import * as Utils from '../utils';
|
||||
import {methods} from './data_store';
|
||||
import {Character, CharacterGroup} from './interfaces';
|
||||
import core from '../../chat/core';
|
||||
|
||||
@Component
|
||||
export default class GroupsView extends Vue {
|
||||
|
@ -36,7 +37,7 @@
|
|||
this.error = '';
|
||||
this.shown = true;
|
||||
this.loading = true;
|
||||
this.groups = await methods.groupsGet(this.character.character.id);
|
||||
this.groups = await this.resolveGroups();
|
||||
} catch(e) {
|
||||
this.shown = false;
|
||||
if(Utils.isJSONError(e))
|
||||
|
@ -45,5 +46,15 @@
|
|||
}
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
async resolveGroups(): Promise<CharacterGroup[]> {
|
||||
const c = await core.cache.profileCache.get(this.character.character.name);
|
||||
|
||||
if ((c) && (c.meta) && (c.meta.groups)) {
|
||||
return c.meta.groups;
|
||||
}
|
||||
|
||||
return methods.groupsGet(this.character.character.id);
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -30,9 +30,10 @@
|
|||
import Vue from 'vue';
|
||||
import * as Utils from '../utils';
|
||||
import {methods, Store} from './data_store';
|
||||
import {Character, GuestbookPost} from './interfaces';
|
||||
import {Character, GuestbookPost, GuestbookState} from './interfaces';
|
||||
|
||||
import GuestbookPostView from './guestbook_post.vue';
|
||||
import core from '../../chat/core';
|
||||
|
||||
@Component({
|
||||
components: {'guestbook-post': GuestbookPostView}
|
||||
|
@ -73,7 +74,7 @@
|
|||
async getPage(): Promise<void> {
|
||||
try {
|
||||
this.loading = true;
|
||||
const guestbookState = await methods.guestbookPageGet(this.character.character.id, this.page, this.unapprovedOnly);
|
||||
const guestbookState = await this.resolvePage();
|
||||
this.posts = guestbookState.posts;
|
||||
this.hasNextPage = guestbookState.nextPage;
|
||||
this.canEdit = guestbookState.canEdit;
|
||||
|
@ -103,8 +104,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
async resolvePage(): Promise<GuestbookState> {
|
||||
if (this.page === 1) {
|
||||
const c = await core.cache.profileCache.get(this.character.character.name);
|
||||
|
||||
if ((c) && (c.meta) && (c.meta.guestbook)) {
|
||||
return c.meta.guestbook;
|
||||
}
|
||||
}
|
||||
|
||||
return methods.guestbookPageGet(this.character.character.id, this.page, this.unapprovedOnly);
|
||||
}
|
||||
|
||||
async show(): Promise<void> {
|
||||
return this.getPage();
|
||||
await this.getPage();
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -36,6 +36,7 @@
|
|||
import * as Utils from '../utils';
|
||||
import {methods} from './data_store';
|
||||
import {Character} from './interfaces';
|
||||
import core from '../../chat/core';
|
||||
|
||||
@Component
|
||||
export default class ImagesView extends Vue {
|
||||
|
@ -58,7 +59,7 @@
|
|||
this.error = '';
|
||||
this.shown = true;
|
||||
this.loading = true;
|
||||
this.images = await methods.imagesGet(this.character.character.id);
|
||||
this.images = await this.resolveImages();
|
||||
} catch(e) {
|
||||
this.shown = false;
|
||||
if(Utils.isJSONError(e))
|
||||
|
@ -68,6 +69,16 @@
|
|||
this.loading = false;
|
||||
}
|
||||
|
||||
async resolveImages(): Promise<CharacterImage[]> {
|
||||
const c = await core.cache.profileCache.get(this.character.character.name);
|
||||
|
||||
if ((c) && (c.meta) && (c.meta.images)) {
|
||||
return c.meta.images;
|
||||
}
|
||||
|
||||
return methods.imagesGet(this.character.character.id);
|
||||
}
|
||||
|
||||
handleImageClick(e: MouseEvent, image: CharacterImage): void {
|
||||
if(this.usePreview) {
|
||||
this.previewImage = methods.imageUrl(image);
|
||||
|
|
Loading…
Reference in New Issue