<commit_before>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import SettingsStore, { SettingLevel } from "../settings/SettingsStore";
import { Room } from "matrix-js-sdk/src/models/room";
import { ActionPayload } from "../dispatcher/payloads";
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
import defaultDispatcher from "../dispatcher/dispatcher";
import { arrayHasDiff } from "../utils/arrays";

const MAX_ROOMS = 20; // arbitrary
const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up

interface IState {
    enabled?: boolean;
    rooms?: Room[];
}

export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
    private static internalInstance = new BreadcrumbsStore();

    private waitingRooms: { roomId: string, addedTs: number }[] = [];

    private constructor() {
        super(defaultDispatcher);

        SettingsStore.monitorSetting("breadcrumb_rooms", null);
        SettingsStore.monitorSetting("breadcrumbs", null);
    }

    public static get instance(): BreadcrumbsStore {
        return BreadcrumbsStore.internalInstance;
    }

    public get rooms(): Room[] {
        return this.state.rooms || [];
    }

    public get visible(): boolean {
        return this.state.enabled;
    }

    protected async onAction(payload: ActionPayload) {
        if (!this.matrixClient) return;

        if (payload.action === 'setting_updated') {
            if (payload.settingName === 'breadcrumb_rooms') {
                await this.updateRooms();
            } else if (payload.settingName === 'breadcrumbs') {
                await this.updateState({enabled: SettingsStore.getValue("breadcrumbs", null)});
            }
        } else if (payload.action === 'view_room') {
            if (payload.auto_join && !this.matrixClient.getRoom(payload.room_id)) {
                // Queue the room instead of pushing it immediately. We're probably just
                // waiting for a room join to complete.
                this.waitingRooms.push({roomId: payload.room_id, addedTs: Date.now()});
            } else {
                // The tests might not result in a valid room object.
                const room = this.matrixClient.getRoom(payload.room_id);
                if (room) await this.appendRoom(room);
            }
        }
    }

    protected async onReady() {
        await this.updateRooms();
        await this.updateState({enabled: SettingsStore.getValue("breadcrumbs", null)});

        this.matrixClient.on("Room.myMembership", this.onMyMembership);
        this.matrixClient.on("Room", this.onRoom);
    }

    protected async onNotReady() {
        this.matrixClient.removeListener("Room.myMembership", this.onMyMembership);
        this.matrixClient.removeListener("Room", this.onRoom);
    }

    private onMyMembership = async (room: Room) => {
        // We turn on breadcrumbs by default once the user has at least 1 room to show.
        if (!this.state.enabled) {
            await SettingsStore.setValue("breadcrumbs", null, SettingLevel.ACCOUNT, true);
        }
    };

    private onRoom = async (room: Room) => {
        const waitingRoom = this.waitingRooms.find(r => r.roomId === room.roomId);
        if (!waitingRoom) return;
        this.waitingRooms.splice(this.waitingRooms.indexOf(waitingRoom), 1);

        if ((Date.now() - waitingRoom.addedTs) > AUTOJOIN_WAIT_THRESHOLD_MS) return; // Too long ago.
        await this.appendRoom(room);
    };

    private async updateRooms() {
        let roomIds = SettingsStore.getValue("breadcrumb_rooms");
        if (!roomIds || roomIds.length === 0) roomIds = [];

        const rooms = roomIds.map(r => this.matrixClient.getRoom(r)).filter(r => !!r);
        const currentRooms = this.state.rooms || [];
        if (!arrayHasDiff(rooms, currentRooms)) return; // no change (probably echo)<commit_msg>Disable new breadcrumb store when old room list is in use<commit_after>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import SettingsStore, { SettingLevel } from "../settings/SettingsStore";
import { Room } from "matrix-js-sdk/src/models/room";
import { ActionPayload } from "../dispatcher/payloads";
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
import defaultDispatcher from "../dispatcher/dispatcher";
import { arrayHasDiff } from "../utils/arrays";
import { RoomListStoreTempProxy } from "./room-list/RoomListStoreTempProxy";

const MAX_ROOMS = 20; // arbitrary
const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up

interface IState {
    enabled?: boolean;
    rooms?: Room[];
}

export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
    private static internalInstance = new BreadcrumbsStore();

    private waitingRooms: { roomId: string, addedTs: number }[] = [];

    private constructor() {
        super(defaultDispatcher);

        SettingsStore.monitorSetting("breadcrumb_rooms", null);
        SettingsStore.monitorSetting("breadcrumbs", null);
    }

    public static get instance(): BreadcrumbsStore {
        return BreadcrumbsStore.internalInstance;
    }

    public get rooms(): Room[] {
        return this.state.rooms || [];
    }

    public get visible(): boolean {
        return this.state.enabled;
    }

    protected async onAction(payload: ActionPayload) {
        if (!this.matrixClient) return;

        // TODO: Remove when new room list is made the default
        if (!RoomListStoreTempProxy.isUsingNewStore()) return;

        if (payload.action === 'setting_updated') {
            if (payload.settingName === 'breadcrumb_rooms') {
                await this.updateRooms();
            } else if (payload.settingName === 'breadcrumbs') {
                await this.updateState({enabled: SettingsStore.getValue("breadcrumbs", null)});
            }
        } else if (payload.action === 'view_room') {
            if (payload.auto_join && !this.matrixClient.getRoom(payload.room_id)) {
                // Queue the room instead of pushing it immediately. We're probably just
                // waiting for a room join to complete.
                this.waitingRooms.push({roomId: payload.room_id, addedTs: Date.now()});
            } else {
                // The tests might not result in a valid room object.
                const room = this.matrixClient.getRoom(payload.room_id);
                if (room) await this.appendRoom(room);
            }
        }
    }

    protected async onReady() {
        // TODO: Remove when new room list is made the default
        if (!RoomListStoreTempProxy.isUsingNewStore()) return;

        await this.updateRooms();
        await this.updateState({enabled: SettingsStore.getValue("breadcrumbs", null)});

        this.matrixClient.on("Room.myMembership", this.onMyMembership);
        this.matrixClient.on("Room", this.onRoom);
    }

    protected async onNotReady() {
        // TODO: Remove when new room list is made the default
        if (!RoomListStoreTempProxy.isUsingNewStore()) return;

        this.matrixClient.removeListener("Room.myMembership", this.onMyMembership);
        this.matrixClient.removeListener("Room", this.onRoom);
    }

    private onMyMembership = async (room: Room) => {
        // We turn on breadcrumbs by default once the user has at least 1 room to show.
        if (!this.state.enabled) {
            await SettingsStore.setValue("breadcrumbs", null, SettingLevel.ACCOUNT, true);
        }
    };

    private onRoom = async (room: Room) => {
        const waitingRoom = this.waitingRooms.find(r => r.roomId === room.roomId);
        if (!waitingRoom) return;
        this.waitingRooms.splice(this.waitingRooms.indexOf(waitingRoom), 1);

        if ((Date.now() - waitingRoom.addedTs) > AUTOJOIN_WAIT_THRESHOLD_MS) return; // Too long ago.
        await this.appendRoom(room);
    };

    private async updateRooms() {
        let roomIds = SettingsStore.getValue("breadcrumb_rooms");
        if (!roomIds || roomIds.length === 0) roomIds = [];

        const rooms = roomIds.map(r => this.matrixClient.getRoom(r)).filter(r => !!r);
        const currentRooms = this.state.rooms || [];
        if (!arrayHasDiff(rooms, currentRooms)) return; // no change (probably echo)
{'fraction_non_alphanumeric': 0.11164839888244144, 'fraction_numerical': 0.004298302170642596, 'mean_word_length': 2.9088618227635448, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '793823', 'n_tokens_mistral': 2610, 'n_tokens_neox': 2527, 'n_words': 746}