├── .gitignore ├── README.md ├── components ├── Result.js ├── Speedometer.js └── TestImg.js ├── next.config.js ├── package.json ├── pages ├── _app.js ├── _document.js └── index.js ├── postcss.config.js ├── public └── favicon.ico ├── styles └── globals.css ├── tailwind.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Замедлен ли у меня твиттер 2 | 3 | Страничка, замеряющая скорость загрузки картинки с замедленного в РФ `abs.twimg.com`. 4 | 5 | Проверить самостоятельно: [https://lynx.pink/is-my-twitter-slow-or-what](https://lynx.pink/is-my-twitter-slow-or-what) 6 | 7 | Все технические обсуждения по данной ситуации: [https://ntc.party/t/twitter/907/](https://ntc.party/t/twitter/907/) 8 | 9 | ## Разработка 10 | 11 | First, run the development server: 12 | 13 | ```bash 14 | npm run dev 15 | # or 16 | yarn dev 17 | ``` 18 | 19 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 20 | 21 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 22 | 23 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 24 | 25 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 26 | 27 | ## Про Next.js 28 | 29 | To learn more about Next.js, take a look at the following resources: 30 | 31 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 32 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 33 | 34 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 35 | -------------------------------------------------------------------------------- /components/Result.js: -------------------------------------------------------------------------------- 1 | const SIZE = 3000 2 | 3 | const Result = ({ test, control, controlTaco }) => { 4 | const testTime = test / 1000 5 | const controlTime = control / 1000 6 | const controlTacoTime = controlTaco / 1000 7 | 8 | const testSpeed = SIZE / test * 1000 9 | const controlSpeed = SIZE / control * 1000 10 | const controlTacoSpeed = SIZE / controlTaco * 1000 11 | 12 | let result = null 13 | let ad = null 14 | 15 | if (testSpeed < 600 && controlSpeed > 600) { 16 | result = замедлен 17 | 18 | // Один раз живём 19 | ad = ( 20 |
21 |

22 | Если хотите вернуть как раньше — установите Red Shield VPN всего за $39 на год с промокодом TWITTER. 23 |
24 | - на правах рекламы 25 |

26 |
27 | ) 28 | } else if (controlSpeed < 600) { 29 | result = возможно замедлен (скорость контроля < 600 kpbs) 30 | } else { 31 | result = не замедлен 32 | } 33 | 34 | const logParams = new URLSearchParams({ test: testSpeed.toFixed(2), control: controlSpeed.toFixed(2), controlTaco: controlTacoSpeed.toFixed(2), v: 4 }) 35 | 36 | const logUrl = `https://imtsow-new.lynx.pink/log.png?${logParams.toString()}` 37 | const darkkLogUrl = `https://darkk.net.ru/garbage/is-my-twitter-slow-or-what/log?${logParams.toString()}` 38 | 39 | return ( 40 | <> 41 |

≈ {testSpeed.toFixed(2)} kbps, {Math.round(testTime)} секунд(-а)

42 |

Скорее всего, твиттер у вас: {result}

43 | {ad} 44 |

Контроль 1 (эта же картинка с другого сервера): {controlSpeed.toFixed(2)} kbps, {Math.round(controlTime)} секунд(-а)

45 |

Контроль 2 (эта же картинка с другого сервера, в адресе которого есть t.co): {controlTacoSpeed.toFixed(2)} kbps, {Math.round(controlTacoTime)} секунд(-а)

46 | 47 | 48 | 49 | ) 50 | } 51 | 52 | export default Result 53 | -------------------------------------------------------------------------------- /components/Speedometer.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import Result from './Result'; 3 | import TestImg from './TestImg'; 4 | 5 | const Speedometer = () => { 6 | const [isChecking, setIsChecking] = useState(false) 7 | const [key, setKey] = useState(Math.random()) 8 | 9 | const [testResult, setTestResult] = useState(false) 10 | const [controlResult, setControlResult] = useState(false) 11 | const [controlTacoResult, setControlTacoResult] = useState(false) 12 | const done = testResult && controlResult && controlTacoResult 13 | const run = () => { 14 | setTestResult(null) 15 | setControlResult(null) 16 | setControlTacoResult(null) 17 | 18 | setIsChecking(true) 19 | } 20 | 21 | const onLoad = (type, result) => { 22 | switch (type) { 23 | case 'test': 24 | setTestResult(result) 25 | setKey(Math.random()) 26 | break 27 | case 'control': 28 | setControlResult(result) 29 | setKey(Math.random()) 30 | break 31 | case 'control-taco': 32 | setControlTacoResult(result) 33 | setKey(Math.random()) 34 | break 35 | } 36 | } 37 | 38 | useEffect(() => { 39 | if (done && isChecking) setIsChecking(false) 40 | }, [isChecking, done]) 41 | 42 | let content = ( 43 | <> 44 | Поехали! 45 | 46 | 47 | ) 48 | 49 | let img = null 50 | 51 | if (isChecking) { 52 | content = ( 53 | Проверяем... 54 | ) 55 | 56 | if (!testResult) { 57 | img = ( 58 |
59 | 65 |
66 | ) 67 | } else if (!controlResult) { 68 | img = ( 69 |
70 | 76 |
77 | ) 78 | } else if (!controlTacoResult) { 79 | img = ( 80 |
81 | 87 |
88 | ) 89 | } 90 | } 91 | 92 | let resultContent = null 93 | 94 | if (!isChecking && done) { 95 | resultContent = 96 | content = Проверить еще раз 97 | } 98 | 99 | return ( 100 | <> 101 | {resultContent} 102 | 109 | {img} 110 | 111 | ) 112 | } 113 | 114 | export default Speedometer 115 | -------------------------------------------------------------------------------- /components/TestImg.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | const TestImg = ({ type, src, onLoad }) => { 4 | const [nonce, setNonce] = useState(null) 5 | const [startedAt, setStartedAt] = useState(false) 6 | 7 | // Меняем nonce при рендере 8 | useEffect(() => { 9 | setNonce(Math.random()) 10 | setStartedAt(+new Date()) 11 | }, [src]); 12 | 13 | const onLoadCallback = () => onLoad(type, +new Date() - startedAt) 14 | 15 | if (nonce) { 16 | return 17 | } 18 | 19 | return null 20 | } 21 | 22 | export default TestImg 23 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | assetPrefix: process.env.BASE_URL || '', 3 | env: { 4 | base: process.env.BASE_URL || '' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "is-my-twitter-slow-or-what", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "gh-pages": "BASE_URL=/is-my-twitter-slow-or-what next build && next export && touch out/.nojekyll && gh-pages -d out -t" 10 | }, 11 | "dependencies": { 12 | "next": "10.0.8", 13 | "react": "17.0.1", 14 | "react-dom": "17.0.1" 15 | }, 16 | "devDependencies": { 17 | "@tailwindcss/typography": "^0.4.0", 18 | "autoprefixer": "^10.2.5", 19 | "gh-pages": "^3.1.0", 20 | "postcss": "^8.2.8", 21 | "tailwindcss": "^2.0.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | class MyDocument extends Document { 4 | static async getInitialProps(ctx) { 5 | const initialProps = await Document.getInitialProps(ctx) 6 | return { ...initialProps } 7 | } 8 | 9 | render() { 10 | return ( 11 | 12 | 13 | 14 |
15 | 16 | 17 |