Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
10 KiB
341 lines
10 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
* |
|
* Originally from: |
|
* https://github.com/evgeny-nadymov/telegram-react |
|
* Copyright (C) 2018 Evgeny Nadymov |
|
* https://github.com/evgeny-nadymov/telegram-react/blob/master/LICENSE |
|
*/ |
|
|
|
import GROUP_CALL_STATE from '../lib/calls/groupCallState'; |
|
import LineBlobDrawable from './lineBlobDrawable'; |
|
|
|
export class WeavingState { |
|
public shader: (ctx: CanvasRenderingContext2D, left: number, top: number, right: number, bottom: number) => void; |
|
|
|
constructor(public stateId: GROUP_CALL_STATE) { |
|
this.createGradient(stateId); |
|
} |
|
|
|
public createGradient(stateId: GROUP_CALL_STATE) { |
|
this.shader = (ctx, left, top, right, bottom) => { |
|
ctx.fillStyle = WeavingState.getGradientFromType(ctx, stateId, left, top, right, bottom); |
|
}; |
|
} |
|
|
|
// Android colors |
|
static getGradientFromType(ctx: CanvasRenderingContext2D, type: GROUP_CALL_STATE, x0: number, y0: number, x1: number, y1: number) { |
|
const gradient = ctx.createLinearGradient(x0, y0, x1, y1); |
|
if(type === GROUP_CALL_STATE.MUTED_BY_ADMIN) { |
|
gradient.addColorStop(0, '#F05459'); |
|
gradient.addColorStop(.4, '#766EE9'); |
|
gradient.addColorStop(1, '#57A4FE'); |
|
} else if(type === GROUP_CALL_STATE.UNMUTED) { |
|
gradient.addColorStop(0, '#52CE5D'); |
|
gradient.addColorStop(1, '#00B1C0'); |
|
} else if(type === GROUP_CALL_STATE.MUTED) { |
|
gradient.addColorStop(0, '#0976E3'); |
|
gradient.addColorStop(1, '#2BCEFF'); |
|
} else if(type === GROUP_CALL_STATE.CONNECTING) { |
|
gradient.addColorStop(0, '#8599aa'); |
|
gradient.addColorStop(1, '#8599aa'); |
|
} |
|
|
|
return gradient; |
|
} |
|
|
|
update(height: number, width: number, dt: number, amplitude: number) { |
|
// TODO: move gradient here |
|
} |
|
} |
|
|
|
export default class TopbarWeave { |
|
private focused: boolean; |
|
private resizing: boolean; |
|
private lastUpdateTime: number; |
|
private amplitude: number; |
|
private amplitude2: number; |
|
|
|
private states: Map<GROUP_CALL_STATE, WeavingState>; |
|
private previousState: WeavingState; |
|
private currentState: WeavingState; |
|
private progressToState: number; |
|
|
|
private scale: number; |
|
private left: number; |
|
private top: number; |
|
private right: number; |
|
private bottom: number; |
|
|
|
private mounted: boolean; |
|
private media: MediaQueryList; |
|
|
|
private container: HTMLDivElement; |
|
private canvas: HTMLCanvasElement; |
|
|
|
private resizeHandler: number; |
|
private raf: number; |
|
|
|
private lbd: LineBlobDrawable; |
|
private lbd1: LineBlobDrawable; |
|
private lbd2: LineBlobDrawable; |
|
|
|
private animateToAmplitude: number; |
|
private animateAmplitudeDiff: number; |
|
private animateAmplitudeDiff2: number; |
|
|
|
constructor() { |
|
this.focused = true; |
|
this.resizing = false; |
|
this.lastUpdateTime = Date.now(); |
|
this.amplitude = 0.0; |
|
this.amplitude2 = 0.0; |
|
|
|
this.states = new Map([ |
|
[GROUP_CALL_STATE.UNMUTED, new WeavingState(GROUP_CALL_STATE.UNMUTED)], |
|
[GROUP_CALL_STATE.MUTED, new WeavingState(GROUP_CALL_STATE.MUTED)], |
|
[GROUP_CALL_STATE.MUTED_BY_ADMIN, new WeavingState(GROUP_CALL_STATE.MUTED_BY_ADMIN)], |
|
[GROUP_CALL_STATE.CONNECTING, new WeavingState(GROUP_CALL_STATE.CONNECTING)], |
|
]); |
|
this.previousState = null; |
|
this.currentState = this.states.get(GROUP_CALL_STATE.CONNECTING); |
|
this.progressToState = 1.0; |
|
} |
|
|
|
public componentDidMount() { |
|
if(this.mounted) { |
|
return; |
|
} |
|
|
|
this.mounted = true; |
|
// window.addEventListener('blur', this.handleBlur); |
|
// window.addEventListener('focus', this.handleFocus); |
|
window.addEventListener('resize', this.handleResize); |
|
this.media = window.matchMedia('screen and (min-resolution: 2dppx)'); |
|
this.media.addEventListener('change', this.handleDevicePixelRatioChanged); |
|
|
|
this.setSize(); |
|
this.forceUpdate(); |
|
|
|
this.lbd = new LineBlobDrawable(3); |
|
this.lbd1 = new LineBlobDrawable(7); |
|
this.lbd2 = new LineBlobDrawable(8); |
|
this.setAmplitude(this.amplitude); |
|
|
|
this.draw(); |
|
} |
|
|
|
public componentWillUnmount() { |
|
this.mounted = false; |
|
// window.removeEventListener('blur', this.handleBlur); |
|
// window.removeEventListener('focus', this.handleFocus); |
|
window.removeEventListener('resize', this.handleResize); |
|
this.media.addEventListener('change', this.handleDevicePixelRatioChanged); |
|
|
|
const {canvas} = this; |
|
const ctx = canvas.getContext('2d'); |
|
ctx.clearRect(0, 0, canvas.width, canvas.height); |
|
} |
|
|
|
private setSize() { |
|
this.scale = window.devicePixelRatio; |
|
this.top = 20 * this.scale; |
|
this.right = (this.mounted ? this.container.offsetWidth : 1261) * this.scale; |
|
this.bottom = (this.mounted ? this.container.offsetHeight : 68) * this.scale; |
|
this.left = 0 * this.scale; |
|
this.setCanvasSize(); |
|
} |
|
|
|
private setCanvasSize() { |
|
this.canvas.width = this.right; |
|
this.canvas.height = this.bottom; |
|
} |
|
|
|
private handleDevicePixelRatioChanged = (e: Event) => { |
|
this.setSize(); |
|
this.forceUpdate(); |
|
} |
|
|
|
private handleResize = () => { |
|
if(this.resizeHandler) { |
|
clearTimeout(this.resizeHandler); |
|
this.resizeHandler = null; |
|
} |
|
|
|
this.resizing = true; |
|
this.resizeCanvas(); |
|
this.resizeHandler = window.setTimeout(() => { |
|
this.resizing = false; |
|
this.invokeDraw(); |
|
}, 250); |
|
} |
|
|
|
private resizeCanvas() { |
|
this.scale = window.devicePixelRatio; |
|
this.right = this.container.offsetWidth * this.scale; |
|
|
|
this.forceUpdate(); |
|
this.invokeDraw(); |
|
} |
|
|
|
public handleFocus = () => { |
|
this.focused = true; |
|
this.invokeDraw(); |
|
} |
|
|
|
public handleBlur = () => { |
|
this.focused = false; |
|
} |
|
|
|
private invokeDraw = () => { |
|
if(this.raf) return; |
|
|
|
this.draw(); |
|
} |
|
|
|
private draw = (force = false) => { |
|
this.raf = null; |
|
if(!this.mounted) { |
|
return; |
|
} |
|
const {lbd, lbd1, lbd2, scale, left, top, right, bottom, currentState, previousState, focused, resizing, canvas} = this; |
|
if(!focused && !resizing && this.progressToState >= 1.0) { |
|
return; |
|
} |
|
|
|
// console.log('[top] draw', [focused, resizing, this.mounted]); |
|
|
|
const newTime = Date.now(); |
|
let dt = (newTime - this.lastUpdateTime); |
|
if(dt > 20) { |
|
dt = 17; |
|
} |
|
|
|
// console.log('draw start', this.amplitude, this.animateToAmplitude); |
|
if(this.animateToAmplitude !== this.amplitude) { |
|
this.amplitude += this.animateAmplitudeDiff * dt; |
|
if(this.animateAmplitudeDiff > 0) { |
|
if(this.amplitude > this.animateToAmplitude) { |
|
this.amplitude = this.animateToAmplitude; |
|
} |
|
} else { |
|
if(this.amplitude < this.animateToAmplitude) { |
|
this.amplitude = this.animateToAmplitude; |
|
} |
|
} |
|
} |
|
|
|
if(this.animateToAmplitude !== this.amplitude2) { |
|
this.amplitude2 += this.animateAmplitudeDiff2 * dt; |
|
if(this.animateAmplitudeDiff2 > 0) { |
|
if(this.amplitude2 > this.animateToAmplitude) { |
|
this.amplitude2 = this.animateToAmplitude; |
|
} |
|
} else { |
|
if(this.amplitude2 < this.animateToAmplitude) { |
|
this.amplitude2 = this.animateToAmplitude; |
|
} |
|
} |
|
} |
|
|
|
if(previousState) { |
|
this.progressToState += dt / 250; |
|
if(this.progressToState > 1) { |
|
this.progressToState = 1; |
|
this.previousState = null; |
|
} |
|
} |
|
|
|
const {amplitude, amplitude2, progressToState} = this; |
|
|
|
const top1 = 6 * amplitude2 * scale; |
|
const top2 = 6 * amplitude2 * scale; |
|
|
|
const ctx = canvas.getContext('2d'); |
|
ctx.clearRect(0, 0, canvas.width, canvas.height); |
|
|
|
lbd.minRadius = 0; |
|
lbd.maxRadius = (2 + 2 * amplitude) * scale; |
|
lbd1.minRadius = 0; |
|
lbd1.maxRadius = (3 + 9 * amplitude) * scale; |
|
lbd2.minRadius = 0; |
|
lbd2.maxRadius = (3 + 9 * amplitude) * scale; |
|
|
|
lbd.update(amplitude, 0.3); |
|
lbd1.update(amplitude, 0.7); |
|
lbd2.update(amplitude, 0.7); |
|
|
|
for(let i = 0; i < 2; i++) { |
|
if(i === 0 && !previousState) { |
|
continue; |
|
} |
|
|
|
let alpha = 1; |
|
let state: WeavingState = null; |
|
if(i === 0) { |
|
alpha = 1 - progressToState; |
|
state = previousState; |
|
// previousState.setToPaint(paint); |
|
} else { |
|
alpha = previousState ? progressToState : 1; |
|
currentState.update(bottom - top, right - left, dt, amplitude); |
|
state = currentState; |
|
// currentState.setToPaint(paint); |
|
} |
|
|
|
const paint1 = (ctx: CanvasRenderingContext2D) => { |
|
ctx.globalAlpha = 0.3 * alpha; |
|
state.shader(ctx, left, top, right, bottom); |
|
}; |
|
const paint = (ctx: CanvasRenderingContext2D) => { |
|
ctx.globalAlpha = i === 0 ? 1 : alpha; |
|
state.shader(ctx, left, top, right, bottom); |
|
}; |
|
|
|
lbd1.draw(left, top - top1, right, bottom, canvas, paint1, top, 1.0); |
|
lbd2.draw(left, top - top2, right, bottom, canvas, paint1, top, 1.0); |
|
lbd.draw(left, top, right, bottom, canvas, paint, top, 1.0); |
|
} |
|
|
|
if(!force) { |
|
this.raf = requestAnimationFrame(() => this.draw()); |
|
} |
|
}; |
|
|
|
public setCurrentState = (stateId: GROUP_CALL_STATE, animated: boolean) => { |
|
const {currentState, states} = this; |
|
|
|
if(currentState?.stateId === stateId) { |
|
return; |
|
} |
|
|
|
this.previousState = animated ? currentState : null; |
|
this.currentState = states.get(stateId); |
|
this.progressToState = this.previousState ? 0.0 : 1.0; |
|
}; |
|
|
|
public setAmplitude(value: number) { |
|
const {amplitude} = this; |
|
this.animateToAmplitude = value; |
|
this.animateAmplitudeDiff = (value - amplitude) / 250; |
|
this.animateAmplitudeDiff2 = (value - amplitude) / 120; |
|
} |
|
|
|
private forceUpdate() { |
|
this.setCanvasSize(); |
|
} |
|
|
|
public render(className: string) { |
|
const container = this.container = document.createElement('div'); |
|
container.classList.add(className); |
|
|
|
const canvas = this.canvas = document.createElement('canvas'); |
|
canvas.classList.add(className + '-canvas'); |
|
|
|
container.append(canvas); |
|
|
|
return container; |
|
} |
|
}
|
|
|