Skip to content

Instantly share code, notes, and snippets.

@yurafuca
Created March 18, 2019 15:23
Show Gist options
  • Select an option

  • Save yurafuca/eac4ef68f9d0cfdb3da76c109d31bcaf to your computer and use it in GitHub Desktop.

Select an option

Save yurafuca/eac4ef68f9d0cfdb3da76c109d31bcaf to your computer and use it in GitHub Desktop.
import { diff } from 'deep-object-diff';
import { Community, Program } from "./Manageable";
import { ProgramBuilder, CommunityBuilder } from "./CheckableBuilder";
class BucketClient {
private rev: number;
private readonly token: string;
constructor(revision: number, token: string) {
this.rev = revision;
this.token = token;
}
revision(): number {
return this.rev;
}
setRevision(revision: number, token: string) {
if (token == this.token) {
this.rev = revision;
}
}
}
export class Bucket {
private communities: Community[];
private revision: number;
private readonly token: string;
static ANONYMOUS_PREFIX = "@@ANONYMOUS";
private anonymousCount: number;
constructor() {
this.communities = new Array<Community>();
this.revision = 0;
this.token = "@@NICOSAPO";
this.anonymousCount = 0;
}
touch(communityBuilder: CommunityBuilder) {
this.touchCommunity(communityBuilder);
}
assign(communityBuilder: CommunityBuilder, programBuilder: ProgramBuilder) {
this.touchBoth(communityBuilder, programBuilder, this.revision);
}
appoint(communityBuilder: CommunityBuilder, programBuilder: ProgramBuilder) {
this.touchBoth(communityBuilder, programBuilder, -1);
}
assignOrphan(programBuilder: ProgramBuilder, thumbnail: string | null = null) {
if (this.findProgram(programBuilder, this.basicCommunities()) != null) {
return;
}
const prefix = this.takeAnonymousPrefix(programBuilder);
const anonymous = new CommunityBuilder().id(prefix);
if (thumbnail) {
anonymous.thumbnailUrl(thumbnail);
}
this.assign(anonymous, programBuilder);
}
appointOrphan(programBuilder: ProgramBuilder, thumbnail: string | null = null) {
if (this.findProgram(programBuilder, this.basicCommunities()) != null) {
return;
}
const prefix = this.takeAnonymousPrefix(programBuilder);
const anonymous = new CommunityBuilder().id(prefix);
if (thumbnail) {
anonymous.thumbnailUrl(thumbnail);
}
this.appoint(anonymous, programBuilder);
}
mask(communityBuilders: CommunityBuilder[]) {
const communities = communityBuilders.map(builder => this.touchCommunity(builder));
const survivors = communities.map(c => c.id);
this.communities = this.communities.filter(c => {
return survivors.includes(c.id) ||
(c.id.startsWith(Bucket.ANONYMOUS_PREFIX) && c.programs.length > 0) ||
c.shouldOpenAutomatically ||
c.programs.some(p => p.shouldOpenAutomatically) ||
c.programs.some(p => p.isVisiting)
});
this.revision += 1;
}
createClient(): BucketClient {
return new BucketClient(0, this.token);
}
private takeAnonymousPrefix(programBuilder: ProgramBuilder) {
const program = this.findProgram(programBuilder, this.anonymousCommunities());
if (program != null) {
return program.community.id;
}
return Bucket.ANONYMOUS_PREFIX + "@" + this.anonymousCount++;
}
private touchCommunity(communityBuilder: CommunityBuilder): Community {
const community = this.createCommunity(communityBuilder);
// Replace.
this.communities = this.communities.filter(c => c.id != community.id);
this.communities.push(community);
return community;
}
private touchBoth(communityBuilder: CommunityBuilder, programBuilder: ProgramBuilder, revision: number) {
const gracefulProgram = this.findProgram(programBuilder, this.anonymousCommunities());
if (gracefulProgram != null) {
gracefulProgram.community.detachProgram(gracefulProgram);
const builder = new ProgramBuilder()
.id(gracefulProgram.id)
.title(gracefulProgram.title)
.isVisiting(gracefulProgram.isVisiting)
.shouldMoveAutomatically(gracefulProgram.shouldMoveAutomatically)
.shouldOpenAutomatically(gracefulProgram.shouldOpenAutomatically);
this.touchBoth(communityBuilder, builder, this.revision);
}
const community = this.touchCommunity(communityBuilder);
const program = this.createProgram(programBuilder, community, revision);
// Attach.
community.attachProgram(program);
}
takeProgramsShouldCancelOpen(client: BucketClient): Program[] {
const result = this.communities
.map(c => c.programs)
.reduce((array, v) => array.concat(v), [])
.filter(p => p.isVisiting)
.filter(p => !p.isVisitedAutomatically)
.filter(p =>
p.shouldOpenAutomatically || p.community.shouldOpenAutomatically
)
.filter(p => p.revision() != -1 && p.revision() > client.revision());
client.setRevision(this.revision, this.token);
return result;
}
takeProgramsShouldOpen(client: BucketClient): Program[] {
const result = this.communities
.map(c => c.programs)
.reduce((array, v) => array.concat(v), [])
.filter(p => !p.isVisiting)
.filter(p =>
p.shouldOpenAutomatically || p.community.shouldOpenAutomatically
)
.filter(p => p.revision() != -1 && p.revision() > client.revision());
client.setRevision(this.revision, this.token);
return result;
}
takeProgramsShouldNotify(client: BucketClient): Program[] {
const result = this.communities
.map(c => c.programs)
.reduce((array, v) => array.concat(v), [])
.filter(p => !p.isVisiting)
.filter(p => p.community.isFollowing)
.filter(p =>
!p.shouldOpenAutomatically && !p.community.shouldOpenAutomatically
)
.filter(p => p.revision() != -1 && p.revision() > client.revision());
client.setRevision(this.revision, this.token);
return result;
}
communitiesShouldPoll(): Community[] {
return this.communities.filter(c => c.shouldOpenAutomatically);
}
programsShouldPoll(): Program[] {
return this.communities
.map(c => c.programs)
.reduce((array, v) => array.concat(v), [])
.filter(p => p.shouldOpenAutomatically);
}
programs(): Program[] {
return this.communities
.map(c => c.programs)
.reduce((array, v) => array.concat(v), [])
}
communityList(): Community[] {
return this.communities;
}
private createCommunity(builder: CommunityBuilder): Community {
const draft = builder.build();
const previous = this.findCommunity(draft, this.communities);
const reference = previous || draft;
// Update.
builder.title(builder.getTitle() || reference.title);
builder.thumbnailUrl(builder.getThumbnailUrl() || reference.thumbnailUrl);
const isFollowing = builder.getIsFollowing();
if (isFollowing != null) {
builder.isFollowing(isFollowing);
} else {
builder.isFollowing(reference.isFollowing);
}
const shouldOpen = builder.getShouldOpenAutomatically();
if (shouldOpen != null) {
builder.shouldOpenAutomatically(shouldOpen);
} else {
builder.shouldOpenAutomatically(reference.shouldOpenAutomatically)
}
// Build.
const community = builder.build();
// Attach previous programs.
reference.programs.forEach(p => {
community.attachProgram(p);
p.community = community;
});
// Print diff.
Bucket.difference(previous, community);
return community;
}
private createProgram(builder: ProgramBuilder, parent: Community, revision: number): Program {
const draft = builder.build(revision);
const previous = this.findProgram(builder, [parent]);
const reference = previous || draft;
// Update.
builder.title(builder.getTitle() || reference.title);
const isVisiting = builder.getIsVisiting();
if (isVisiting != null) {
builder.isVisiting(isVisiting);
} else {
builder.isVisiting(reference.isVisiting);
}
const shouldOpen = builder.getShouldOpenAutomatically();
if (shouldOpen != null) {
builder.shouldOpenAutomatically(shouldOpen);
} else {
builder.shouldOpenAutomatically(reference.shouldOpenAutomatically)
}
const shouldMove = builder.getShouldMoveAutomatically();
if (shouldMove != null) {
builder.shouldMoveAutomatically(shouldMove);
} else {
builder.shouldMoveAutomatically(reference.shouldMoveAutomatically)
}
// Choose revision.
let rev: number;
if (reference.revision() == -1) {
rev = revision;
} else {
rev = reference.revision();
}
// Build.
const program = builder.build(rev);
program.community = parent;
// Print diff.
Bucket.difference(previous, program);
return program;
}
private findCommunity(community: Community, communities: Community[]): Community | null {
const ids = communities.map(c => c.id).filter(id => id == community.id);
if (ids.length == 0) {
return null;
}
const id = ids[0];
return communities.filter(c => c.id == id)[0];
}
private findProgram(programBuilder: ProgramBuilder, parents: Community[]): Program | null {
const program = programBuilder.build(-100); // => dummy revision.
return parents
.map(c => c.programs)
.reduce((array, v) => array.concat(v), [])
.filter(p => p.id == program.id)[0];
}
private anonymousCommunities() {
return this.communities.filter(c => c.id.startsWith(Bucket.ANONYMOUS_PREFIX));
}
private basicCommunities() {
return this.communities.filter(c => !c.id.startsWith(Bucket.ANONYMOUS_PREFIX));
}
private static difference(prev: object | null, next: object) {
if (prev != null) {
const difference = diff(prev, next);
if (!Bucket.isEmpty(difference)) {
console.log(difference)
}
} else {
console.info(next);
}
}
private static isEmpty(obj: {}): boolean {
return Object.keys(obj).length === 0 && obj.constructor === Object
}
}
const bucket = new Bucket();
export default bucket;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment