mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-02 13:49:00 +02:00
Convert utility functions to TypeScript (#22658)
* Convert event-tracking to TypeScript * Convert local-storage to TypeScript * Convert mapSeries to TypeScript * Convert SessionStorage to TypeScript * Convert account-upgrade to TypeScript * Convert isValidTeXFile to TypeScript * Convert date functions to TypeScript * Convert EventEmitter to TypeScript * Convert isNetworkError to TypeScript * Convert webpack-public-path to TypeScript * Convert displayNameForUser to TypeScript GitOrigin-RevId: 79c5a2d1101fcd520f3116f0f4af29d974189d94
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import isValidTeXFile from '../../../../main/is-valid-tex-file'
|
||||
import { isValidTeXFile } from '../../../../main/is-valid-tex-file'
|
||||
import { useEditorContext } from '../../../../shared/context/editor-context'
|
||||
import { useProjectSettingsContext } from '../../context/project-settings-context'
|
||||
import SettingsMenuSelect from './settings-menu-select'
|
||||
|
||||
+1
-2
@@ -3,7 +3,6 @@ import Icon from '../../../../shared/components/icon'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import StartFreeTrialButton from '../../../../shared/components/start-free-trial-button'
|
||||
import { paywallPrompt } from '../../../../main/account-upgrade'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
|
||||
function FeatureItem({ text }: { text: string }) {
|
||||
@@ -22,7 +21,7 @@ export function OwnerPaywallPrompt() {
|
||||
|
||||
useEffect(() => {
|
||||
eventTracking.send('subscription-funnel', 'editor-click-feature', 'history')
|
||||
paywallPrompt('history')
|
||||
eventTracking.sendMB('paywall-prompt', { 'paywall-type': 'history' })
|
||||
}, [])
|
||||
|
||||
const handleFreeTrialClick = useCallback(() => {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { User } from '@/features/history/services/types/shared'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { formatUserName } from '@/features/history/utils/history-details'
|
||||
|
||||
export default function displayNameForUser(
|
||||
user:
|
||||
| (User & {
|
||||
name?: string
|
||||
})
|
||||
| null
|
||||
) {
|
||||
if (user == null) {
|
||||
return 'Anonymous'
|
||||
}
|
||||
if (user.id === getMeta('ol-user').id) {
|
||||
return 'you'
|
||||
}
|
||||
if (user.name != null) {
|
||||
return user.name
|
||||
}
|
||||
return formatUserName(user)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import displayNameForUser from '../../../ide/history/util/displayNameForUser'
|
||||
import moment from 'moment/moment'
|
||||
import ColorManager from '../../../ide/colors/ColorManager'
|
||||
import { DocDiffChunk, Highlight } from '../services/types/doc'
|
||||
import { TFunction } from 'i18next'
|
||||
import displayNameForUser from './display-name-for-user'
|
||||
|
||||
export function highlightsFromDiffResponse(
|
||||
chunks: DocDiffChunk[],
|
||||
|
||||
@@ -12,7 +12,7 @@ import useScopeEventEmitter from '@/shared/hooks/use-scope-event-emitter'
|
||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
import useScopeValue from '@/shared/hooks/use-scope-value'
|
||||
import isValidTeXFile from '@/main/is-valid-tex-file'
|
||||
import { isValidTeXFile } from '@/main/is-valid-tex-file'
|
||||
import localStorage from '@/infrastructure/local-storage'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
prefetchLargeEnabled,
|
||||
trackPdfDownloadEnabled,
|
||||
} from './pdf-caching-flags'
|
||||
import { isNetworkError } from '@/utils/isNetworkError'
|
||||
import { isNetworkError } from '@/utils/is-network-error'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import { PDFJS } from './pdf-js'
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useUserContext } from '../../../shared/context/user-context'
|
||||
import { upgradePlan } from '../../../main/account-upgrade'
|
||||
import { upgradePlan } from '@/main/account-upgrade'
|
||||
import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import { useFeatureFlag } from '../../../shared/context/split-test-context'
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Notification from '@/shared/components/notification'
|
||||
import { upgradePlan } from '../../../../main/account-upgrade'
|
||||
import { upgradePlan } from '@/main/account-upgrade'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Notification from '@/shared/components/notification'
|
||||
import { upgradePlan } from '../../../../main/account-upgrade'
|
||||
import { upgradePlan } from '@/main/account-upgrade'
|
||||
import { linkSharingEnforcementDate } from '../../utils/link-sharing'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
|
||||
@@ -3,7 +3,7 @@ import useScopeValue from '@/shared/hooks/use-scope-value'
|
||||
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
||||
import useTutorial from '@/shared/hooks/promotions/use-tutorial'
|
||||
import { sendMB } from '../../../infrastructure/event-tracking'
|
||||
import isValidTeXFile from '../../../main/is-valid-tex-file'
|
||||
import { isValidTeXFile } from '../../../main/is-valid-tex-file'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
EditorSwitchBeginnerTooltip,
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import Icon from '../../../../shared/components/icon'
|
||||
import { useProjectContext } from '../../../../shared/context/project-context'
|
||||
import { useUserContext } from '../../../../shared/context/user-context'
|
||||
import { startFreeTrial, upgradePlan } from '../../../../main/account-upgrade'
|
||||
import { startFreeTrial, upgradePlan } from '@/main/account-upgrade'
|
||||
import { memo } from 'react'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
import OLModal, {
|
||||
|
||||
@@ -47,7 +47,7 @@ import { setVisual } from '../extensions/visual/visual'
|
||||
import { useFileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
|
||||
import { useUserSettingsContext } from '@/shared/context/user-settings-context'
|
||||
import { setDocName } from '@/features/source-editor/extensions/doc-name'
|
||||
import isValidTexFile from '@/main/is-valid-tex-file'
|
||||
import { isValidTeXFile } from '@/main/is-valid-tex-file'
|
||||
import { captureException } from '@/infrastructure/error-reporter'
|
||||
import grammarlyExtensionPresent from '@/shared/utils/grammarly'
|
||||
import { DocumentContainer } from '@/features/ide-react/editor/document-container'
|
||||
@@ -288,7 +288,7 @@ function useCodeMirrorScope(view: EditorView) {
|
||||
|
||||
const { previewByPath } = useFileTreePathContext()
|
||||
|
||||
const showVisual = visual && isValidTexFile(docName)
|
||||
const showVisual = visual && isValidTeXFile(docName)
|
||||
|
||||
const visualRef = useRef({
|
||||
previewByPath,
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
export default function displayNameForUser(user) {
|
||||
if (user == null) {
|
||||
return 'Anonymous'
|
||||
}
|
||||
if (user.id === getMeta('ol-user').id) {
|
||||
return 'you'
|
||||
}
|
||||
if (user.name != null) {
|
||||
return user.name
|
||||
}
|
||||
let name = [user.first_name, user.last_name]
|
||||
.filter(n => n != null)
|
||||
.join(' ')
|
||||
.trim()
|
||||
if (name === '') {
|
||||
name = user.email.split('@')[0]
|
||||
}
|
||||
if (name == null || name === '') {
|
||||
return '?'
|
||||
}
|
||||
return name
|
||||
}
|
||||
+33
-11
@@ -1,25 +1,40 @@
|
||||
import sessionStorage from '../infrastructure/session-storage'
|
||||
import sessionStorage from './session-storage'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
type Segmentation = Record<
|
||||
string,
|
||||
string | number | boolean | undefined | unknown | any // TODO: RecurlyError
|
||||
>
|
||||
|
||||
const CACHE_KEY = 'mbEvents'
|
||||
|
||||
function alreadySent(key) {
|
||||
function alreadySent(key: string) {
|
||||
const eventCache = sessionStorage.getItem(CACHE_KEY) || {}
|
||||
return !!eventCache[key]
|
||||
}
|
||||
function markAsSent(key) {
|
||||
function markAsSent(key: string) {
|
||||
const eventCache = sessionStorage.getItem(CACHE_KEY) || {}
|
||||
eventCache[key] = true
|
||||
sessionStorage.setItem(CACHE_KEY, eventCache)
|
||||
}
|
||||
|
||||
export function send(category, action, label, value) {
|
||||
export function send(
|
||||
category: string,
|
||||
action: string,
|
||||
label?: string,
|
||||
value?: string
|
||||
) {
|
||||
if (typeof window.ga === 'function') {
|
||||
window.ga('send', 'event', category, action, label, value)
|
||||
}
|
||||
}
|
||||
|
||||
export function sendOnce(category, action, label, value) {
|
||||
export function sendOnce(
|
||||
category: string,
|
||||
action: string,
|
||||
label: string,
|
||||
value: string
|
||||
) {
|
||||
if (alreadySent(category)) return
|
||||
if (typeof window.ga !== 'function') return
|
||||
|
||||
@@ -27,7 +42,7 @@ export function sendOnce(category, action, label, value) {
|
||||
markAsSent(category)
|
||||
}
|
||||
|
||||
export function sendMB(key, segmentation = {}) {
|
||||
export function sendMB(key: string, segmentation: Segmentation = {}) {
|
||||
if (!segmentation.page) {
|
||||
segmentation.page = window.location.pathname
|
||||
}
|
||||
@@ -40,21 +55,28 @@ export function sendMB(key, segmentation = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
export function sendMBOnce(key, segmentation = {}) {
|
||||
export function sendMBOnce(key: string, segmentation: Segmentation = {}) {
|
||||
if (alreadySent(key)) return
|
||||
sendMB(key, segmentation)
|
||||
markAsSent(key)
|
||||
}
|
||||
|
||||
export function sendMBSampled(key, body = {}, rate = 0.01) {
|
||||
export function sendMBSampled(
|
||||
key: string,
|
||||
segmentation: Segmentation = {},
|
||||
rate = 0.01
|
||||
) {
|
||||
if (Math.random() < rate) {
|
||||
sendMB(key, body)
|
||||
sendMB(key, segmentation)
|
||||
}
|
||||
}
|
||||
|
||||
const sentOncePerPageLoad = new Set()
|
||||
|
||||
export function sendMBOncePerPageLoad(key, segmentation = {}) {
|
||||
export function sendMBOncePerPageLoad(
|
||||
key: string,
|
||||
segmentation: Segmentation = {}
|
||||
) {
|
||||
if (sentOncePerPageLoad.has(key)) return
|
||||
sendMB(key, segmentation)
|
||||
sentOncePerPageLoad.add(key)
|
||||
@@ -66,7 +88,7 @@ export function sendMBOncePerPageLoad(key, segmentation = {}) {
|
||||
// @screen-sm: 768px;
|
||||
export const isSmallDevice = window.screen.width < 768
|
||||
|
||||
function sendBeacon(key, data) {
|
||||
function sendBeacon(key: string, data: Segmentation) {
|
||||
if (!navigator || !navigator.sendBeacon) return
|
||||
if (!getMeta('ol-ExposedSettings').isOverleaf) return
|
||||
|
||||
+13
-8
@@ -12,7 +12,11 @@ import { debugConsole } from '@/utils/debugging'
|
||||
* @param {string?} key Key passed to the localStorage function (if any)
|
||||
* @param {any?} value Value passed to the localStorage function (if any)
|
||||
*/
|
||||
const callSafe = function (fn, key, value) {
|
||||
const callSafe = function (
|
||||
fn: (...args: any) => any,
|
||||
key?: string,
|
||||
value?: any
|
||||
) {
|
||||
try {
|
||||
return fn(key, value)
|
||||
} catch (e) {
|
||||
@@ -21,11 +25,12 @@ const callSafe = function (fn, key, value) {
|
||||
}
|
||||
}
|
||||
|
||||
const getItem = function (key) {
|
||||
return JSON.parse(localStorage.getItem(key))
|
||||
const getItem = function (key: string) {
|
||||
const value = localStorage.getItem(key)
|
||||
return value === null ? null : JSON.parse(value)
|
||||
}
|
||||
|
||||
const setItem = function (key, value) {
|
||||
const setItem = function (key: string, value: any) {
|
||||
localStorage.setItem(key, JSON.stringify(value))
|
||||
}
|
||||
|
||||
@@ -33,15 +38,15 @@ const clear = function () {
|
||||
localStorage.clear()
|
||||
}
|
||||
|
||||
const removeItem = function (key) {
|
||||
const removeItem = function (key: string) {
|
||||
return localStorage.removeItem(key)
|
||||
}
|
||||
|
||||
const customLocalStorage = {
|
||||
getItem: key => callSafe(getItem, key),
|
||||
setItem: (key, value) => callSafe(setItem, key, value),
|
||||
getItem: (key: string) => callSafe(getItem, key),
|
||||
setItem: (key: string, value: any) => callSafe(setItem, key, value),
|
||||
clear: () => callSafe(clear),
|
||||
removeItem: key => callSafe(removeItem, key),
|
||||
removeItem: (key: string) => callSafe(removeItem, key),
|
||||
}
|
||||
|
||||
export default customLocalStorage
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* run `fn` in serie for all values, and resolve with an array of the results
|
||||
* inspired by https://stackoverflow.com/a/50506360/1314820
|
||||
* @template T the input array's item type
|
||||
* @template V the `fn` function's return type
|
||||
* @param {T[]} values
|
||||
* @param {(item: T) => Promise<V>} fn
|
||||
* @returns {V[]}
|
||||
*/
|
||||
export function mapSeries(values, fn) {
|
||||
return values.reduce(async (promiseChain, value) => {
|
||||
const chainResults = await promiseChain
|
||||
const currentResult = await fn(value)
|
||||
return [...chainResults, currentResult]
|
||||
}, Promise.resolve([]))
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* run `fn` in series for all values, and resolve with an array of the results
|
||||
*/
|
||||
export const mapSeries = async <T = any, V = any>(
|
||||
values: T[],
|
||||
fn: (item: T) => Promise<V>
|
||||
) => {
|
||||
const output: V[] = []
|
||||
for (const value of values) {
|
||||
output.push(await fn(value))
|
||||
}
|
||||
return output
|
||||
}
|
||||
+13
-8
@@ -12,7 +12,11 @@ import { debugConsole } from '@/utils/debugging'
|
||||
* @param {string?} key Key passed to the sessionStorage function (if any)
|
||||
* @param {any?} value Value passed to the sessionStorage function (if any)
|
||||
*/
|
||||
const callSafe = function (fn, key, value) {
|
||||
const callSafe = function (
|
||||
fn: (...args: any) => any,
|
||||
key?: string,
|
||||
value?: any
|
||||
) {
|
||||
try {
|
||||
return fn(key, value)
|
||||
} catch (e) {
|
||||
@@ -21,11 +25,12 @@ const callSafe = function (fn, key, value) {
|
||||
}
|
||||
}
|
||||
|
||||
const getItem = function (key) {
|
||||
return JSON.parse(sessionStorage.getItem(key))
|
||||
const getItem = function (key: string) {
|
||||
const value = sessionStorage.getItem(key)
|
||||
return value === null ? null : JSON.parse(value)
|
||||
}
|
||||
|
||||
const setItem = function (key, value) {
|
||||
const setItem = function (key: string, value: any) {
|
||||
sessionStorage.setItem(key, JSON.stringify(value))
|
||||
}
|
||||
|
||||
@@ -33,15 +38,15 @@ const clear = function () {
|
||||
sessionStorage.clear()
|
||||
}
|
||||
|
||||
const removeItem = function (key) {
|
||||
const removeItem = function (key: string) {
|
||||
return sessionStorage.removeItem(key)
|
||||
}
|
||||
|
||||
const customSessionStorage = {
|
||||
getItem: key => callSafe(getItem, key),
|
||||
setItem: (key, value) => callSafe(setItem, key, value),
|
||||
getItem: (key: string) => callSafe(getItem, key),
|
||||
setItem: (key: string, value: any) => callSafe(setItem, key, value),
|
||||
clear: () => callSafe(clear),
|
||||
removeItem: key => callSafe(removeItem, key),
|
||||
removeItem: (key: string) => callSafe(removeItem, key),
|
||||
}
|
||||
|
||||
export default customSessionStorage
|
||||
@@ -1,49 +0,0 @@
|
||||
import * as eventTracking from '../infrastructure/event-tracking'
|
||||
|
||||
function startFreeTrial(source, version, $scope, variant) {
|
||||
const eventSegmentation = { 'paywall-type': source }
|
||||
if (variant) {
|
||||
eventSegmentation.variant = variant
|
||||
}
|
||||
|
||||
eventTracking.send('subscription-funnel', 'upgraded-free-trial', source)
|
||||
eventTracking.sendMB('paywall-click', eventSegmentation)
|
||||
|
||||
const searchParams = new URLSearchParams({
|
||||
itm_campaign: source,
|
||||
})
|
||||
|
||||
if (version) {
|
||||
searchParams.set('itm_content', version)
|
||||
}
|
||||
|
||||
if ($scope) {
|
||||
$scope.startedFreeTrial = true
|
||||
}
|
||||
|
||||
window.open(`/user/subscription/choose-your-plan?${searchParams.toString()}`)
|
||||
}
|
||||
|
||||
function upgradePlan(source, $scope) {
|
||||
const w = window.open()
|
||||
const go = function () {
|
||||
if (typeof ga === 'function') {
|
||||
ga('send', 'event', 'subscription-funnel', 'upgraded-plan', source)
|
||||
}
|
||||
const url = '/user/subscription'
|
||||
|
||||
if ($scope) {
|
||||
$scope.startedFreeTrial = true
|
||||
}
|
||||
|
||||
w.location = url
|
||||
}
|
||||
|
||||
go()
|
||||
}
|
||||
|
||||
function paywallPrompt(source) {
|
||||
eventTracking.sendMB('paywall-prompt', { 'paywall-type': source })
|
||||
}
|
||||
|
||||
export { startFreeTrial, upgradePlan, paywallPrompt }
|
||||
@@ -0,0 +1,29 @@
|
||||
import * as eventTracking from '../infrastructure/event-tracking'
|
||||
|
||||
export function startFreeTrial(source: string, variant?: string) {
|
||||
const eventSegmentation: Record<string, string> = { 'paywall-type': source }
|
||||
if (variant) {
|
||||
eventSegmentation.variant = variant
|
||||
}
|
||||
|
||||
eventTracking.send('subscription-funnel', 'upgraded-free-trial', source)
|
||||
eventTracking.sendMB('paywall-click', eventSegmentation)
|
||||
|
||||
const searchParams = new URLSearchParams({
|
||||
itm_campaign: source,
|
||||
})
|
||||
|
||||
window.open(`/user/subscription/choose-your-plan?${searchParams.toString()}`)
|
||||
}
|
||||
|
||||
export function upgradePlan(source: string) {
|
||||
const openedWindow = window.open()
|
||||
|
||||
if (typeof window.ga === 'function') {
|
||||
window.ga('send', 'event', 'subscription-funnel', 'upgraded-plan', source)
|
||||
}
|
||||
|
||||
if (openedWindow) {
|
||||
openedWindow.location = '/user/subscription'
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -1,6 +1,6 @@
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
function isValidTeXFile(filename) {
|
||||
export const isValidTeXFile = (filename: string) => {
|
||||
const validTeXFileRegExp = new RegExp(
|
||||
`\\.(${getMeta('ol-ExposedSettings').validRootDocExtensions.join('|')})$`,
|
||||
'i'
|
||||
@@ -8,5 +8,3 @@ function isValidTeXFile(filename) {
|
||||
|
||||
return validTeXFileRegExp.test(filename)
|
||||
}
|
||||
|
||||
export default isValidTeXFile
|
||||
@@ -1,5 +1,5 @@
|
||||
import './../utils/meta'
|
||||
import './../utils/webpack-public-path'
|
||||
import '../utils/webpack-public-path'
|
||||
import './../infrastructure/error-reporter'
|
||||
import '@/i18n'
|
||||
import '../features/event-tracking'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import './../utils/meta'
|
||||
import './../utils/webpack-public-path'
|
||||
import '../utils/webpack-public-path'
|
||||
import './../infrastructure/error-reporter'
|
||||
import '@/i18n'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import './../utils/meta'
|
||||
import './../utils/webpack-public-path'
|
||||
import '../utils/webpack-public-path'
|
||||
import './../infrastructure/error-reporter'
|
||||
import '@/i18n'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import '../../marketing'
|
||||
import './../../utils/meta'
|
||||
import './../../utils/webpack-public-path'
|
||||
import '../../utils/webpack-public-path'
|
||||
import './../../infrastructure/error-reporter'
|
||||
import '@/i18n'
|
||||
import '../../features/settings/components/root'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import './../../../utils/meta'
|
||||
import './../../../utils/webpack-public-path'
|
||||
import '../../../utils/webpack-public-path'
|
||||
import './../../../infrastructure/error-reporter'
|
||||
import '@/i18n'
|
||||
import '../../../features/event-tracking'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MouseEventHandler, useCallback, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { startFreeTrial } from '../../main/account-upgrade'
|
||||
import { startFreeTrial } from '@/main/account-upgrade'
|
||||
import * as eventTracking from '../../infrastructure/event-tracking'
|
||||
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function StartFreeTrialButton({
|
||||
handleClick(event)
|
||||
}
|
||||
|
||||
startFreeTrial(source, null, null, variant)
|
||||
startFreeTrial(source, variant)
|
||||
},
|
||||
[handleClick, source, variant]
|
||||
)
|
||||
|
||||
+17
-7
@@ -7,11 +7,19 @@
|
||||
// Remove a listener for the foo event with the bar namespace: .off 'foo.bar'
|
||||
|
||||
export default class EventEmitter {
|
||||
events: Record<
|
||||
string,
|
||||
{
|
||||
callback: (...args: any[]) => void
|
||||
namespace: string
|
||||
}[]
|
||||
>
|
||||
|
||||
constructor() {
|
||||
this.events = {}
|
||||
}
|
||||
|
||||
on(event, callback) {
|
||||
on(event: string, callback: (...args: any[]) => void) {
|
||||
if (!this.events) {
|
||||
this.events = {}
|
||||
}
|
||||
@@ -26,7 +34,7 @@ export default class EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
off(event, cb) {
|
||||
off(event?: string, callback?: (...args: any[]) => void) {
|
||||
if (!this.events) {
|
||||
this.events = {}
|
||||
}
|
||||
@@ -36,8 +44,10 @@ export default class EventEmitter {
|
||||
if (!this.events[event]) {
|
||||
this.events[event] = []
|
||||
}
|
||||
if (cb) {
|
||||
this.events[event] = this.events[event].filter(e => e.callback !== cb)
|
||||
if (callback) {
|
||||
this.events[event] = this.events[event].filter(
|
||||
e => e.callback !== callback
|
||||
)
|
||||
} else if (!namespace) {
|
||||
// Clear all listeners for event
|
||||
delete this.events[event]
|
||||
@@ -53,7 +63,7 @@ export default class EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
trigger(event, ...args) {
|
||||
trigger(event: string, ...args: any[]) {
|
||||
if (!this.events) {
|
||||
this.events = {}
|
||||
}
|
||||
@@ -62,7 +72,7 @@ export default class EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
emit(...args) {
|
||||
this.trigger(...args)
|
||||
emit(event: string, ...args: any[]) {
|
||||
this.trigger(event, ...args)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import moment from 'moment'
|
||||
|
||||
export function formatDate(date, format) {
|
||||
export function formatDate(date: moment.MomentInput, format?: string) {
|
||||
if (!date) return 'N/A'
|
||||
if (format == null) {
|
||||
format = 'Do MMM YYYY, h:mm a'
|
||||
@@ -8,6 +8,6 @@ export function formatDate(date, format) {
|
||||
return moment(date).format(format)
|
||||
}
|
||||
|
||||
export function fromNowDate(date) {
|
||||
export function fromNowDate(date: moment.MomentInput | string) {
|
||||
return moment(date).fromNow()
|
||||
}
|
||||
+2
-2
@@ -68,6 +68,6 @@ const NETWORK_ERRORS = [
|
||||
'요청한 시간이 초과되었습니다.',
|
||||
]
|
||||
|
||||
export function isNetworkError(err) {
|
||||
return NETWORK_ERRORS.includes(err?.message)
|
||||
export function isNetworkError(err?: Error) {
|
||||
return err && NETWORK_ERRORS.includes(err.message)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import EmailsSection from '../../js/features/settings/components/emails-section'
|
||||
import { UserEmailsProvider } from '../../js/features/settings/context/user-email-context'
|
||||
import { LeaversSurveyAlert } from '../../js/features/settings/components/leavers-survey-alert'
|
||||
import localStorage from '../../js/infrastructure/local-storage'
|
||||
import localStorage from '@/infrastructure/local-storage'
|
||||
import { bsVersionDecorator } from '../../../.storybook/utils/with-bootstrap-switcher'
|
||||
|
||||
export const SurveyAlert = () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import '../../helpers/bootstrap-3'
|
||||
import localStorage from '../../../../frontend/js/infrastructure/local-storage'
|
||||
import localStorage from '@/infrastructure/local-storage'
|
||||
import PdfPreview from '../../../../frontend/js/features/pdf-preview/components/pdf-preview'
|
||||
import { EditorProviders } from '../../helpers/editor-providers'
|
||||
import { mockScope } from './scope'
|
||||
|
||||
+7
-8
@@ -1,17 +1,13 @@
|
||||
import { screen, within } from '@testing-library/dom'
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import SettingsDocument from '../../../../../../frontend/js/features/editor-left-menu/components/settings/settings-document'
|
||||
import * as isValidTeXFileModule from '../../../../../../frontend/js/main/is-valid-tex-file'
|
||||
import { Folder } from '../../../../../../types/folder'
|
||||
import { EditorLeftMenuProvider } from '@/features/editor-left-menu/components/editor-left-menu-context'
|
||||
import { render } from '@testing-library/react'
|
||||
import { EditorProviders } from '../../../../helpers/editor-providers'
|
||||
|
||||
describe('<SettingsDocument />', function () {
|
||||
let isValidTeXFileStub: sinon.SinonStub
|
||||
|
||||
const rootFolder: Folder = {
|
||||
_id: 'root-folder-id',
|
||||
name: 'rootFolder',
|
||||
@@ -25,15 +21,18 @@ describe('<SettingsDocument />', function () {
|
||||
folders: [],
|
||||
}
|
||||
|
||||
let originalSettings: typeof window.metaAttributesCache
|
||||
|
||||
beforeEach(function () {
|
||||
isValidTeXFileStub = sinon
|
||||
.stub(isValidTeXFileModule, 'default')
|
||||
.returns(true)
|
||||
originalSettings = window.metaAttributesCache.get('ol-ExposedSettings')
|
||||
window.metaAttributesCache.set('ol-ExposedSettings', {
|
||||
validRootDocExtensions: ['tex'],
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
fetchMock.reset()
|
||||
isValidTeXFileStub.restore()
|
||||
window.metaAttributesCache.set('ol-ExposedSettings', originalSettings)
|
||||
})
|
||||
|
||||
it('shows correct menu', async function () {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { expect } from 'chai'
|
||||
import { fireEvent, screen } from '@testing-library/react'
|
||||
import LayoutDropdownButton from '../../../../../frontend/js/features/editor-navigation-toolbar/components/layout-dropdown-button'
|
||||
import { renderWithEditorContext } from '../../../helpers/render-with-context'
|
||||
import * as eventTracking from '../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
|
||||
describe('<LayoutDropdownButton />', function () {
|
||||
let openStub
|
||||
|
||||
+3
-14
@@ -1,19 +1,8 @@
|
||||
/* eslint-disable
|
||||
no-return-assign,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import { expect } from 'chai'
|
||||
import displayNameForUser from '@/features/history/utils/display-name-for-user'
|
||||
|
||||
import displayNameForUser from '../../../../../frontend/js/ide/history/util/displayNameForUser'
|
||||
|
||||
export default describe('displayNameForUser', function () {
|
||||
const currentUsersId = 42
|
||||
describe('displayNameForUser', function () {
|
||||
const currentUsersId = 'user-a'
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set('ol-user', { id: currentUsersId })
|
||||
})
|
||||
+1
-1
@@ -7,7 +7,7 @@ import {
|
||||
IndividualPlanSubscription,
|
||||
} from '../../../../../types/project/dashboard/subscription'
|
||||
import { DeepReadonly } from '../../../../../types/utils'
|
||||
import * as eventTracking from '../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
import CurrentPlanWidget from '../../../../../frontend/js/features/project-list/components/current-plan-widget/current-plan-widget'
|
||||
|
||||
describe('<CurrentPlanWidget />', function () {
|
||||
|
||||
@@ -36,7 +36,7 @@ import { DeepPartial } from '../../../../../types/utils'
|
||||
import { Project } from '../../../../../types/project/dashboard/api'
|
||||
import GroupsAndEnterpriseBanner from '../../../../../frontend/js/features/project-list/components/notifications/groups-and-enterprise-banner'
|
||||
import GroupSsoSetupSuccess from '../../../../../frontend/js/features/project-list/components/notifications/groups/group-sso-setup-success'
|
||||
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
|
||||
import localStorage from '@/infrastructure/local-storage'
|
||||
import * as useLocationModule from '../../../../../frontend/js/shared/hooks/use-location'
|
||||
import {
|
||||
commonsSubscription,
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import fetchMock from 'fetch-mock'
|
||||
import sinon from 'sinon'
|
||||
import ProjectListRoot from '../../../../../frontend/js/features/project-list/components/project-list-root'
|
||||
import { renderWithProjectListContext } from '../helpers/render-with-context'
|
||||
import * as eventTracking from '../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
import {
|
||||
projectsData,
|
||||
owner,
|
||||
|
||||
@@ -2,7 +2,7 @@ import sinon from 'sinon'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import { expect } from 'chai'
|
||||
import SearchForm from '../../../../../frontend/js/features/project-list/components/search-form'
|
||||
import * as eventTracking from '../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import { Filter } from '../../../../../frontend/js/features/project-list/context/project-list-context'
|
||||
import { Tag } from '../../../../../app/src/Features/Tags/types'
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { projectsData } from '../../../../fixtures/projects-data'
|
||||
import * as useLocationModule from '../../../../../../../../frontend/js/shared/hooks/use-location'
|
||||
import { CompileAndDownloadProjectPDFButtonTooltip } from '../../../../../../../../frontend/js/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import * as eventTracking from '../../../../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
|
||||
describe('<CompileAndDownloadProjectPDFButton />', function () {
|
||||
let assignStub: sinon.SinonStub
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import {
|
||||
resetProjectListContextFetch,
|
||||
renderWithProjectListContext,
|
||||
} from '../../helpers/render-with-context'
|
||||
import * as eventTracking from '../../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
|
||||
describe('<ProjectsActionModal />', function () {
|
||||
const actionHandler = sinon.stub().resolves({})
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@ import sinon from 'sinon'
|
||||
import { fireEvent, screen, render } from '@testing-library/react'
|
||||
import { UserEmailsProvider } from '../../../../../frontend/js/features/settings/context/user-email-context'
|
||||
import { LeaversSurveyAlert } from '../../../../../frontend/js/features/settings/components/leavers-survey-alert'
|
||||
import * as eventTracking from '../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
import localStorage from '@/infrastructure/local-storage'
|
||||
import fetchMock from 'fetch-mock'
|
||||
|
||||
function renderWithProvider() {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import { screen, fireEvent, render, waitFor } from '@testing-library/react'
|
||||
import { IntegrationLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/integration-widget'
|
||||
import * as eventTracking from '../../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
|
||||
describe('<IntegrationLinkingWidgetTest/>', function () {
|
||||
const defaultProps = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import { screen, render, waitFor } from '@testing-library/react'
|
||||
import * as eventTracking from '../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
import SettingsPageRoot from '../../../../../frontend/js/features/settings/components/root'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
fakeUsersData,
|
||||
unconfirmedCommonsUserData,
|
||||
} from '../fixtures/test-user-email-data'
|
||||
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
|
||||
import localStorage from '@/infrastructure/local-storage'
|
||||
|
||||
const renderUserEmailsContext = () =>
|
||||
renderHook(() => useUserEmailsContext(), {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { expect } from 'chai'
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import * as eventTracking from '../../../../../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
|
||||
import {
|
||||
annualActiveSubscription,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import customLocalStorage from '../../../frontend/js/infrastructure/local-storage'
|
||||
import customLocalStorage from '@/infrastructure/local-storage'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
|
||||
describe('localStorage', function () {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai'
|
||||
import { screen, render } from '@testing-library/react'
|
||||
import Notification from '../../../../frontend/js/shared/components/notification'
|
||||
import * as eventTracking from '../../../../frontend/js/infrastructure/event-tracking'
|
||||
import * as eventTracking from '@/infrastructure/event-tracking'
|
||||
import sinon from 'sinon'
|
||||
|
||||
describe('<Notification />', function () {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect } from 'chai'
|
||||
import { useEffect } from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import usePersistedState from '../../../../frontend/js/shared/hooks/use-persisted-state'
|
||||
import localStorage from '../../../../frontend/js/infrastructure/local-storage'
|
||||
import localStorage from '@/infrastructure/local-storage'
|
||||
|
||||
describe('usePersistedState', function () {
|
||||
beforeEach(function () {
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
/* eslint-disable
|
||||
max-len,
|
||||
no-return-assign,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import EventEmitter from '../../../frontend/js/utils/EventEmitter'
|
||||
import EventEmitter from '@/utils/EventEmitter'
|
||||
|
||||
export default describe('EventEmitter', function () {
|
||||
describe('EventEmitter', function () {
|
||||
beforeEach(function () {
|
||||
return (this.eventEmitter = new EventEmitter())
|
||||
this.eventEmitter = new EventEmitter()
|
||||
})
|
||||
|
||||
it('calls listeners', function () {
|
||||
@@ -28,7 +17,7 @@ export default describe('EventEmitter', function () {
|
||||
this.eventEmitter.trigger('foo')
|
||||
|
||||
expect(cb1).to.have.been.called
|
||||
return expect(cb2).to.not.have.been.called
|
||||
expect(cb2).to.not.have.been.called
|
||||
})
|
||||
|
||||
it('calls multiple listeners', function () {
|
||||
@@ -40,7 +29,7 @@ export default describe('EventEmitter', function () {
|
||||
this.eventEmitter.trigger('foo')
|
||||
|
||||
expect(cb1).to.have.been.called
|
||||
return expect(cb2).to.have.been.called
|
||||
expect(cb2).to.have.been.called
|
||||
})
|
||||
|
||||
it('calls listeners with namespace', function () {
|
||||
@@ -52,7 +41,7 @@ export default describe('EventEmitter', function () {
|
||||
this.eventEmitter.trigger('foo')
|
||||
|
||||
expect(cb1).to.have.been.called
|
||||
return expect(cb2).to.have.been.called
|
||||
expect(cb2).to.have.been.called
|
||||
})
|
||||
|
||||
it('removes listeners', function () {
|
||||
@@ -62,7 +51,7 @@ export default describe('EventEmitter', function () {
|
||||
|
||||
this.eventEmitter.trigger('foo')
|
||||
|
||||
return expect(cb).to.not.have.been.called
|
||||
expect(cb).to.not.have.been.called
|
||||
})
|
||||
|
||||
it('removes namespaced listeners', function () {
|
||||
@@ -72,7 +61,7 @@ export default describe('EventEmitter', function () {
|
||||
|
||||
this.eventEmitter.trigger('foo')
|
||||
|
||||
return expect(cb).to.not.have.been.called
|
||||
expect(cb).to.not.have.been.called
|
||||
})
|
||||
|
||||
it('does not remove unnamespaced listeners if off called with namespace', function () {
|
||||
@@ -85,6 +74,6 @@ export default describe('EventEmitter', function () {
|
||||
this.eventEmitter.trigger('foo')
|
||||
|
||||
expect(cb1).to.have.been.called
|
||||
return expect(cb2).to.not.have.been.called
|
||||
expect(cb2).to.not.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
@@ -37,5 +37,7 @@ declare global {
|
||||
store: ScopeValueStore
|
||||
}
|
||||
}
|
||||
ga?: (...args: any) => void
|
||||
gtag?: (...args: any) => void
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user