<commit_before>import {
  ChangeDetectorRef,
  Component,
  ElementRef,
  Input,
  OnDestroy,
  OnInit,
  Optional,
  SkipSelf,
  ViewChild,
  Injector,
} from '@angular/core';
import { Router } from '@angular/router';

import { Client } from '../../../services/api';
import { Session } from '../../../services/session';
import { ScrollService } from '../../../services/ux/scroll';
import { AnalyticsService } from '../../../services/analytics';
import { MindsBlogEntity } from '../../../interfaces/entities';

import { AttachmentService } from '../../../services/attachment';
import { ContextService } from '../../../services/context.service';
import { optimizedResize } from '../../../utils/optimized-resize';
import { OverlayModalService } from '../../../services/ux/overlay-modal';
import { ActivityService } from '../../../common/services/activity.service';
import { ShareModalComponent } from '../../../modules/modals/share/share';
import { FeaturesService } from '../../../services/features.service';
import { MetaService } from '../../../common/services/meta.service';
import { ConfigsService } from '../../../common/services/configs.service';
import { ClientMetaDirective } from '../../../common/directives/client-meta.directive';
import { ClientMetaService } from '../../../common/services/client-meta.service';
import { FormToastService } from '../../../common/services/form-toast.service';

@Component({
  selector: 'm-blog-view',
  host: {
    class: 'm-blog',
  },
  templateUrl: 'view.html',
  styleUrls: ['./view.ng.scss'],
  providers: [ActivityService],
})
export class BlogView implements OnInit, OnDestroy {
  readonly cdnUrl: string;
  readonly siteUrl: string;

  guid: string;
  blog: MindsBlogEntity;
  // sharetoggle: boolean = false;
  deleteToggle: boolean = false;
  element;

  inProgress: boolean = false;
  moreData: boolean = true;
  activeBlog: number = 0;

  visible: boolean = false;
  index: number = 0;

  scroll_listener;

  menuOptions: Array<string> = [
    'edit',
    'follow',
    'feature',
    'delete',
    'report',
    'subscribe',
    'set-explicit',
    'remove-explicit',
    'rating',
    'allow-comments',
  ];

  @Input() showActions: boolean = true;
  @Input() showComments: boolean = true;

  @Input('blog') set _blog(value: MindsBlogEntity) {
    this.blog = value;
    setTimeout(() => {
      this.calculateLockScreenHeight();
    });
  }

  @Input('index') set _index(value: number) {
    this.index = value;
    if (this.index === 0) {
      this.visible = true;
    }
  }

  set data(value: any) {
    this.blog = value;
  }

  @ViewChild('lockScreen', { read: ElementRef }) lockScreen;

  constructor(
    public session: Session,
    public client: Client,
    public router: Router,
    _element: ElementRef,
    public scroll: ScrollService,
    public metaService: MetaService,
    public attachment: AttachmentService,
    private context: ContextService,
    public analytics: AnalyticsService,
    public analyticsService: AnalyticsService,
    protected activityService: ActivityService,
    private cd: ChangeDetectorRef,
    private overlayModal: OverlayModalService,
    private clientMetaService: ClientMetaService,
    public featuresService: FeaturesService,
    protected toasterService: FormToastService,
    @SkipSelf() injector: Injector,
    @Optional() @SkipSelf() protected parentClientMeta: ClientMetaDirective,
    configs: ConfigsService
  ) {
    this.cdnUrl = configs.get('cdn_url');
    this.siteUrl = configs.get('site_url');
    this.element = _element.nativeElement;
    optimizedResize.add(this.onResize.bind(this));
  }

  ngOnInit() {
    this.isVisible();
    this.context.set('object:blog');
    this.clientMetaService.recordView(this.blog, this.parentClientMeta, {
      source: 'single',
      medium: 'single',
    });
  }

  isVisible() {
    // listens every 0.6 seconds
    this.scroll_listener = this.scroll.listen(
      e => {
        const bounds = this.element.getBoundingClientRect();
        if (
          bounds.top < this.scroll.view.clientHeight &&
          bounds.top + bounds.height > this.scroll.view.clientHeight
        ) {
          let url = `${this.siteUrl}blog/view/${this.blog.guid}`;

          if (this.blog.route) {
            url = `${this.siteUrl}${this.blog.route}`;
          }

          if (!this.visible) {
            window.history.pushState(null, this.blog.title, url);
            this.updateMeta();
            this.analyticsService.send('pageview', {
              url: `/blog/view/${this.blog.guid}`,
            });
          }
          this.visible = true;
        } else {
          this.visible = false;
        }
      },
      0,
      300
    );
  }

  /**
   * Call to delete a blog entity
   * Redirects on success.
   */
  async delete(): Promise<void> {
    try {
      const response: any = await this.client.delete(
        `api/v1/blog/${this.blog.guid}`
      );<commit_msg>(fix): blogs to support automatically unlocking if a query param exists - closes engine#1745<commit_after>import {
  ChangeDetectorRef,
  Component,
  ElementRef,
  Input,
  OnDestroy,
  OnInit,
  Optional,
  SkipSelf,
  ViewChild,
  Injector,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';

import { Client } from '../../../services/api';
import { Session } from '../../../services/session';
import { ScrollService } from '../../../services/ux/scroll';
import { AnalyticsService } from '../../../services/analytics';
import { MindsBlogEntity } from '../../../interfaces/entities';

import { AttachmentService } from '../../../services/attachment';
import { ContextService } from '../../../services/context.service';
import { optimizedResize } from '../../../utils/optimized-resize';
import { OverlayModalService } from '../../../services/ux/overlay-modal';
import { ActivityService } from '../../../common/services/activity.service';
import { ShareModalComponent } from '../../../modules/modals/share/share';
import { FeaturesService } from '../../../services/features.service';
import { MetaService } from '../../../common/services/meta.service';
import { ConfigsService } from '../../../common/services/configs.service';
import { ClientMetaDirective } from '../../../common/directives/client-meta.directive';
import { ClientMetaService } from '../../../common/services/client-meta.service';
import { FormToastService } from '../../../common/services/form-toast.service';

@Component({
  selector: 'm-blog-view',
  host: {
    class: 'm-blog',
  },
  templateUrl: 'view.html',
  styleUrls: ['./view.ng.scss'],
  providers: [ActivityService],
})
export class BlogView implements OnInit, OnDestroy {
  readonly cdnUrl: string;
  readonly siteUrl: string;

  guid: string;
  blog: MindsBlogEntity;
  // sharetoggle: boolean = false;
  deleteToggle: boolean = false;
  element;

  inProgress: boolean = false;
  moreData: boolean = true;
  activeBlog: number = 0;

  visible: boolean = false;
  index: number = 0;

  scroll_listener;

  menuOptions: Array<string> = [
    'edit',
    'follow',
    'feature',
    'delete',
    'report',
    'subscribe',
    'set-explicit',
    'remove-explicit',
    'rating',
    'allow-comments',
  ];

  @Input() showActions: boolean = true;
  @Input() showComments: boolean = true;

  @Input('blog') set _blog(value: MindsBlogEntity) {
    this.blog = value;
    setTimeout(() => {
      this.calculateLockScreenHeight();
    });
  }

  @Input('index') set _index(value: number) {
    this.index = value;
    if (this.index === 0) {
      this.visible = true;
    }
  }

  set data(value: any) {
    this.blog = value;
  }

  @ViewChild('lockScreen', { read: ElementRef }) lockScreen;
  @ViewChild('lockScreen') lockScreenComponent;

  constructor(
    public session: Session,
    public client: Client,
    public router: Router,
    private route: ActivatedRoute,
    _element: ElementRef,
    public scroll: ScrollService,
    public metaService: MetaService,
    public attachment: AttachmentService,
    private context: ContextService,
    public analytics: AnalyticsService,
    public analyticsService: AnalyticsService,
    protected activityService: ActivityService,
    private cd: ChangeDetectorRef,
    private overlayModal: OverlayModalService,
    private clientMetaService: ClientMetaService,
    public featuresService: FeaturesService,
    protected toasterService: FormToastService,
    @SkipSelf() injector: Injector,
    @Optional() @SkipSelf() protected parentClientMeta: ClientMetaDirective,
    configs: ConfigsService
  ) {
    this.cdnUrl = configs.get('cdn_url');
    this.siteUrl = configs.get('site_url');
    this.element = _element.nativeElement;
    optimizedResize.add(this.onResize.bind(this));
  }

  ngOnInit() {
    this.isVisible();
    this.context.set('object:blog');
    this.clientMetaService.recordView(this.blog, this.parentClientMeta, {
      source: 'single',
      medium: 'single',
    });

    this.unlockIfQueryParam();
  }

  isVisible() {
    // listens every 0.6 seconds
    this.scroll_listener = this.scroll.listen(
      e => {
        const bounds = this.element.getBoundingClientRect();
        if (
          bounds.top < this.scroll.view.clientHeight &&
          bounds.top + bounds.height > this.scroll.view.clientHeight
        ) {
          let url = `${this.siteUrl}blog/view/${this.blog.guid}`;

          if (this.blog.route) {
            url = `${this.siteUrl}${this.blog.route}`;
          }

          if (!this.visible) {
            window.history.pushState(null, this.blog.title, url);
            this.updateMeta();
            this.analyticsService.send('pageview', {
              url: `/blog/view/${this.blog.guid}`,
            });
          }
          this.visible = true;
        } else {
          this.visible = false;
        }
      },
      0,
      300
    );
  }

  /**
   * Unlocks the paywall if there is an unlock query param
   * !! This must be within a 1 minute window of the current time !!
   * This constraints prevents users accidentaly sharing unlocked posts
   */
  unlockIfQueryParam(): void {
    const unlockTs = parseInt(this.route.snapshot.queryParamMap.get('unlock'));
    if (unlockTs > Date.now() - 60000 && unlockTs < Date.now() + 60000) {
      setTimeout(() => {
        this.lockScreenComponent.unlock(); // Unlock if param provided and within 1 minute
      }, 1);
    }
  }

  /**
   * Call to delete a blog entity
   * Redirects on success.
   */
  async delete(): Promise<void> {
    try {
      const response: any = await this.client.delete(
        `api/v1/blog/${this.blog.guid}`
      );
{'fraction_non_alphanumeric': 0.15280263777673103, 'fraction_numerical': 0.003485633537447009, 'mean_word_length': 2.7968526466380546, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3085225', 'n_tokens_mistral': 3162, 'n_tokens_neox': 3139, 'n_words': 706}