master 782e4189cad6 cached
88 files
307.9 KB
91.7k tokens
9 symbols
1 requests
Download .txt
Showing preview only (330K chars total). Download the full file or copy to clipboard to get everything.
Repository: blueedgetechno/androidInReact
Branch: master
Commit: 782e4189cad6
Files: 88
Total size: 307.9 KB

Directory structure:
gitextract_7ne9rzeo/

├── .github/
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.yml
│   ├── dependabot.yml
│   └── workflows/
│       ├── Build-Deploy.yml
│       ├── Issue-text.yml
│       ├── PR-Preview.yml
│       ├── desktop.yml
│       ├── package-lock.yml
│       ├── virus.yml
│       └── winget.yml
├── .gitignore
├── LICENSE
├── README.md
├── jsconfig.json
├── package.json
├── public/
│   ├── _redirects
│   ├── img/
│   │   └── asset/
│   │       └── whatsapp/
│   │           └── pfp/
│   │               ├── apoc.jfif
│   │               ├── ava.jfif
│   │               ├── ceaser.jfif
│   │               ├── check.jfif
│   │               ├── fine.jfif
│   │               ├── liam.jfif
│   │               ├── lion.jfif
│   │               ├── pose.jfif
│   │               ├── real.jfif
│   │               └── viva.jfif
│   ├── index.html
│   ├── manifest.json
│   ├── robots.txt
│   └── site.webmanifest
├── src/
│   ├── App.css
│   ├── App.js
│   ├── components/
│   │   ├── background/
│   │   │   ├── back.scss
│   │   │   └── index.js
│   │   ├── bottomnav/
│   │   │   ├── bottom.scss
│   │   │   └── index.js
│   │   ├── icons.js
│   │   ├── main.scss
│   │   ├── quickpanel/
│   │   │   ├── index.js
│   │   │   └── qkpanel.scss
│   │   ├── statusbar.js
│   │   ├── utils.js
│   │   └── widgets/
│   │       ├── index.js
│   │       └── widget.scss
│   ├── containers/
│   │   ├── apps/
│   │   │   ├── google/
│   │   │   │   ├── google.scss
│   │   │   │   ├── index.js
│   │   │   │   └── news.json
│   │   │   ├── index.js
│   │   │   ├── index.scss
│   │   │   ├── playstore/
│   │   │   │   └── index.js
│   │   │   ├── themes.scss
│   │   │   ├── whatsapp/
│   │   │   │   ├── elements/
│   │   │   │   │   ├── extra.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── index.js
│   │   │   │   └── whatsapp.scss
│   │   │   └── youtube/
│   │   │       ├── extra.js
│   │   │       ├── extra.scss
│   │   │       ├── index.js
│   │   │       └── youtube.scss
│   │   └── home/
│   │       ├── home.scss
│   │       └── index.js
│   ├── index.css
│   ├── index.js
│   ├── service-worker.js
│   ├── serviceWorkerRegistration.js
│   └── store/
│       ├── actions/
│       │   ├── data/
│       │   │   ├── apps.js
│       │   │   ├── preset.js
│       │   │   ├── whatsapp.json
│       │   │   └── youtube.json
│       │   ├── index.js
│       │   └── prototypes.js
│       ├── index.js
│       └── reducers/
│           ├── apps/
│           │   ├── whatsapp.js
│           │   └── youtube.js
│           ├── global.js
│           ├── home.js
│           ├── quickpanel.js
│           ├── wallpaper.js
│           └── widget.js
├── src-tauri/
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── build.rs
│   ├── icons/
│   │   └── icon.icns
│   ├── src/
│   │   └── main.rs
│   └── tauri.conf.json
├── tailwind.config.js
└── timeline.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/CODEOWNERS
================================================
.github/* @andrewstech
src-tauri/* @andrewstech


================================================
FILE: .github/FUNDING.yml
================================================
custom: https://www.buymeacoffee.com/blueedgetechno


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: File a bug report
title: "[Bug]: "
labels: ["bug"]
assignees:
  - android69420
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report!
  - type: textarea
    id: what-happened
    attributes:
      label: What happened?
      description: Also tell us, what did you expect to happen?
      placeholder: Tell us what you see!
      value: "A bug happened!"
    validations:
      required: true
  - type: textarea
    id: Reproduce
    attributes:
      label: To reproduce the behavior?
      description: Steps to reproduce the behavior.
      placeholder: opened store
      value: "reproduce!"
    validations:
      required: true
  - type: dropdown
    id: browsers
    attributes:
      label: What browser are you seeing the problem on?
      multiple: false
      options:
        - Firefox
        - Chrome
        - Safari
        - Microsoft Edge
        - Other Browser
    validations:
      required: true
        


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: daily

  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: daily


================================================
FILE: .github/workflows/Build-Deploy.yml
================================================
name: Build & Deploy

on:
  push:
    branches: [master]
  workflow_dispatch:

jobs:
  Build-Deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: 16
        cache: npm

    - name: Build
      run: npm ci && npm run ghbuild

    - name: Deploy to gh-pages
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        cname: android.blueedge.me
        publish_dir: ./build


================================================
FILE: .github/workflows/Issue-text.yml
================================================
name: issue-text

on:
  issues:
    types: [opened]

jobs:
  comment:
    runs-on: ubuntu-latest

    steps:
    - uses: ben-z/actions-comment-on-issue@1.0.2
      with:
        message: "Hey, thank you for creating an issue. We will normally respond within 24 hours."
        GITHUB_TOKEN: ${{ secrets.BOT }}
        
    - uses: stefanbuck/github-issue-parser@v2
      id: issue-parser
      with:
        template-path: .github/ISSUE_TEMPLATE/bug_report.yml
        
    - run: echo '${{ steps.issue-parser.outputs.jsonString }}'
    
    - run: echo ${{ steps.issue-parser.outputs.issueparser_what_browser_are_you_seeing_the_problem_on }}
    
    - uses: actions-ecosystem/action-add-labels@v1
      with:
        labels: ${{ steps.issue-parser.outputs.issueparser_what_browser_are_you_seeing_the_problem_on }}
        github_token: ${{ secrets.BOT }}
        


================================================
FILE: .github/workflows/PR-Preview.yml
================================================
name: Generate PR Preview

on:
  pull_request_target:
    types: [opened, reopened, synchronize]

jobs:
  Build:
    runs-on: ubuntu-latest

    steps:
      - uses: peter-evans/create-or-update-comment@v2
        id: couc
        with:
          body: ⌛ Deploy Preview - Build in Progress
          issue-number: ${{ github.event.pull_request.number }}
          token: ${{ secrets.BOT }}

      - uses: actions/checkout@v3
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/merge

      - uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: create repo
        if: ${{ github.event.action == 'opened'}}
        run: gh repo create blueDroidBot/${{ github.event.pull_request.number }} --public
        env:
          GITHUB_TOKEN: ${{ secrets.BOT }}

      - name: Build
        run: npm ci && npm run ghbuild

      - uses: peaceiris/actions-gh-pages@v3
        with:
          personal_token: ${{ secrets.BOT }}
          publish_dir: ./build
          external_repository: blueDroidBot/${{ github.event.pull_request.number }}

      - name: Sleep to make sure page gets updated
        run: sleep 1m
        shell: bash

      - name: comment Preview Ready
        uses: peter-evans/create-or-update-comment@v2
        with:
          body: |
            ✔️ Deploy Preview for Ready!
            😎 Browse the preview: https://android-preview.blueedge.me/${{ github.event.pull_request.number }} !
            🔍 Inspect the deploy log: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          comment-id: ${{ steps.couc.outputs.comment-id }}
          edit-mode: replace
          token: ${{ secrets.BOT }}

      - if: ${{ failure() }}
        uses: peter-evans/create-or-update-comment@v2
        with:
          body: |
            ❌ Deploy Preview failed
            🔍 Inspect the deploy log: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          comment-id: ${{ steps.couc.outputs.comment-id }}
          edit-mode: replace
          token: ${{ secrets.BOT }}


================================================
FILE: .github/workflows/desktop.yml
================================================
name: publish
on:
  issues:
    types:
      - labeled
jobs:
  publish-tauri:
    if: github.event.label.name == 'desktop-release'
    strategy:
      fail-fast: false
      matrix:
        platform: [macos-latest, ubuntu-latest, windows-latest]

    runs-on: ${{ matrix.platform }}
    env:
      CI: false
    steps:
      - uses: actions/checkout@v3
      - name: setup node
        uses: actions/setup-node@v3
        with:
          node-version: 12
          cache: npm

      - name: git http
        run: |
          git config --global url."https://github.com/".insteadOf git@github.com:
          git config --global url."https://".insteadOf git://

      - name: install Rust stable
        uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          
      - uses: Swatinem/rust-cache@v1
        with:
          sharedKey: ${{ matrix.platform }}
      
      - name: install webkit2gtk (ubuntu only)
        if: matrix.platform == 'ubuntu-latest'
        run: |
          sudo apt-get update
          sudo apt-get install -y webkit2gtk-4.0

      - name: CI
        run: |
          npm ci

      - name: install app dependencies and build it
        run: npm run build
      - uses: tauri-apps/tauri-action@v0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
          TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
        with:
          tagName: __VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version
          releaseName: "androidInReact v__VERSION__"
          releaseBody: ${{ github.event.issue.body }}
          releaseDraft: false
          prerelease: false


================================================
FILE: .github/workflows/package-lock.yml
================================================
name: "Re generate the package-lock file"
on:
  workflow_dispatch:
  
jobs:
  rebuild:
    name: reBuild
    runs-on: windows-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    
    - name: Install Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '16.6.1'
        
    - name: remove old one
      run: rm package-lock.json
      
    - name: build new one
      run:  npm install
      env:
          CI: false
      
    - name: cpr
      uses: peter-evans/create-pull-request@v4
      with:
        token: ${{ secrets.GITHUB_TOKEN }}
        commit-message: Update package-lock
        title: Update package-lock
        body: Update package-lock  


================================================
FILE: .github/workflows/virus.yml
================================================
name: released

on:
  release:
  workflow_dispatch:  

jobs:
  virustotal:
    runs-on: ubuntu-latest
    steps:
      - name: VirusTotal Scan
        uses: crazy-max/ghaction-virustotal@v2
        with:
          vt_api_key: ${{ secrets.VT_API_KEY }}
          update_release_body: true
          files: |
            .msi$
            .exe$
            .deb$


================================================
FILE: .github/workflows/winget.yml
================================================
name: Publish to WinGet
on:
  release:
    types: [released]
jobs:
  publish:
    runs-on: windows-latest # action can only be run on windows
    steps:
      - uses: vedantmgoyal2009/winget-releaser@latest
        with:
          identifier: blueedge.android11react
          token: ${{ secrets.BOT }}


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
node_modules
/.pnp
.pnp.js
.env
todo*

# testing
/coverage

# production
/build
/target/
WixTools
*.py
rough*

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*


================================================
FILE: LICENSE
================================================
Creative Commons Legal Code

CC0 1.0 Universal

    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
    HEREUNDER.

Statement of Purpose

The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").

Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.

For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.

1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:

  i. the right to reproduce, adapt, distribute, perform, display,
     communicate, and translate a Work;
 ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
     likeness depicted in a Work;
 iv. rights protecting against unfair competition in regards to a Work,
     subject to the limitations in paragraph 4(a), below;
  v. rights protecting the extraction, dissemination, use and reuse of data
     in a Work;
 vi. database rights (such as those arising under Directive 96/9/EC of the
     European Parliament and of the Council of 11 March 1996 on the legal
     protection of databases, and under any national implementation
     thereof, including any amended or successor version of such
     directive); and
vii. other similar, equivalent or corresponding rights throughout the
     world based on applicable law or treaty, and any national
     implementations thereof.

2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.

3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.

4. Limitations and Disclaimers.

 a. No trademark or patent rights held by Affirmer are waived, abandoned,
    surrendered, licensed or otherwise affected by this document.
 b. Affirmer offers the Work as-is and makes no representations or
    warranties of any kind concerning the Work, express, implied,
    statutory or otherwise, including without limitation warranties of
    title, merchantability, fitness for a particular purpose, non
    infringement, or the absence of latent or other defects, accuracy, or
    the present or absence of errors, whether or not discoverable, all to
    the greatest extent permissible under applicable law.
 c. Affirmer disclaims responsibility for clearing rights of other persons
    that may apply to the Work or any use thereof, including without
    limitation any person's Copyright and Related Rights in the Work.
    Further, Affirmer disclaims responsibility for obtaining any necessary
    consents, permissions or other rights required for any use of the
    Work.
 d. Affirmer understands and acknowledges that Creative Commons is not a
    party to this document and has no duty or obligation with respect to
    this CC0 or use of the Work.


================================================
FILE: README.md
================================================
# Android on Web

[![Follow me](https://img.shields.io/github/followers/blueedgetechno?label=follow%20me&style=social)](https://github.com/blueedgetechno)
[![Follow Twitter](https://img.shields.io/twitter/follow/blueedgetechno?label=Follow%20me&style=social)](https://twitter.com/blueedgetechno)
[![Join](https://img.shields.io/discord/868499076432408627.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/NcjaNdwtnR)
[![coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/blueedgetechno)

An open-source project that aims to provide an indistinguishably accurate experience of Android on web. The project leverages different JavaScript Web APIs to imitates the Android functionalities and CSS animations to bring life into it.

------------
#### Visit the Live Application🌈:   [android.blueedge.me](https://android.blueedge.me)

## Gallery
![img1](public/gall1.png)

![img1](public/gall2.png)

#### Visit the Live Application🌈:   [android.blueedge.me](https://android.blueedge.me)

# Stack

- Framework - React (^17.0.2) + Redux
- Component/UI Library - Material UI
- Styling Solution - SCSS and CSS Modules (+ tailwind).
- Icons - fontAwesome/mui icons/custom svg icons

# Features
- [x] Home page, App icons
- [x] Bottom Navigation bar
- [x] Widgets like google search
- [x] Calendar, Date, Clock Widgets
- [x] Quick menu from slide up
- [x] Recent menu and apps
- [x] WhatsApp Application
- [x] YouTube Application
- [x] Google Search App

# Dev Features
- [x] Flexible and Scalable
- [x] Easy to add custom Applications
- [x] Easy to bind with existing system
- [x] Built-In Navigation system per app
- [x] Modular Applications and their styling
- [x] Proper state management with Redux

## FAQ

1. Is this real android OS?
    - No, This is not a real operating system. It is just imitation of it with different technology.


2. How long did it take?
    - 6 weeks (without gaps)


3. Have you ran into problems during development?
    - Duhh. More than dozen big ones and hundreds of small problems ([more info](timeline.md)).


4. Can I contribute?
    - Yes, you can! Open an issue, create a pull request, head over to [discussions](https://github.com/blueedgetechno/androidInReact/discussions) or join the [discord](https://discord.gg/NcjaNdwtnR).


5. Where did you get the inspiration from, if you have?
    - I got the inspired to make the project on the suggestion of [Derry Shribman](https://github.com/xderry).


6. Why do JavaScript developers wear glasses?
    - Because they cannot C# (see sharp).     ![answer](public/hehe.jpg)


## Online Deployment

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)

[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2Fblueedgetechno%2FandroidInReact&envs=PORT&PORTDesc=Port+of+the+application&PORTDefault=3000)

[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/blueedgetechno/androidInReact)


## Acknowledgements

- [Derry Shribman](https://github.com/xderry)
- [React-hammerjs](https://github.com/JedWatson/react-hammerjs)
- [React-slick](https://github.com/akiran/react-slick)


## Contributors
[![contributors](https://contrib.rocks/image?repo=blueedgetechno/androidInReact)](https://github.com/blueedgetechno/androidInReact/graphs/contributors)


## License

⚖️ CC0-1.0 License


================================================
FILE: jsconfig.json
================================================
{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}


================================================
FILE: package.json
================================================
{
  "name": "Android11React",
  "description": "Android made in React",
  "repository": "https://github.com/blueedgetechno/androidInReact",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.9.0",
    "@emotion/styled": "^11.8.1",
    "@fortawesome/fontawesome-svg-core": "^6.1.1",
    "@fortawesome/free-regular-svg-icons": "^6.1.1",
    "@fortawesome/free-solid-svg-icons": "^6.1.1",
    "@fortawesome/react-fontawesome": "^0.1.18",
    "@mui/icons-material": "^5.6.2",
    "@mui/lab": "^5.0.0-alpha.83",
    "@mui/material": "^5.8.1",
    "@reduxjs/toolkit": "^1.8.2",
    "@sentry/react": "^6.19.7",
    "@sentry/tracing": "^6.19.7",
    "@tauri-apps/api": "^1.0.0-rc.6",
    "@win11react/react-hammerjs": "1.0.2",
    "axios": "^0.27.2",
    "color-parse": "^1.4.2",
    "color-rgba": "^2.4.0",
    "react": "^18.1.0",
    "react-calendar": "^3.7.0",
    "react-dom": "^18.1.0",
    "react-player": "^2.10.1",
    "react-redux": "^8.0.2",
    "react-scripts": "^5.0.1",
    "react-slick": "^0.29.0",
    "redux": "^4.2.0",
    "redux-thunk": "^2.4.1",
    "slick-carousel": "^1.8.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "ghbuild": "CI=false react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "tauri": "tauri"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@tauri-apps/cli": "^1.0.0-rc.12",
    "node-sass": "^7.0.1",
    "tailwindcss": "^3.0.24"
  }
}


================================================
FILE: public/_redirects
================================================
/*  /index.html  200


================================================
FILE: public/index.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="Android on web made with React" />

  <meta name="og:title" property="og:title" content="Android in React" />
  <meta name="og:description" property="og:description" content="Android on web made with React" />
  <meta name="og:color" content="#303030" />

  <title>Android in React</title>
  <link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" />
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

</head>

<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root"></div>
</body>

</html>


================================================
FILE: public/manifest.json
================================================
{
  "short_name": "Android In React",
  "name": "Android on web",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    },
    {
      "src": "logo192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "logo512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "author": {
		"name": "Blue Edge",
		"website": "https://blueedge.me",
		"github": "https://github.com/blueedgetechno",
		"source-repo": "https://github.com/blueedgetechno/androidInReact"
	},
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#fefefe",
  "background_color": "#ffffff"
}


================================================
FILE: public/robots.txt
================================================
# https://www.robotstxt.org/robotstxt.html
User-agent: *


================================================
FILE: public/site.webmanifest
================================================
{
  "name": "",
  "short_name": "",
  "icons": [{
    "src": "/android-chrome-192x192.png",
    "sizes": "192x192",
    "type": "image/png"
  }, {
    "src": "/android-chrome-512x512.png",
    "sizes": "512x512",
    "type": "image/png"
  }],
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "display": "standalone"
}


================================================
FILE: src/App.css
================================================
.App {
  --stbar-h: 2.4em;
}

*[data-ninja="true"] {
  pointer-events: none;
}

.uicon {
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
}

.text-xss {
  font-size: 0.64em;
}

.smooth-trans {
  transition: all 200ms ease-in-out;
}

.invert-true,
img[data-invert="true"],
svg[data-invert="true"],
i[data-invert="true"] {
  filter: invert(1);
}

.rounded-true,
img[data-rounded="true"],
svg[data-rounded="true"],
i[data-rounded="true"] {
  border-radius: 1000px;
}

.flip-true,
img[data-flip="true"],
svg[data-flip="true"],
i[data-flip="true"] {
  transform: scale(-1, 1);
}

.uicon img {
  transform-origin: center;
  transition: 400ms ease-in-out;
}

.imageCont, .vidCont{
  position: relative;
  display: grid;
  place-items: center;
  width: auto;
  height: auto;
}

.imageCont &[data-back="true"] {
  background-position: center;
  background-size: cover;
}

.imageCont img[data-free="false"] {
  max-width: 100%;
  max-height: 100%;
}

.imageCont.rounded {
  overflow: hidden;
}

.noscroll::-webkit-scrollbar {
  display: none;
}

.thinScroll::-webkit-scrollbar {
  width: 2px;
  height: 2px;
  background-color: transparent;
}

.medScroll::-webkit-scrollbar {
  width: 4px;
  height: 4px;
  background-color: transparent;
}

.thinScroll:hover::-webkit-scrollbar-thumb,
.medScroll:hover::-webkit-scrollbar-thumb {
  background: rgba(32, 24, 148, 0.4);
}

.lightScroll:hover::-webkit-scrollbar-thumb {
  background: rgba(162, 159, 209, 0.4);
}

.thinScroll::-webkit-scrollbar-thumb {
  width: 2px;
  height: 2px;
  border-radius: 10px;
  background-color: transparent;
}

.medScroll::-webkit-scrollbar-thumb {
  width: 4px;
  height: 4px;
  border-radius: 10px;
  background-color: transparent;
}

.upbug::after {
  content: "";
  position: absolute;
  left: 0;
  top: -2px;
  width: 100%;
  height: 4px;
  background: #128c7e;
}

.downbug::after {
  content: "";
  position: absolute;
  z-index: 1;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 4px;
  background: #128c7e;
}

@media only screen and (min-width: 520px) {
  *{
    /* cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewport='0 0 24 24' width='24' height='24'><circle cx='12' cy='12' r='12' style='fill: rgba(64, 64, 64, 0.2);'></circle><circle cx='12' cy='12' r='10' style='fill: rgba(0, 0, 0, 0.25);'></circle></svg>"), auto; */
  }
}


================================================
FILE: src/App.js
================================================
import React, {useState, useEffect} from 'react';

import {Background, OverLay} from 'components/background';
import Home from 'containers/home';
import {loadSettings} from 'store/actions/index';

import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import './App.css';

function App() {

  useEffect(() => {
    if (!window.onstart) {
      // console.log("Loading settings...");
      window.onstart = loadSettings();
    }
  });

  return (
    <div className="App">
      <div className="appwrap">
        <Background/>
        <Home/>
        <OverLay/>
      </div>
    </div>
  );
}

export default App;


================================================
FILE: src/components/background/back.scss
================================================
.background {
  min-width: 100vw;
  min-height: 100%;
  background-color: #fefefe;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  transition: all 0.2s ease;
}

.app-overlay, .brightness-overlay{
  position: absolute;
  pointer-events: none;
  inset: 0;
}

.brightness-overlay{
  background: #000;
}


================================================
FILE: src/components/background/index.js
================================================
import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import "./back.scss";

export const Background = () => {
  const wall = useSelector((state) => state.wallpaper);

  return (
    <div className="background" style={{
        backgroundImage: `url(img/wallpaper/${wall.src})`,
      }}></div>
  );
}

export const OverLay = () => {
  const bright = useSelector((state) => state.quickpanel.lazy_bright)

  return (
    <div className="app-overlay">
      <div className="brightness-overlay" style={{opacity: `${90 - bright*0.9}%`}}></div>
    </div>
  );
}


================================================
FILE: src/components/bottomnav/bottom.scss
================================================
.bottom-nav{
  display: flex;
  flex-direction: column;
  align-items: center;
  transition: background-color 200ms ease-in-out;
}

.bt-nav-container{
  display: flex;
  justify-content: space-around;
  padding: 0.1em 1em;
  width: 100%;
  max-width: 50em;

  .uicon{
    padding: 0.5em;
    color: var(--med-txt);
    // filter: drop-shadow(1px 1px 4px rgba(254, 254, 254, 0.6));
  }

  &[data-invert="true"]{
    .uicon{
      color: var(--gray-txt);
    }
  }

  .bar-icon{
    transform: rotateZ(90deg);
  }
}


================================================
FILE: src/components/bottomnav/index.js
================================================
import React, { useState, useEffect, useRef} from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {Icon} from 'components/utils';
import parse from 'color-parse';
import rgba from 'color-rgba';

import './bottom.scss';

const BottomNav = (props) => {
  const [invert, setInvert] = useState(props.invert);
  const navBar = useRef();
  const dispatch = useDispatch();

  const istransparent = (bgcolor)=>{
    var colors = parse(bgcolor)
    return colors.alpha < 0.1;
  }

  const refreshInvert = ()=>{
    var bgcolor = getComputedStyle(navBar.current).getPropertyValue('background-color');
    if(!istransparent(bgcolor)){
      var arr = rgba(bgcolor),
          avg = (arr[0]+arr[1]+arr[2])/3

      if(avg<128) setInvert(true)
      else setInvert(false)
    }else setInvert(false)
  }

  useEffect(()=>{
    if(navBar.current && props.invert==null){
      setTimeout(refreshInvert,100)
    }else if (props.invert!=null) {
      setInvert(props.invert)
    }
  }, [props.bg, props.invert])

  return (
    <div className="bottom-nav" style={{
      backgroundColor: props.bg
    }} ref={navBar}>
      <div className="bt-nav-container" data-invert={invert}>
        <Icon className="bar-icon press-in" fafa="faBars" w={16} action="home/setRecent"/>
        <Icon className="press-in" mui="CropSquare" w={20} action="home/setHome"/>
        <Icon className="press-in" mui="ArrowBackIos" w={20} action="home/goBack"/>
      </div>
    </div>
  );
};

export default BottomNav;


================================================
FILE: src/components/icons.js
================================================
export const NetworkIcon = (props) => {
  // network icon that is responsive to a status props indicating network strength
  const status = props.status || 4;
  return (
    <div className="uicon">
      <svg
        viewBox="0 0 110 100"
        width={props.w}
        height={props.h || props.w}
        fill={props.fill}
      >
        <rect x={0} y={75} width={20} height={25} rx={5} ry={5}></rect>
        <rect
          x={30}
          y={50}
          width={20}
          height={50}
          rx={5}
          ry={5}
          fill={status > 1 ? props.fill : props.filldim}
        ></rect>
        <rect
          x={60}
          y={25}
          width={20}
          height={75}
          rx={5}
          ry={5}
          fill={status > 2 ? props.fill : props.filldim}
        ></rect>
        <rect
          x={90}
          y={0}
          width={20}
          height={100}
          rx={5}
          ry={5}
          fill={status > 3 ? props.fill : props.filldim}
        ></rect>
      </svg>
    </div>
  );
};

// Battery Icon by Google Inc. - Iconscout
export const BatteryIcon = (props) => {
  var h = ((props.battery.level || 100)*33);

  return (
    <div className="uicon">
      <svg
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 24 48"
        width={props.w}
        height={props.h || props.w}>
        <g
          style={{
            transform: "translateX(-12px)",
          }}
          fill={null}>
          <path d="M34 10.67c0-1.48-1.19-2.67-2.67-2.67h-3.33v-4h-8v4h-3.33c-1.48 0-2.67 1.19-2.67 2.67v7.33h20v-7.33z" />
          <path d="M14 18v23.33c0 1.47 1.19 2.67 2.67 2.67h14.67c1.47 0 2.67-1.19 2.67-2.67v-23.33h-20.01z" />
          <rect
            x={15}
            y={42.5 - h}
            width={18}
            height={h}
            fill={props.fill}
            rx={2}></rect>
          <g style={{ transform: "translate(19px,16px) scale(0.036)" }}>
            <path
              fill={props.battery.charging ? props.cfill : "none"}
              d="M296 160H180.6l42.6-129.8C227.2 15 215.7 0 200 0H56C44 0 33.8 8.9 32.2 20.8l-32 240C-1.7 275.2 9.5 288 24 288h118.7L96.6 482.5c-3.6 15.2 8 29.5 23.3 29.5 8.4 0 16.4-4.4 20.8-12l176-304c9.3-15.9-2.2-36-20.7-36z"></path>
          </g>
        </g>
      </svg>
    </div>
  );
};

export const pinned = (props) => {
  return (
    <svg viewBox="0 0 16 16" height={16} width={16} {...props} className={
      props.flip? "flip-true": "" +
      (props.invert? " invert-true": "") +
      (props.rounded? " rounded-true": "")
    } style={{
      width: props.w,
      height: props.h || props.w,
      color: props.color,
      margin: props.margin
    }}>
      <path
        d="M12.074 4.21 8.7 8.232l.116 4.233a.4.4 0 0 1-.657.318L.43 6.297a.4.4 0 0 1 .199-.702l4.196-.622L8.196.957a.63.63 0 0 1 .887-.078l2.914 2.445a.63.63 0 0 1 .077.887ZM1.294 14.229a.713.713 0 0 1-1.09-.915l2.674-3.64 1.536 1.288-3.12 3.267Z">
      </path>
    </svg>
  );
};

export const seentick = (props) => {
  return (
    <svg viewBox="0 0 16 15" width="16" height="15" {...props} className={
      props.flip? "flip-true": "" +
      (props.invert? " invert-true": "") +
      (props.rounded? " rounded-true": "")
    } style={{
      width: props.w,
      height: props.h || props.w,
      color: props.color,
      margin: props.margin
    }}>
      <path d="m15.01 3.316-.478-.372a.365.365 0 0 0-.51.063L8.666 9.879a.32.32 0 0 1-.484.033l-.358-.325a.319.319 0 0 0-.484.032l-.378.483a.418.418 0 0 0 .036.541l1.32 1.266c.143.14.361.125.484-.033l6.272-8.048a.366.366 0 0 0-.064-.512zm-4.1 0-.478-.372a.365.365 0 0 0-.51.063L4.566 9.879a.32.32 0 0 1-.484.033L1.891 7.769a.366.366 0 0 0-.515.006l-.423.433a.364.364 0 0 0 .006.514l3.258 3.185c.143.14.361.125.484-.033l6.272-8.048a.365.365 0 0 0-.063-.51z">
      </path>
    </svg>
  );
};


================================================
FILE: src/components/main.scss
================================================
body {
  --dark-txt: #000;
  --med-dark: #111;
  --txt-col: #222;
  --med-txt: #3c3c3c;
  --comp-txt: #ddd;
  --light-txt: #aaa;
  --comp-clr: #e6e6e6;
  --gray-txt: #555;
  --sat-txt: #777;
  --mid-txt: #888;
  --clrPrm: #0067c0;
  --scroll: rgb(255 255 255 / 80%);
  --white: #fefefe;
  --black: #010101;
  --active-blue: #0181fd;
}

.med-txt {
  color: var(--med-txt);
}

.light-txt {
  color: var(--light-txt);
}

.comp-txt {
  color: var(--comp-txt);
}

.gray-txt {
  color: var(--gray-txt);
}

.sat-txt {
  color: var(--sat-txt);
}

.backblur {
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}

.prtclk {
  * {
    pointer-events: none;
  }
}

.txt-ovf {
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wb-line-2 {
  -webkit-line-clamp: 2;
}

.wb-line-3 {
  -webkit-line-clamp: 3;
}

.wb-line-4 {
  -webkit-line-clamp: 4;
}

.status-bar {
  // position: absolute;
  height: var(--stbar-h);
  width: 100%;
  display: flex;
  font-size: 0.8em;
  padding: 1em 1em 0;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
  transition: background-color 200ms ease-in-out;
  color: var(--txt-col);

  &[data-invert="true"] {
    color: var(--comp-txt);
  }
}

.status-row {
  display: flex;
  align-items: center;

  & > div {
    margin: 0 2px;
  }

  .battery-level {
    font-size: 0.9em;
    height: 14px;
    margin-right: 0.25em;
  }
}
@keyframes fastfadein {
  0% {
    opacity: 0;
  }

  99% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}
@keyframes pop-up {
  from {
    transform: scale(0.5);
  }

  to {
    transform: scale(1);
  }
}
@keyframes pop-up2 {
  from {
    transform: scale(0.5);
  }

  to {
    transform: scale(1);
  }
}

.press-in {
  transition: transform 200ms ease-in-out;

  &:active {
    transform: scale(0.86);
  }
}

.softpress-in {
  transition: transform 200ms ease-in-out;

  &:active {
    transform: scale(0.94);
  }
}

.quick-trans {
  transition: transform 100ms ease-in-out;
}

.flex-column {
  display: flex;
  flex-direction: column;
}

.active-light-lit {
  transition: all 200ms ease-in-out;

  &:active {
    background: rgba(254, 254, 254, 0.25);
  }
}

.active-dark-lit {
  transition: all 200ms ease-in-out;

  &:active {
    background: rgba(0, 0, 0, 0.25);
  }
}

.xlit:active {
  background: rgba(0, 0, 0, 0.12);
}

.vidCont {
  .react-video,
  video {
    height: 100%;
  }

  .play-icon {
    position: absolute;
    width: 60px;
    height: 60px;
    background: rgba(0, 0, 0, 0.5);
    border-radius: 4em;
    color: var(--white);
    z-index: 1;
    opacity: 0;
    transition: all 200ms ease-in-out;
  }

  &:hover .play-icon,
  .play-icon:hover {
    opacity: 1;
  }

  .play-icon.opacity-100 {
    opacity: 1;
  }
}

.video-control-container {
  width: 100%;
  display: flex;
  align-items: center;
  position: absolute;
  bottom: 0;

  .video-progress {
    flex-grow: 1;
    margin: 0 0.8em;
  }

  .MuiSlider-rail {
    background: var(--comp-txt);
    opacity: 0.8;
  }

  .MuiSlider-thumb,
  .MuiSlider-track {
    opacity: 1;
  }

  .prog-text {
    color: var(--comp-txt);
    font-size: 0.8em;
    font-weight: 500;
    padding: 0 0.5em;
  }
}


================================================
FILE: src/components/quickpanel/index.js
================================================
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Hammer, { displayName, propTypes } from '@win11react/react-hammerjs';
import Slider from '@mui/material/Slider';
import Swiper from 'react-slick';

import { Icon } from 'components/utils';
import StatusBar from 'components/statusbar';
import { dispatchAction, dispatchAct } from 'store/actions';

import './qkpanel.scss';

const DateObj = (props) => {
  const date = useSelector((state) => state.global.date)
  const time = useSelector((state) => state.global.time)

  var datestring = new Date(date.year, date.month, date.day).toLocaleString(
    "en-us",
    { weekday: "short", day: "numeric", month: "long" }
  );

  return props.showtime ? (
    <div className="date-day quick-panel-time" data-extended={props.ext}>
      <div className="text-5xl font-thin">
        {time.hours}:{time.minutes}
      </div>
      <div className="date-day-text">{datestring}</div>
    </div>
  ) : (
    <div className={"date-day smooth-trans " + (props.ext ? "opacity-0" : "")}>
      <div className="date-day-text">{datestring}</div>
    </div>
  )
}

export default function QuickPanel() {
  const [brightness, setBrightness] = useState(100)
  const quickpanel = useSelector((state) => state.quickpanel)
  const display = useSelector((state) => state.global.display)

  const closePanel = (e) => {
    if (e.target.classList.contains("quickpanel-container")) {
      dispatchAct({ type: "quickpanel/close" })
    }
  }

  const handleBright = (e, value) => setBrightness(value)
  const extendPanel = () => dispatchAct({ type: "quickpanel/extend" })
  const collapsePanel = (e) => {
    dispatchAct({ type: "quickpanel/collapse" })
  }

  useEffect(() => {
    dispatchAct({type: "quickpanel/setLazyBright", payload: brightness})
  }, [brightness])

  return (
    <Hammer
      onClick={closePanel}
      onSwipeUp={closePanel}
      onSwipeDown={extendPanel}
      direction="DIRECTION_ALL">
      <div
        className="quickpanel-container backblur"
        data-open={quickpanel.open}>
        <Hammer onSwipeUp={collapsePanel} direction="DIRECTION_ALL">
          <div className="quickpanel" data-extended={quickpanel.extended}>
            <StatusBar hidetime={quickpanel.extended} />
            <DateObj showtime ext={quickpanel.extended && display.height > 600} />
            <div className="date-and-setting">
              <DateObj ext={quickpanel.extended} />
              <Icon fafa="faCog" w={16} />
            </div>
            <QuickTool ext={quickpanel.extended} tools={quickpanel.config}/>
            <div className="brightness-input-container flex">
              <Icon className="mr-2" mui="LightMode" w={16}/>
              <Slider value={brightness} defaultValue={brightness}
                onChange={handleBright} size="small" aria-label="Small"/>
            </div>
            <div className="quick-panel-bottom-bar"></div>
          </div>
        </Hammer>
      </div>
    </Hammer>
  )
}

const QuickTool = (props) => {
  const [toolCount, setToolCount] = useState(12)
  const display = useSelector((state) => state.global.display)
  const settings = {
    dots: true,
    arrows: false,
    infinite: false,
    speed: 200
  }

  useEffect(() => {
    if(display.height > 600) setToolCount(12)
    else setToolCount(8)
  },[display.height])

  return (
    <div className="notif-panel">
      <div className="mini-quick-panel" data-ext={props.ext}>
        {props.tools && props.tools.slice(0,6).map((item) => {
          return (
            <div className="mini-quick-panel-item" key={item.name} data-active={item.state!=0}>
              <Icon className="mini-quick-icon" mui={item.icon} w={26} />
            </div>
          );
        })}
      </div>
      <Swiper {...settings} className={"extended-quick" + (
        props.ext ? " extended-quick-open" : ""
      )}>
        {props.tools && props.tools.map((temp, idx) => {
          if(idx%toolCount==0){
            return (
              <div className="quick-tool-container" key={idx}>
                {props.tools.slice(idx, idx+toolCount).map((item,i) => {
                  return (
                    <div className="quick-tool-item press-in prtclk" onClick={dispatchAction}
                      data-action="quickpanel/toggleTool" data-payload={i} key={i}>
                      <div className="mini-quick-panel-item" data-active={item.state!=0}>
                        <Icon className="mini-quick-icon" mui={item.icon} w={26}/>
                      </div>
                      <div className="quick-tool-info">{item.name}</div>
                    </div>
                  );
                })}
              </div>
            )
          }
        })}
      </Swiper>
    </div>
  )
}


================================================
FILE: src/components/quickpanel/qkpanel.scss
================================================
.quickpanel-container {
  position: absolute;
  top: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  background: rgba(0, 0, 0, 0);
  opacity: 0;
  z-index: -1;
  transition: 0.3s all ease-in-out;
  display: flex;
  flex-direction: column;
  align-items: center;

  &[data-open="true"] {
    pointer-events: auto;
    z-index: 1000;
    opacity: 1;
    background: rgba(0, 0, 0, 0.2);

    .quickpanel {
      transform: translateY(0);
    }
  }
}

.quickpanel {
  width: 100%;
  max-width: 540px;
  max-height: 50%;
  // padding: 0 1.6em 0.5em;
  padding-bottom: 0.5em;
  color: var(--med-txt);
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: hidden;

  & > div {
    width: 100%;
    padding: 0 1.6em;
  }

  .quick-panel-bottom-bar {
    width: 32px;
    height: 4px;
    background: var(--light-txt);
    border-radius: 10px;
  }

  .notif-panel {
    padding-left: 0;
    padding-right: 0;
  }

  border-radius: 0 0 1em 1em;
  background: #fefefe;
  transform: translateY(-100%);
  transition: all 0.3s ease-in-out;

  &[data-extended="true"] {
    max-height: 100%;
  }

  .date-and-setting {
    display: flex;
    justify-content: space-between;
    color: var(--med-txt);
    margin-bottom: 0.5em;
    transition: all 200ms ease-in-out;
  }

  .date-day {
    font-weight: 600;
    color: var(--txt-col);
  }

  .quick-panel-time {
    max-height: 0;
    padding: 0;
    opacity: 0;
    font-weight: 500;
    transition: all 200ms linear;
    display: flex;
    overflow: hidden;
    flex-direction: column;
    align-items: center;

    &[data-extended="true"] {
      opacity: 1;
      padding: 2em 0;
      max-height: 240px;
    }
  }
}

.notif-panel {
  padding-top: 1em;
  text-align: center;

  .mini-quick-panel {
    padding: 0 1.6em;
    display: flex;
    width: 100%;
    justify-content: space-between;
    transition: all 300ms ease-in-out;
    margin-bottom: 1em;

    &[data-ext="true"] {
      position: absolute;
      width: 150%;
      opacity: 0;
      z-index: -1;
      pointer-events: none;
    }
  }

  .mini-quick-panel-item {
    padding: 8px;
    background-color: var(--comp-txt);
    border-radius: 50%;

    .mini-quick-icon {
      color: var(--sat-txt);
    }

    &[data-active="true"] {
      background: var(--active-blue);

      svg {
        color: #fefefe;
      }
    }
  }

  .extended-quick {
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    padding: 0 1.6em;
    pointer-events: none;
    transition: all 200ms ease-in-out;

    .quick-tool-container {
      width: 100%;
      display: grid !important;
      grid-template-columns: auto auto auto auto;
      place-items: center;
      align-items: flex-start;
      justify-content: space-between;
      // margin-right: 1em;
      // margin: 0 1em;
    }

    .quick-tool-item {
      max-width: 42px;
      margin: 0.5em 0;
      display: flex;
      flex-direction: column;
      align-items: center;

      .mini-quick-panel-item {
        margin-bottom: 0.5em;
      }

      .quick-tool-info {
        font-size: 0.75em;
        line-height: 1em;
        font-weight: 600;
        width: 200%;
      }
    }
  }

  .extended-quick-open {
    max-height: 800px;
    pointer-events: auto;
    // overflow: visible;
    opacity: 1;
    animation: fastfadein 200ms linear;
  }

  .slick-list {
    margin: 0 -2em;
  }

  .slick-slide > div {
    margin: 0 2em;
  }

  .slick-dots {
    position: relative;
    bottom: 0;

    li {
      width: 6px;
    }
  }
}


================================================
FILE: src/components/statusbar.js
================================================
import React, {useState, useEffect, useRef} from 'react';
import { useSelector } from 'react-redux';
import Hammer from '@win11react/react-hammerjs';

import parse from 'color-parse';
import rgba from 'color-rgba';

import { Icon } from 'components/utils';
import { dispatchAction } from 'store/actions';
import { NetworkIcon, BatteryIcon } from 'components/icons';

import './main.scss';

const StatusBar = (props) => {
  const [invert, setInvert] = useState(props.invert);
  const statusBar = useRef();
  const battery = useSelector((state) => state.global.battery);
  const time = useSelector((state) => state.global.time);

  const istransparent = (bgcolor)=>{
    var colors = parse(bgcolor)
    return colors.alpha < 0.1;
  }

  useEffect(()=>{
    if(props.invert!=null){
      setInvert(props.invert)
    }else if(props.bg || (invert && !props.bg)){
      if(statusBar.current){
        var bgcolor = getComputedStyle(statusBar.current).getPropertyValue('background-color');
        if(!istransparent(bgcolor)){
          var arr = rgba(bgcolor),
              avg = (arr[0]+arr[1]+arr[2])/3

          if(avg<128) setInvert(true)
          else setInvert(false)
        }else{
          setInvert(false)
        }
      }
    }
  }, [props.bg])

  return (
    <Hammer onSwipeDown={dispatchAction} direction="DIRECTION_ALL">
      <div className="status-bar" data-action="quickpanel/open" style={{
        backgroundColor: props.bg
      }} data-invert={invert}>
        <div className="hidden" style={{backgroundColor: props.bg}} ref={statusBar}></div>
        <div className={
            "status-time smooth-trans" + (props.hidetime ? " opacity-0" : "")
          }>{time.hours}:{time.minutes}</div>
        <div className="status-row">
          <Icon fafa="faWifi" w={12} fill="#444" />
          <NetworkIcon w={10} fill={invert?"#ddd":"#222"} filldim="#888" />
          <div className="flex items-center text-xs font-bold">
            <div className="battery-level">
              {Math.round(battery.level * 100) + "%"}
            </div>
            <BatteryIcon w={6} h={12} fill={invert?"#ddd":"#222"} battery={battery}
              filldim={invert?"#555":"#aaa"} cfill={invert?"#010101":"#fefefe"}/>
          </div>
        </div>
      </div>
    </Hammer>
  );
};

export default StatusBar;


================================================
FILE: src/components/utils.js
================================================
import React, { useState, useEffect, useRef } from 'react';
import {useSelector} from 'react-redux';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import ReactPlayer from 'react-player';

import Slider from '@mui/material/Slider';

import { dispatchAction } from 'store/actions';
import * as FaIcons from '@fortawesome/free-solid-svg-icons';
import * as FaRegIcons from '@fortawesome/free-regular-svg-icons';
import * as MUIcons from '@mui/icons-material';
import * as AllIcons from 'components/icons';

const {round, floor, random, min, max, abs} = Math;

export const MaterialIcon = (props) => {
  var icon = props.mui;
  if (props.out) icon += "Outlined";
  if (props.round) icon += "Rounded";
  if (props.twotone) icon += "TwoTone";
  if (props.sharp) icon += "Sharp";

  // const Icon = <div></div>
  const MuiIcon = MUIcons[icon];
  return (
    <MuiIcon
      className={
        props.flip? "flip-true": "" +
        (props.invert? " invert-true": "") +
        (props.rounded? " rounded-true": "")
      } style={{
        width: props.w,
        height: props.h || props.w,
        color: props.color,
        margin: props.margin,
      }}
    />
  );
};

export const Icon = (props) => {
  var src = `img/icon/${props.ui ? "ui/" : ""}${props.src}`;
  if (props.src && !props.src.includes(".")) {
    src += ".png";
  }

  if (props.ext || (props.src && props.src.startsWith("http"))) {
    src = props.src;
  }

  var dataset = {}
  Object.entries(props).forEach(([key, value]) => {
    if(key.includes("data-")){
      dataset[key] = value;
    }
  });

  var classname = `uicon prtclk ${props.className || ""}`.trim()
  var styles = {
    borderRadius: props.radii
  }

  const label = props.label!=null && <span>{props.label}</span>

  if (props.fafa) {
    return (
      <div className={classname} {...dataset} style={styles}
        onClick={props.onClick || (props.action && dispatchAction)}
        data-action={props.action} data-payload={props.payload}>
        <FontAwesomeIcon
          data-flip={props.flip}
          data-invert={props.invert}
          data-rounded={props.rounded}
          style={{
            width: props.w,
            height: props.h || props.w,
            color: props.color,
            margin: props.margin
          }}
          icon={!props.reg ? FaIcons[props.fafa] : FaRegIcons[props.fafa]}
        />
        {label}
      </div>
    );
  } else if (props.mui) {
    return (
      <div className={classname} {...dataset} style={styles}
        onClick={props.onClick || (props.action && dispatchAction)}
        data-action={props.action} data-payload={props.payload}>
        <MaterialIcon {...props} />
        {label}
      </div>
    );
  }else if (props.icon) {
    var CustomIcon = AllIcons[props.icon];
    return (
      <div className={classname} {...dataset} style={styles}
        onClick={props.onClick || (props.action && dispatchAction)}
        data-action={props.action} data-payload={props.payload}>
        <CustomIcon {...props} />
        {label}
      </div>
    );
  } else {
    return (
      <div className={classname} data-active={props.active} {...dataset}
        data-action={props.action} data-payload={props.payload}
        onClick={props.onClick || dispatchAction} style={styles}>
        <img width={props.w} height={props.h} data-flip={props.flip}
          data-invert={props.invert} data-rounded={props.rounded}
          src={src} style={{ margin: props.margin}}
          alt={props.alt || ""}/>
          {label}
      </div>
    );
  }
};

export const Image = (props) => {
  var src = `img/${(props.dir?props.dir+"/":"")+props.src}`;
  if (props.src && !props.src.includes(".")) {
    src += ".png";
  }

  if (props.ext || (props.src && props.src.startsWith("http"))) {
    src = props.src;
  }

  const errorHandler = (e)=>{
    if(props.err) e.target.src = props.err
  }

  var dataset = {}
  Object.entries(props).forEach(([key, value]) => {
    if(key.includes("data-")){
      dataset[key] = value;
    }
  });

  return (
    <div className={`imageCont prtclk ${props.className||''}`} id={props.id} style={{
      backgroundImage: props.back && `url(${src})`
    }} data-back={props.back} onClick={props.onClick || (props.action && dispatchAction)}
      data-action={props.action} data-payload={props.payload} {...dataset}>
        {!props.back?<img
          width={props.w}
          height={props.h}
          data-free={props.free}
          data-var={props.var}
          loading={props.lazy && "lazy"}
          src={src} alt="" onError={errorHandler}/>:null}
    </div>
  )
}

export const isValidURL = (str)=>{
  var res = str.match(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
  return (res !== null)
};

const formatseconds = (sec)=>{
  if (!sec) return "00:00";
  var res = floor(sec / 60);
  res += ':';
  sec %= 60;
  if (sec < 10) res += "0";
  res += sec;

  return res;
}

export const Video = (props) => {
  const [play, setPlay] = useState(props.autoplay);
  const [prog, setProg] = useState(0); // time elapsed
  const [perProg, setPerProg] = useState(0); // time elapsed in %
  const vidplayer = useRef();

  var src = `img/${(props.dir?props.dir+"/":"")+props.src}`;
  if (props.src && !props.src.includes(".")) src += ".mp4";

  if (props.ext || (props.src && props.src.includes("http"))) {
    src = props.src;
  }

  const className = `vidCont ${(props.inactive || props.clickToggle)?'prtclk':''} ${props.className||''}`.trim()
  var dataset = {}
  Object.entries(props).forEach(([key, value]) => {
    if(key.includes("data-")){
      dataset[key] = value
    }
  });

  const handlePause = (e) => setPlay(false)
  const handlePlay = (e) => setPlay(true)
  const togglePlay = (e) => setPlay(!play)

  const handleProg = (e)=>{
    setProg(floor(e.playedSeconds))
    setPerProg(e.played)
  }

  const handleSliderChange = (e)=>{
    if(!vidplayer.current) return

    var ip = e.target.value/100
    vidplayer.current.seekTo(ip, 'fraction')
    setPerProg(ip);
    setProg(floor(ip*vidplayer.current.getDuration()));
  }

  return (
    <div className={className} id={props.id} onClick={ props.onClick ||
        (props.action && dispatchAction) || (props.clickToggle && togglePlay)
      } data-action={props.action} data-payload={props.payload} {...dataset} tabIndex="1">
        {!props.playIcon && play &&
          <Icon className="play-icon" mui="Pause" round w={48} onClick={handlePause}/>}
        {!props.playIcon && !play &&
          <Icon className="play-icon opacity-100" mui="PlayArrow" round w={48} onClick={handlePlay}/>}
        {props.playIcon}
        <ReactPlayer className="react-video" url={src}
          width={props.w || "auto"} height={props.h || "auto"} ref={vidplayer}
          playing={props.play || play} volume={props.muted ? 0 : 1} controls={props.controls}
          onPlay={props.onPlay || handlePlay} onPause={props.onPause || handlePause}
          onProgress={props.onProgress || handleProg} onEnded={props.onEnded || handlePause}/>
        {props.cstmctrl && (
          <div className="video-control-container">
            <span className="prog-text">{formatseconds(prog)}</span>
            <Slider size="small"
              className="video-progress"
              value={perProg*100}
              defaultValue={0}
              onChange={handleSliderChange}
            />
            <span className="prog-text">{formatseconds(floor(prog/perProg))}</span>
          </div>
        )}
    </div>
  )
}

export const LazyComponent = ({ show, children }) => {
  const [loaded, setLoad] = useState(false);

  useEffect(() => {
    if (show && !loaded) setLoad(true);
  }, [show]);

  return show || loaded ? <>{children}</> : null;
};


================================================
FILE: src/components/widgets/index.js
================================================
import React, { useState, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import Calendar from 'react-calendar'

import {Icon} from 'components/utils'
import {dispatchAction, dispatchAct, openAppPage} from 'store/actions';
import './widget.scss'

const ClockDate = (props)=>{
  const date = useSelector((state) => state.global.date);
  var datestring = new Date(date.year, date.month, date.day).toLocaleString("en-us",{
    weekday: "short", day: "numeric", month: props.datemin?"short":"long"
  });

  var classNameString = props.className || ""
  return <div className={"clock-date " + classNameString}>{datestring}</div>
}

export const GoogleSearch = () => {

  const handleClick = () => {
    openAppPage("google","google.search")
  }

  return (
    <div className="google-search-container press-in" onClick={handleClick}>
      <div className="google-input-container flex justify-between items-center">
        <Icon src="apps/google" w={26}/>
        <Icon src="apps/mic" w={24}/>
      </div>
    </div>
  );
};

export const AnalogClock = () => {
  const time = useSelector((state) => state.global.time);

  const calculateAngles = ()=>{
    var hr = time.hours >= 12 ? time.hours - 12 : time.hours,
        mn = time.minutes

    hr *= 30
    hr += mn/2
    mn *= 6
    return [hr, mn]
  }

  return (
    <div className="analog-clock-container">
      <div className="clock-body">
        <div className="minute-container" style={{
          transform: `rotateZ(${calculateAngles()[1]}deg)`
        }}>
          <div className="minute-hand"></div>
        </div>
        <div className="hour-container" style={{
          transform: `rotateZ(${calculateAngles()[0]}deg)`
        }}>
          <div className="hour-hand"></div>
        </div>
        <div className="clock-center"></div>
      </div>
      <ClockDate/>
    </div>
  );
};

export const MinimalClock = () => {
  const time = useSelector((state) => state.global.time);

  return (
    <div className="digital-time-container">
      <div className="flex items-baseline">
        <div className="text-4xl">{time.hours}:{time.minutes}</div>
        <div className="text-xs ml-1">{time.abb}</div>
      </div>
      <ClockDate className="text-sm mt-2"/>
    </div>
  );
};

export const MinimalVertClock = () => {
  const time = useSelector((state) => state.global.time);
  const fillZero = (x)=>{
    return (x<9?"0":"") + x
  }

  return (
    <div className="vert-time-container">
      <div className="flex flex-col">
        <div className="text-6xl font-thin">{fillZero(time.hours)}</div>
        <div className="text-6xl font-thin">{time.minutes}</div>
      </div>
      <ClockDate className="text-sm mt-2" datemin/>
    </div>
  );
};

export const DayCountdown = ()=>{
  return (
    <div className="countdown-container">
      <div className="countdown-topic text-xs">Mom's B'day</div>
      <div className="quant-left text-2xl">215</div>
      <div className="quant-unit text-xs">days left</div>
    </div>
  )
}

export const WideCalender = ()=>{
  const date = useSelector((state) => state.global.date);
  var datemonth = new Date(date.year, date.month, date.day).toLocaleString("en-us",{month: "long"});

  const formatShortWeekday = (label, date)=>{
    return date.toLocaleString("en-us",{weekday: "short"})[0]
  }

  return (
    <div className="calender-container medScroll">
      <div className="calendar-month text-xl pb-1">{datemonth}</div>
      <div className="flex">
        <Calendar
          showNavigation={false}
          showNeighboringMonth={false}
          formatShortWeekday={formatShortWeekday}/>

        <div className="calender-events">
          <div className="event-container">
            <div className="event-name text-sm">Disneyland</div>
            <div className="event-time text-xs">All day</div>
          </div>
          <div className="event-container">
            <div className="event-name text-sm">Meeting</div>
            <div className="event-time text-xs">11:00 AM - 2:00 PM</div>
          </div>
          <div className="event-container">
            <div className="event-name text-sm">Business</div>
            <div className="event-time text-xs">3:30 PM - 8:00 PM</div>
          </div>
        </div>
      </div>
    </div>
  )
}

export const WideWeather = ()=>{
  const date = useSelector((state) => state.global.date);
  const weather = useSelector((state) => state.widget.weather);
  var datemonth = new Date(date.year, date.month, date.day).toLocaleString("en-us",{month: "long"});
  var iconurl = "https://www.metaweather.com/static/img/weather/"

  return (
    <div className="weather-container medScroll">
      <div className="city-container">
        <Icon mui="LocationOn" w={18}/>
        <span>{weather.city}</span>
      </div>
      <ClockDate className="weather-date"/>
      <div className="main-weather">
        <Icon src={iconurl+weather.icon+".svg"} ext w={48}/>
        <div className="today-temperature">{weather.temperature}°</div>
      </div>
      <div className="weather-pred-container">
        {weather && weather.predictions.map((pred, i)=>{
          return(
            <div className="weather-pred" key={i}>
              <Icon src={iconurl + pred.icon + ".svg"} ext w={32}/>
              <div className="pred-day">{pred.day}</div>
            </div>
          )
        })}
      </div>
    </div>
  )
}

export const ShortWeather = ()=>{
  const date = useSelector((state) => state.global.date);
  const weather = useSelector((state) => state.widget.weather);
  var datemonth = new Date(date.year, date.month, date.day).toLocaleString("en-us",{month: "long"});
  var iconurl = "https://www.metaweather.com/static/img/weather/"

  return (
    <div className="weather-container short-weather-container medScroll">
      <div className="main-weather">
        <Icon src="weather/clear-sky" w={56}/>
        <div className="today-temperature">{weather.temperature}°</div>
      </div>
      <div className="city-container">
        <Icon mui="LocationOn" w={18}/>
        <span>{weather.city}</span>
      </div>
      <ClockDate className="weather-date"/>
    </div>
  )
}


================================================
FILE: src/components/widgets/widget.scss
================================================
.google-search-container {
  width: 100%;
  padding: 0 1em;
  display: flex;
}

.google-input-container {
  background: #fefefe;
  border-radius: 200px;
  padding: 0.8em;
  flex-grow: 1;
}

.analog-clock-container {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;

  .clock-body {
    width: 60%;
    aspect-ratio: 1 / 1;
    max-width: 160px;
    max-height: 160px;
    background: #fafafa;
    border-radius: 50%;
    position: relative;
    box-shadow: 0 -4px 16px rgba(36, 36, 36, 0.1), 0 4px 16px rgba(36, 36, 36, 0.1);

    .clock-center {
      position: absolute;
      background: #333;
      inset: 50%;
      width: 8px;
      height: 8px;
      margin-top: -4px;
      margin-left: -4px;
      border-radius: 50%;
    }

    .hour-container,
    .minute-container {
      width: 100%;
      height: 100%;
      position: absolute;
      inset: 0;
      display: grid;
      place-items: center;
    }

    .hour-hand,
    .minute-hand {
      width: 4px;
      border-radius: 8px;
      transform: translateY(-50%);
    }

    .hour-hand {
      height: 20%;
      background: #444;
    }

    .minute-hand {
      height: 40%;
      background: #888;
    }
  }

  .clock-date {
    font-size: 0.9em;
    margin-top: 0.5em;
    color: var(--med-txt);
  }
}

.digital-time-container,
.vert-time-container {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  color: var(--med-txt);
}

.vert-time-container > div {
  width: 100%;
  padding: 0 1em;
}

.cell-container {
  // box-sizing: border-box;
  // border: solid 1px red;
}

.calender-container {
  box-sizing: border-box;
  background: #fefefe;
  border-radius: 1em;
  padding: 0.6em 1em;
  position: absolute;
  inset: 0.5em 1em;
  overflow-x: hidden;
  overflow-y: scroll;

  .react-calendar {
    width: 66%;

    .react-calendar__month-view__days__day--weekend {
      color: red;
    }

    .react-calendar__tile--now {
      color: #fefefe;
      position: relative;
      transform: translateX(-36%);

      abbr {
        position: absolute;
        inset: 0 0.3em;
        text-align: center;
        font-weight: 400;
        background: #444;
        border-radius: 6px;
      }
    }

    abbr {
      display: block;
      text-decoration: none;
      font-size: 0.72em;
      font-weight: 600;
      padding: 0.2em 0;
      text-align: left;
    }
  }

  .calender-events {
    display: grid;
    grid-template-rows: 1fr 1fr 1fr;
    width: 33%;

    .event-container {
      color: var(--med-txt);
      border: 0 solid var(--comp-txt);
      border-left-width: 2px;
      padding-left: 0.5em;
      position: relative;

      &::after {
        content: "";
        position: absolute;
        top: -2px;
        left: -4px;
        width: 6px;
        height: 6px;
        border-radius: 20px;
        background: #277beb;
      }

      .event-time {
        height: 16px;
        color: var(--sat-txt);
        font-weight: 300;
        overflow: hidden;
      }
    }
  }
}

.countdown-container {
  position: absolute;
  inset: 0.5em 1em;
  text-align: center;
  background: #fefefe;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0.5em;
  border-radius: 1em;
}

.weather-container {
  box-sizing: border-box;
  background: #fefefe;
  border-radius: 1em;
  padding: 0.6em 1em;
  position: absolute;
  inset: 0.5em 1em;
  display: flex;
  flex-direction: column;
  align-items: center;
  color: var(--med-txt);
  letter-spacing: 1px;
  overflow-x: hidden;
  overflow-y: scroll;

  .city-container {
    display: flex;

    span {
      font-size: 1rem;
    }
  }

  .weather-date {
    font-size: 0.9em;
    font-weight: 100;
    color: var(--gray-txt);
  }

  .main-weather {
    display: flex;
    align-items: center;
    flex-grow: 1;
  }

  .today-temperature {
    font-size: 3em;
    font-weight: 100;
    margin-left: 0.3em;
  }

  .weather-pred-container {
    display: flex;
    justify-content: space-between;

    .weather-pred {
      margin: 0 0.8em;
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    .pred-day {
      font-size: 0.8em;
    }
  }
}

.short-weather-container {
  .today-temperature {
    font-size: 2.5em;
    margin-left: 0.1em;
  }

  .main-weather .uicon {
    height: 100%;
  }
}


================================================
FILE: src/containers/apps/google/google.scss
================================================
.google-home, .google-spage, .search-frame {
  padding: 1em 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  box-sizing: border-box;
}

.google-land, .google-spage{
  .namelogo {
    width: 100%;
  }

  .search-box-container {
    width: 96%;
    padding: 0.5em 1em;
    margin-top: 2em;
    margin-right: 2%;
    margin-bottom: 1em;
    border-radius: 2em;
    box-shadow: 1px 2px 8px rgba(74, 71, 71, 0.15);
  }

  .search-input {
    display: flex;
    align-items: center;

    svg {
      color: var(--light-txt);
    }

    .search-inp {
      flex-grow: 1;

      input {
        padding: 0.25em 0.5em;
      }

      fieldset {
        display: none;
      }
    }
  }

  .news-container {
    padding: 0 0.5em;

    .news-box {
      margin: 0.6em 0;
      color: var(--mid-txt);
    }

    .news-title {
      font-size: 0.96em;
      color: var(--med-txt);
    }

    .imageCont {
      border-radius: 0.8em;
      overflow: hidden;
      margin-bottom: 0.5em;
    }
  }
}

.google-spage{
  .search-box-container{
    box-sizing: border-box;
    padding: 0.5em 0.25em;
    margin: 0;
    width: 90%;
    border: 0 solid var(--comp-txt);
    border-radius: 0;
    border-bottom-width: 2px;
    box-shadow: none;

    .search-inp input{
      padding: 0.25em 1em;
    }
  }

  .search-suggestions{
    flex-grow: 1;
    width: 100%;
    overflow-y: scroll;
    margin-top: 0.5em;
    color: var(--light-txt);

    .trending-search{
      margin-top: 1em;

      .sugg-title{
        margin-bottom: 0.5em;
      }
    }

    .sugg-title{
      padding: 0 0.5em;
      margin: 0 1.5em;
      font-size: 0.68em;
      letter-spacing: 1px;
      font-weight: 600;
    }

    .sugg-box{
      display: flex;
      padding: 0.5em 1.5em;

      span{
        margin-left: 1em;
        font-weight: 500;
        color: var(--txt-col);
      }
    }
  }
}

.search-frame{
  padding: 0;

  iframe{
    width: 100%;
    height: 100%;
  }
}


================================================
FILE: src/containers/apps/google/index.js
================================================
import React, {useState, useEffect} from 'react'
import {useSelector, useDispatch} from 'react-redux'

import TextField from '@mui/material/TextField'

import {Icon, Image} from 'components/utils'
import {dispatchAction, dispatchAct} from 'store/actions'

import './google.scss'
import newsdata from './news.json'

export const GoogleApp = () => {
  const app = useSelector(state => state.home.apps.google || {})
  const home = useSelector(state => state.home)
  const show = home.ishome==false && home.stack.at(-1)==app.payload
  const pagetree = app && app.pagetree || {
    "main": {
      "search" : {},
      "result" : {}
    }
  }

  useEffect(()=>{
    if(app && !app.pagetree){
      dispatchAct({type: "home/setApp", payload: {
        id: app.payload,
        data: {
          ... app,
          pagetree: pagetree,
          path: ['main']
        }
      }})
    }
  }, [app])

  return <AppContainer app={app} show={show}/>
}

const AppContainer = ({app, show}) => {
  const [searchtxt, setTxt] = useState('')
  const clstring = `${app.payload}-wrapper`
  const path = app.path || ['main']

  const handlleReset = () => setTxt('')
  const openResults = () => dispatchAct({
    type: "home/navApp",
    payload: "google.result"
  })

  const handleTxtChange = (e) => setTxt(e.target.value)
  const handleSuggClk = (e) => {
    setTxt(e.target.innerText)
    openResults()
  }

  const handleSearch = (e) => {
    if(e.key == "Enter" && searchtxt.length) openResults()
  }

  const checkstate = (comp) => {
    return path.includes(comp) ? (
      path.at(-1) == comp ? 1 : 2
    ) : 0;
  }

  const sdata = {
    "sugg": ["videos", "old songs", "hd wallpapers", "love", "new movies", "new songs", "cool photos", "images"],
    "trend": ["T20 World Cup", "Squid Game", "Afghanistan", "Ethereum Price", "Baked oats", "Battlefield 2042", "Black Widow"]
  }

  return (
    <div className={"app-wrapper "+clstring} id={clstring} data-open={show}>
      <div className="app-inner-wrapper gg-inner-wrapper">
        {checkstate('main')==1 && (
          <div className="google-home overflow-y-scroll h-full">
            <div className="google-land flex flex-col items-end">
              <Image className="rounded rounded-full" src="blue.jpg" w={28}/>
              <Image className="namelogo" dir="asset/other" src="googlename" w={120}/>
              <div className="search-box-container press-in prtclk" onClick={dispatchAction}
                data-action="home/navApp" data-payload="google.search">
                <div className="search-input">
                  <Icon mui="Search" w={24}/>
                  <TextField className="search-inp" placeholder="Search..." disabled/>
                  <Icon src="apps/mic" w={20}/>
                </div>
              </div>
              <div className="news-container">
                {newsdata.articles.map((article,i) => {
                  return (
                    <div className="news-box" key={i}>
                      <Image src={article.urlToImage}/>
                      <div className="news-title txt-ovf wb-line-3">
                        {article.title}
                      </div>
                      <div className="flex mt-2 px-1 mb-8">
                        <Icon className="rounded-full" src={new URL(article.url).origin + "/favicon.ico"} w={14}/>
                        <span className="text-xs light-txt font-thin ml-2">{article.source.name}</span>
                        <div className="flex-grow"></div>
                        <Icon className="mr-4" mui="Share" w={16}/>
                        <Icon mui="MoreVert" w={18}/>
                      </div>
                    </div>
                  )
                })}
              </div>
            </div>
          </div>
        )}
        {checkstate('search')==1 && (
          <div className="google-spage">
            <div className="search-box-container">
              <div className="search-input">
                <Icon src="apps/google" w={26}/>
                <TextField className="search-inp" placeholder="Search..." autoFocus
                  value={searchtxt} onChange={handleTxtChange} onKeyPress={handleSearch}/>
                  {searchtxt.length ? (
                    <Icon mui="Close" w={20} onClick={handlleReset}/>
                  ) : <Icon src="apps/mic" w={20}/>}
              </div>
            </div>
            <div className="search-suggestions thinScroll">
              <div className="try-search">
                <div className="sugg-title">TRY SEARCHING FOR</div>
                {sdata.sugg.map((x,i) => (
                  <div className="sugg-box active-dark-lit xlit prtclk"
                    onClick={handleSuggClk} key={i}>
                    <Icon mui="Search" w={20}/>
                    <span>{x}</span>
                    <div className="flex-grow"></div>
                    <Icon mui="CallMade" flip w={20}/>
                  </div>
                ))}
              </div>
              <div className="trending-search">
                <div className="sugg-title">TRENDING SEARCHES</div>
                {sdata.trend.map((x,i) => (
                  <div className="sugg-box active-dark-lit xlit prtclk"
                    onClick={handleSuggClk} key={i}>
                    <Icon mui="TrendingUp" w={20}/>
                    <span>{x}</span>
                  </div>
                ))}
              </div>
            </div>
          </div>
        )}
        {checkstate('result')==1 && (
          <div className="search-frame">
            <iframe src={'https://www.google.com/search?igu=1&q='+searchtxt} frameBorder="0"></iframe>
          </div>
        )}
      </div>
    </div>
  );
}


================================================
FILE: src/containers/apps/google/news.json
================================================
{
  "articles": [{
      "source": {
        "id": null,
        "name": "NDTV News"
      },
      "author": "NDTV Sports Desk",
      "title": "IPL 2022, SRH vs KKR Live Score: Kane Williamson, Rahul Tripathi Rebuild For SRH After Early Wicket - NDTV Sports",
      "description": "IPL 2022, SRH vs KKR Live Updates: Chasing a target of 176, SunRisers Hyderabad (SRH) lost both of their openers in the powerplay against Kolkata Knight Riders (KKR) in match 25 of IPL 2022 at the Brabourne Stadium in Mumbai.",
      "url": "https://sports.ndtv.com/ipl-2022/ipl-2022-srh-vs-kkr-live-score-updates-2891194",
      "urlToImage": "https://c.ndtvimg.com/2022-04/f6386o_kkr-bcci_625x300_15_April_22.jpg?im=FeatureCrop,algorithm=dnn,width=1200,height=675",
      "publishedAt": "2022-04-15T16:31:54Z",
      "content": "IPL 2022, SRH vs KKR Live Updates: Chasing a target of 176, SunRisers Hyderabad (SRH) lost both of their openers in the powerplay against Kolkata Knight Riders (KKR) in match 25 of IPL 2022 at the Br… [+1230 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "NDTV News"
      },
      "author": null,
      "title": "WHO Monitors Hepatitis Of Unknown Origin In UK Children - NDTV",
      "description": "The World Health Organization said Friday it was monitoring cases of hepatitis of unknown origin in dozens of children in Britain, some of whom required a liver transplant.",
      "url": "https://www.ndtv.com/world-news/who-monitors-hepatitis-of-unknown-origin-in-uk-children-2891847",
      "urlToImage": "https://i.ndtvimg.com/i/2017-09/hepatitis-action-plan-to-come-in-october_650x400_41505196638.jpg",
      "publishedAt": "2022-04-15T16:17:52Z",
      "content": "The infection mainly affected children aged under 10.\r\nLondon, United Kingdom: The World Health Organization said Friday it was monitoring cases of hepatitis of unknown origin in dozens of children i… [+1589 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "NDTV News"
      },
      "author": null,
      "title": "Karnataka Minister KS Eshwarappa, Named In Suicide Case, Resigns: Report - NDTV",
      "description": "After a meeting with Chief Minister Basavaraj Bommai at his residence in Bengaluru, Mr Eshwarappa submitted his resignation.",
      "url": "https://www.ndtv.com/india-news/karnataka-minister-ks-eshwarappa-named-in-suicide-case-resigns-2891676",
      "urlToImage": "https://c.ndtvimg.com/2022-04/88a73te8_ks-eswarappa-with-basavaraj-bommai-650_650x400_15_April_22.jpg",
      "publishedAt": "2022-04-15T15:46:11Z",
      "content": "\"I am also confident that I will come out clean,\" KS Eshwarappa said before resigning.\r\nBengaluru: BJP stalwart and Karnataka minister KS Eshwarappa, caught in a huge controversy following allegation… [+3084 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Indian Express"
      },
      "author": "Express News Service",
      "title": "At Delhi BJP office, 8 accused of attacking Kejriwal residence are garlanded - The Indian Express",
      "description": "AAP MLA Atishi lashed out at the BJP, saying they had sent a message to its workers across the country that they will be felicitated if they indulge in “hooliganism and vandalism”.",
      "url": "https://indianexpress.com/article/cities/delhi/delhi-bjp-office-8-accused-attacking-kejriwal-residence-are-garlanded-7871100/",
      "urlToImage": "https://images.indianexpress.com/2022/04/BJYM.jpg",
      "publishedAt": "2022-04-15T15:35:42Z",
      "content": "Eight men who were arrested and later released on bail in connection with the attack on Chief Minister Arvind Kejriwals residence were on Thursday felicitated at the Delhi BJP office by the partys st… [+2134 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Indian Express"
      },
      "author": "Man Aman Singh Chhina",
      "title": "Explained: Why is the Black Sea crucial to Russia, and the loss of the ‘Moskva’ a significant blow - The Indian Express",
      "description": "The sinking of the warship Moskva, the 600-foot, 12,500-tonne flagship of the Russian Black Sea Fleet — whether due to a Ukrainian missile strike or, as Russia claims, a fire on board — is a serious setback for Russia.",
      "url": "https://indianexpress.com/article/explained/everyday-explainers/explained-russia-warship-moksva-black-sea-7870946/",
      "urlToImage": "https://images.indianexpress.com/2022/04/Russia-warship-ex.jpg",
      "publishedAt": "2022-04-15T15:35:22Z",
      "content": "The sinking of the warship Moskva, the 600-foot, 12,500-tonne flagship of the Russian Black Sea Fleet whether due to a Ukrainian missile strike or, as Russia claims, a fire on board is a serious setb… [+6665 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Indian Express"
      },
      "author": "Entertainment Desk",
      "title": "KGF 2 box office collection day 1: Yash-starrer registers biggest-ever opening day in Hindi belt, earns Rs 134.5 crore in India - The Indian Express",
      "description": "Yash's KGF 2 has received a massive opening across south Indian states. While the film dominated the box office in Karnataka, it also received a huge launch in Tamil Nadu, Kerala and the Telugu states.",
      "url": "https://indianexpress.com/article/entertainment/regional/kgf-chapter-2-box-office-day-1-yash-starrer-creates-history-7870297/",
      "urlToImage": "https://images.indianexpress.com/2022/04/kgf-box-office.jpg",
      "publishedAt": "2022-04-15T15:09:24Z",
      "content": "Kannada movie KGF: Chapter 2, starring Yash in the lead role, has stormed the Indian box office. Thanks to the huge demand created by the film’s first part in 2018, the sequel reaped the benefit by o… [+2922 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "PINKVILLA"
      },
      "author": "Pinkvilla Desk",
      "title": "Ranbir Kapoor-Alia Bhatt Wedding: Actress skipped chooda ceremony due to her Hollywood debut; Reports - PINKVILLA",
      "description": "Alia Bhatt reportedly didn2019t do the traditional chooda ceremony due to her big Hollywood debut with Gal Gadot.",
      "url": "https://www.pinkvilla.com/entertainment/news/ranbir-kapoor-alia-bhatt-wedding-actress-skipped-chooda-ceremony-due-her-hollywood-debut-reports-1068918",
      "urlToImage": "https://www.pinkvilla.com/files/styles/fbimagesection/public/ranbir_alia_social_0.jpg?itok=RiRlyEBC",
      "publishedAt": "2022-04-15T14:21:48Z",
      "content": "Tinsel town's favourite couple, Alia Bhatt and Ranbir Kapoor got married on April 14th at the Yeh Jawaani Hai Deewani actor's Vastu residence in Bandra, Mumbai after dating for five years. The couple… [+1423 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "NDTV News"
      },
      "author": null,
      "title": "India Receives S-400 Overhauled Engines, Spares From Russia: Report - NDTV",
      "description": "Amid the ongoing war between Russia and Ukraine, India has received the simulators and other equipment from Moscow for the training squadron of the S-400 Triumf air defence missile system.",
      "url": "https://www.ndtv.com/india-news/india-russia-s-400-deal-india-russia-defence-deal-russia-ukraine-war-india-recieves-s-400-overhauled-engines-spares-from-russia-report-2891267",
      "urlToImage": "https://i.ndtvimg.com/i/2016-10/s-400-missiles-reuters_650x400_51476365973.jpg",
      "publishedAt": "2022-04-15T13:02:56Z",
      "content": "India-Russia S-400 Missile System: The supplies from Russia included overhauled fighter engines.\r\nNew Delhi: Amid the ongoing war between Russia and Ukraine, India has received the simulators and oth… [+2188 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Livemint"
      },
      "author": "Livemint",
      "title": "Tata Sons Effects Major Reshuffle In Air India's Top Management | Mint - Mint",
      "description": "Aggarwal, who is also Senior Vice President at Tata Sons, replaces Air India veteran Meenakshi Malik, while Tripathi, who was Vice President of Human Resources at Tata Steel from 2012 to 2021, succeeds AI's Amrita Sharan.",
      "url": "https://www.livemint.com/companies/news/tata-sons-effects-major-reshuffle-in-air-india-s-top-management-11650026050123.html",
      "urlToImage": "https://images.livemint.com/img/2022/04/15/600x338/Air_India_1650026093350_1650026093656.jpg",
      "publishedAt": "2022-04-15T12:36:06Z",
      "content": "Tata Sons has effected a major reshuffle in the top management of Air India, news agency PTI reported on Friday.\r\nThe group chairman N Chandrasekaran issued the order, according to which, Air India v… [+1836 chars]"
    },
    {
      "source": {
        "id": "the-times-of-india",
        "name": "The Times of India"
      },
      "author": "Aishwarya Dharni",
      "title": "Indian-Origin NASA Astronaut Raja Chari All Set To Return To Earth After Six Months In Space - Indiatimes.com",
      "description": "After spending six months in microgravity, astronaut Raja Chari is all set to return home. The Crew-3 astronauts who docked to the International Space Station in November last year will return to planet Earth later this month. NASA astronauts Raja Chari, Tom …",
      "url": "https://www.indiatimes.com/trending/social-relevance/indian-american-astronaut-raja-chari-to-return-home-567090.html",
      "urlToImage": "https://im.indiatimes.in/content/2022/Apr/raja-chari_6259659bac146.png",
      "publishedAt": "2022-04-15T12:25:40Z",
      "content": "After spending six months in microgravity, astronaut Raja Chari is all set to return home. The Crew-3 astronauts who docked to the International Space Station in November last year will return to pla… [+2049 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Livemint"
      },
      "author": "Pooja Sitaram Jaiswar",
      "title": "Rakesh Jhunjhunwala Raises Stake In This Infra Stock During Q4 | Mint - Mint",
      "description": "The latest shareholding data of NCC on exchanges showed that Rekha Jhunjhunwala's holding in the company now stands at 1.6 crore equity shares or 2.62% as of March 2022.",
      "url": "https://www.livemint.com/market/stock-market-news/rakesh-jhunjhunwala-raises-stake-in-this-infra-stock-during-q4-11650024996162.html",
      "urlToImage": "https://images.livemint.com/img/2022/04/15/600x338/5244bf12-7a11-11ec-9eea-9c1e2652c262_1644643836353_1650025079879.jpg",
      "publishedAt": "2022-04-15T12:22:15Z",
      "content": "Ace investor Rakesh Jhunjhunwala has increased shareholding in the construction and engineering company, NCC Limited, through his wife's portfolio Rekha Jhunjhunwala. The investor who is referred to … [+3686 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "NDTV News"
      },
      "author": null,
      "title": "Russia Pledges More Missile Attacks On Kyiv After Moskva Flagship Sinks - NDTV",
      "description": "Moscow warned Friday it would step up missile attacks on Kyiv in response to what it said were sorties across the border, the day after its Black Sea naval flagship sank.",
      "url": "https://www.ndtv.com/world-news/russia-ukraine-war-kyiv-missile-attacks-russia-pledges-more-missile-attacks-on-kyiv-after-moskva-flagship-sinks-2890987",
      "urlToImage": "https://c.ndtvimg.com/2022-03/2171rpvg_shelling-podil-districtkyivreuters-_625x300_21_March_22.jpg",
      "publishedAt": "2022-04-15T11:33:21Z",
      "content": "Russia-Ukraine War: Russia attacked a factory near Kyiv where Neptune missiles were manufactured. (File)\r\nKyiv: Moscow warned Friday it would step up missile attacks on Kyiv in response to what it sa… [+5437 chars]"
    },
    {
      "source": {
        "id": "the-times-of-india",
        "name": "The Times of India"
      },
      "author": "PTI",
      "title": "If harmed, India will not spare anyone, says Rajnath Singh in a strong message to China - Economic Times",
      "description": "Singh, in his address to the Indian-American community in San Francisco, also sent a subtle message to the US that New Delhi does not believe in a diplomacy of  zero-sum game  and its relationship with one country cannot be at the expense of the other.",
      "url": "https://economictimes.indiatimes.com/news/defence/if-harmed-india-will-not-spare-anyone-says-rajnath-singh-in-a-strong-message-to-china/articleshow/90863870.cms",
      "urlToImage": "https://img.etimg.com/thumb/msid-90863903,width-1070,height-580,imgsize-27366,overlay-etdefence/photo.jpg",
      "publishedAt": "2022-04-15T11:32:00Z",
      "content": "In a strong message to China, Defence Minister Rajnath Singh has said that if harmed, India will not spare anyone, as he asserted that India under Prime Minister Narendra Modi has emerged as a powerf… [+5028 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "India Today"
      },
      "author": null,
      "title": "Closing schools would be last option, says Manish Sisodia amid rising Covid cases in Delhi - India Today",
      "description": "The Delhi Directorate of Education issued an advisory for students on Friday, following reports of rising Covid cases. \"Partial closure of schools would be implemented if required,\" said deputy CM Manish Sisodia.",
      "url": "https://www.indiatoday.in/coronavirus-outbreak/story/closing-schools-would-be-last-option-says-manish-sisodia-amid-reports-of-rising-covid-cases-in-delhi-1937939-2022-04-15",
      "urlToImage": "https://akm-img-a-in.tosshub.com/indiatoday/images/story/202204/Delhi_students_Covid_PTI-647x363.jpeg?gAOfpQ3bwoIzw92GXQJmhvLSnALaSyXL",
      "publishedAt": "2022-04-15T11:18:31Z",
      "content": "As Covid cases are again on the rise, the Delhi Directorate of Education issued guidelines for students on Friday. \"Closing schools would be the last option. Partial closure would be implemented if r… [+1557 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Iplt20.com"
      },
      "author": null,
      "title": "Deepak Chahar ruled out of TATA IPL 2022, Harshit Rana joins KKR as a replacement for Rasikh Salam - IPLT20.com",
      "description": "Deepak Chahar ruled out of TATA IPL 2022, Harshit Rana joins KKR as a replacement for Rasikh Salam",
      "url": "https://www.iplt20.com/news/3764/deepak-chahar-ruled-out-of-tata-ipl-2022-harshit-rana-joins-kkr-as-a-replacement-for-rasikh-salam",
      "urlToImage": "https://assets.iplt20.com/bcci/articles/1650020545_aa.jpeg",
      "publishedAt": "2022-04-15T11:08:49Z",
      "content": null
    },
    {
      "source": {
        "id": null,
        "name": "Pragativadi.com"
      },
      "author": "Pradeep Sahoo",
      "title": "iQOO Z6 Pro 5G India Launch Set To April 27; Snapdragon 778G 5G Chip & 66W Fast Charging Confirmed - Pragativadi",
      "description": "New Delhi: iQoo Z6 Pro 5G India launch date has been set for April 27 as the Chinese smartphone manufacturer announced. The handset will also feature the company’s VC liquid cooling technology and is claimed to offer the highest AnTuTu benchmark scor",
      "url": "https://pragativadi.com/iqoo-z6-pro-5g-india-launch-set-to-april-27-snapdragon-778g-5g-chip-66w-fast-charging-confirmed/",
      "urlToImage": "https://pragativadi.com/wp-content/uploads/2022/04/aaa.jpg",
      "publishedAt": "2022-04-15T11:04:21Z",
      "content": "New Delhi: iQoo Z6 Pro 5G India launch date has been set for April 27 as the Chinese smartphone manufacturer announced. The handset will also feature the companys VC liquid cooling technology and is … [+1414 chars]"
    },
    {
      "source": {
        "id": "the-times-of-india",
        "name": "The Times of India"
      },
      "author": "TIMESOFINDIA.COM",
      "title": "Twitter shareholder Saudi Prince rejects Elon Musk's offer; Tesla chief reacts - Times of India",
      "description": "International Business News: NEW DELHI: Saudi Prince Alwaleed bin Talal, who owns a major stake in social media platform Twitter, rejected Elon Musk's offer to acquire 100 per cen.",
      "url": "https://timesofindia.indiatimes.com/business/international-business/twitter-shareholder-saudi-prince-rejects-elon-musks-offer-tesla-chief-reacts/articleshow/90862756.cms",
      "urlToImage": "https://static.toiimg.com/thumb/msid-90862992,width-1070,height-580,imgsize-24050,resizemode-75,overlay-toi_sw,pt-32,y_pad-40/photo.jpg",
      "publishedAt": "2022-04-15T10:39:00Z",
      "content": "I don't believe that the proposed offer by @elonmusk ($54.20) comes close to the intrinsic value of @Twitter given https://t.co/PImKaphxuS\r\n— (@Alwaleed_Talal) 1649947559000"
    },
    {
      "source": {
        "id": null,
        "name": "India.com"
      },
      "author": "India.com Lifestyle Staff",
      "title": "7 Reasons Why You are Gaining Belly Fat and Tips to Reduce it - India.com",
      "description": "Make some changes to your lifestyle to help reverse the scale and feel better.",
      "url": "https://www.india.com/health/weight-loss-tips-7-reasons-why-you-gaining-belly-fat-and-tips-to-reduce-it-5339525/",
      "urlToImage": "https://static.india.com/wp-content/uploads/2022/04/pexels-andres-ayrton-6551059.jpg",
      "publishedAt": "2022-04-15T10:19:53Z",
      "content": "Has your waistline expanded since the pandemic began? Don’t worry, you are not alone. A lifestyle that confines you inside the four walls of a house, with limited movement, is bound to add inches to … [+4334 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Hindustan Times"
      },
      "author": "HT Tech",
      "title": "Top 5 Windows 11 features: If you are not using them, you’re missing out - HT Tech",
      "description": "Top 5 Windows 11 features: Windows 11 brought a wide range of changes and new additions, but these are arguably the best of them. Have you checked all of these out?",
      "url": "https://tech.hindustantimes.com/tech/news/top-5-windows-11-features-if-you-are-not-using-them-you-re-missing-out-71650017645967.html",
      "urlToImage": "https://images.hindustantimes.com/tech/img/2022/04/15/1600x900/Windows_11_(7)_1633403699297_1650017692498.jpg",
      "publishedAt": "2022-04-15T10:17:13Z",
      "content": "Top 5 Windows 11 features: Microsoft started rolling out Windows 11 starting October 2021 and promised a completely redesigned user-interface, new productivity tools and cool features that create a s… [+2581 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "News18"
      },
      "author": "Buzz Staff",
      "title": "'Alien Footprints': NASA Image of Martian Crater is a Treat for Astronomy Nerds - News18",
      "description": "NASA has released a new image of a crater on Mars.",
      "url": "https://www.news18.com/news/buzz/alien-footprints-nasa-image-of-martian-crater-is-a-treat-for-astronomy-nerds-4988995.html",
      "urlToImage": "https://images.news18.com/ibnlive/uploads/2022/04/martian-crater--165001301416x9.jpg",
      "publishedAt": "2022-04-15T09:14:40Z",
      "content": "NASA recently shared an image on its Instagram handle leaving the netizens completely stunned. The space agency has released a new image of a Mars crater. In the caption, NASA wrote, “Youre looking a… [+2101 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Indian Express"
      },
      "author": "Sports Desk",
      "title": "Joe Root steps down as England’s Test team captain - The Indian Express",
      "description": "Root was appointed as Sir Alastair Cook's successor in 2017, and holds the record for the most number of matches and wins as England Test captain.",
      "url": "https://indianexpress.com/article/sports/cricket/joe-root-resigns-as-englands-test-team-captain-7870558/",
      "urlToImage": "https://images.indianexpress.com/2022/03/joe-root.jpg",
      "publishedAt": "2022-04-15T08:28:50Z",
      "content": "Joe Root has stepped down as England Test captain following a run of poor results capped by dispiriting tours of Australia and the Caribbean.The 31-year-old was appointed as Alastair Cook’s successor… [+3209 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Indian Express"
      },
      "author": "Shubhra Gupta",
      "title": "Mai review: Sakshi Tanwar shines in Netflix series - The Indian Express",
      "description": "Mai review: Despite the elements that stretch our credulity, Sakshi Tanwar manages to hold our attention.",
      "url": "https://indianexpress.com/article/entertainment/web-series/mai-review-sakshi-tanwar-raima-sen-7870464/",
      "urlToImage": "https://images.indianexpress.com/2022/04/Mai-review-120.jpg",
      "publishedAt": "2022-04-15T07:48:09Z",
      "content": "Mai cast: Sakshi Tanwar, Vivek Mushran, Wamiqa Gabbi, Raima Sen, Prashant Narayanan, Ankur Ratan, Anant Vidhaat, Vaibhav Raj Gupta, Seema PahwaMai directors: Anshai Lal, Atul MongiaMai pulls off a to… [+4294 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "NDTV News"
      },
      "author": null,
      "title": "Moon Dust Collected By Neil Armstrong During Apollo 11 Mission Sells For $500,000 At Auction - NDTV",
      "description": "A minuscule amount of lunar dust collected by Neil Armstrong during Apollo 11, the mission that first put men on the Moon, has been sold for $504,375 at an auction on Wednesday.",
      "url": "https://www.ndtv.com/world-news/moon-dust-collected-by-neil-armstrong-during-apollo-11-mission-sells-for-500-000-at-auction-2890273",
      "urlToImage": "https://c.ndtvimg.com/2022-04/sa75jpj_moon-650_625x300_15_April_22.jpg",
      "publishedAt": "2022-04-15T06:54:03Z",
      "content": "NASA even went to court to fight for the ownership of the lunar dust.\r\nA minuscule amount of lunar dust collected by Neil Armstrong during the Apollo 11 mission, that first put men on the Moon, has b… [+2060 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "ESPN India"
      },
      "author": "Sam Marsden",
      "title": "Furious Barcelona seek answers after 20,000 Frankfurt fans flood Camp Nou - ESPN India",
      "description": "Barcelona's coach and players want to know how at least 20,000 Eintracht Frankfurt fans gained access to the home end at Camp Nou.",
      "url": "https://www.espn.in/football/story/4642402/furious-barcelona-seek-answers-after-20,000-frankfurt-fans-flood-camp-nou",
      "urlToImage": "https://a3.espncdn.com/combiner/i?img=%2Fphoto%2F2022%2F0414%2Fr999905_1296x729_16%2D9.jpg",
      "publishedAt": "2022-04-15T06:45:36Z",
      "content": "Barcelona coach Xavi Hernandez says his players want to know how at least 20,000 Eintracht Frankfurt fans gained access to the home end at Camp Nou on Thursday as his side were knocked out of the Eur… [+3569 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Indian Express"
      },
      "author": "Express News Service",
      "title": "Delhi: Cops remove saffron flags, banners put up by Hindu Sena near JNU campus - The Indian Express",
      "description": "Reacting to the police action, national president of Hindu Sena Vishnu Gupta said that by removing the flags, the police have disrespected the Constitution.",
      "url": "https://indianexpress.com/article/delhi/delhi-cops-remove-saffron-flags-banners-put-up-by-hindu-sena-near-jnu-campus-7870385/",
      "urlToImage": "https://images.indianexpress.com/2022/04/IMG-20220415-WA0017.jpg",
      "publishedAt": "2022-04-15T06:23:54Z",
      "content": "In protest against the violence that broke out at the Jawaharlal Nehru University (JNU) between two groups of students on Ram Navami, members of the Hindu Sena put up saffron flags and banners readin… [+2160 chars]"
    },
    {
      "source": {
        "id": "the-times-of-india",
        "name": "The Times of India"
      },
      "author": "PTI",
      "title": "Egypt approves India as wheat supplier: Piyush Goyal - Economic Times",
      "description": "“Indian farmers are feeding the world. Egypt approves India as a wheat supplier. Modi Govt. steps in as the world looks for reliable alternate sources for steady food supply. Our farmers have ensured our granaries overflow and we are ready to serve the world,…",
      "url": "https://economictimes.indiatimes.com/news/economy/foreign-trade/egypt-approves-india-as-wheat-supplier-piyush-goyal/articleshow/90858683.cms",
      "urlToImage": "https://img.etimg.com/thumb/msid-90858680,width-1070,height-580,imgsize-2041580,overlay-economictimes/photo.jpg",
      "publishedAt": "2022-04-15T05:21:00Z",
      "content": "Commerce and Industry Minister Piyush Goyal on Friday said Egypt, which is one of the largest importers of wheat from Ukraine and Russia, has approved India as a wheat supplier.\r\nThere is a sharp dec… [+2046 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "NDTV News"
      },
      "author": null,
      "title": "China Hesitates On Bailing Out \"Sinking Ships\" Sri Lanka, Pakistan - NDTV",
      "description": "Over the past few years, the U.S. has accused China of using \"debt diplomacy\" to make developing nations across the world more dependent on Beijing.",
      "url": "https://www.ndtv.com/world-news/china-hesitates-on-bailing-out-sinking-ships-sri-lanka-pakistan-2887151",
      "urlToImage": "https://c.ndtvimg.com/2022-04/ui5tc3fg_sri-lanka-protestsbloomberg-_625x300_14_April_22.jpg",
      "publishedAt": "2022-04-15T04:23:00Z",
      "content": "Sri Lanka President Gotabaya Rajapaksa is facing pressure from protesters to step down.\r\nOver the past few years, the U.S. has accused China of using \"debt diplomacy\" to make developing nations acros… [+6157 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Indian Express"
      },
      "author": "Lifestyle Desk",
      "title": "Good Friday 2022: Inspirational Quotes, Images, and Messages of Jesus Christ - The Indian Express",
      "description": "Good Friday 2022 Images, Quotes, Messages, Status, Jesus Christ Inspirational Quotes: It is held after the Christian holy day of Maundy Thursday, which commemorates the Last Supper of Jesus Christ, and on a Friday before Easter Sunday.",
      "url": "https://indianexpress.com/article/lifestyle/life-style/good-friday-2022-inspirational-quotes-images-and-messages-of-jesus-christ-7867753/",
      "urlToImage": "https://images.indianexpress.com/2022/04/good-friday-1200-1.jpg",
      "publishedAt": "2022-04-15T04:00:36Z",
      "content": "Good Friday 2022 Quotes: Good Friday, a Christian holy day, is observed the Friday before Easter Sunday to commemorate Jesus’ crucifixion. Christians believe that He sacrificed Himself to purge the w… [+1638 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Moneycontrol"
      },
      "author": "Nikhil Walavalkar",
      "title": "82 percent of large-cap schemes underperform benchmarks over 5 years: SPIVA report - Moneycontrol",
      "description": "The numbers for 2021 look better than the scorecard for 2020. The study showed 50 percent of the large cap equity funds underperformed the S&amp;P BSE 100 index in 2021 compared with 80 percent in 2020.",
      "url": "https://www.moneycontrol.com/news/business/personal-finance/82-percent-of-large-cap-schemes-underperform-benchmarks-over-5-years-spiva-report-8361441.html",
      "urlToImage": "https://images.moneycontrol.com/static-mcnews/2022/03/MUTUAL-FUNDS-770x431.png",
      "publishedAt": "2022-04-15T02:44:47Z",
      "content": "Its getting tougher for actively managed equity funds to outperform their benchmark indices. Many actively managed equity mutual fund schemes have underperformed the indices, according to the S&amp;P… [+4140 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "ThePrint"
      },
      "author": null,
      "title": "‘Immersive’ look at 1998 Pokhran test, Emergency — new museum shows highs & lows of 13 past PMs - ThePrint",
      "description": "PM Modi inaugurated Rs 271 crore the Pradhan Mantri Sangrahalaya — Museum of Prime Ministers — Thursday. Exhibits range from civil nuclear agreement and Pokhran, to Bofors and Emergency.",
      "url": "https://theprint.in/india/immersive-look-at-1998-pokhran-test-emergency-new-museum-shows-highs-lows-of-13-past-pms/916716/",
      "urlToImage": "https://static.theprint.in/wp-content/uploads/2022/04/museum-1.jpg",
      "publishedAt": "2022-04-15T02:09:03Z",
      "content": "New Delhi: The new Rs 271 crore Pradhan Mantri Sangrahalaya (Museum of Prime Ministers), inaugurated by Prime Minister Narendra Modi Thursday, highlights the achievements as well as the controversies… [+4910 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Hindustan Times"
      },
      "author": "HT Entertainment Desk",
      "title": "Lock Upp: Poonam Pandey says her family was thrown out of a residential society - Hindustan Times",
      "description": "Lock Upp day 46 written update: Poonam Pandey recalled one of her tough times on the show. On the other hand, Payal Rohatgi fought with Saisha Shinde while Mandana Karimi got into a fight with Anjali Arora. | Web Series",
      "url": "https://www.hindustantimes.com/entertainment/web-series/lock-upp-day-46-written-update-poonam-pandey-recalls-the-time-her-family-was-thrown-out-of-a-residential-society-101649981922710.html",
      "urlToImage": "https://images.hindustantimes.com/img/2022/04/15/1600x900/poonam_Pandey_crying_1649984064242_1649984064498.JPG",
      "publishedAt": "2022-04-15T01:16:50Z",
      "content": "On Thursday's episode of the ongoing reality show Lock Upp, Poonam Pandey was seen crying as she recalled the time when she could not get a house for herself, after stepping out of a hospital. Poonam… [+1906 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Thewire.in"
      },
      "author": null,
      "title": "With Small Uptick in Cases, Should India Be Worried About a Fourth COVID Wave? - The Wire",
      "description": "Health researcher Anurag Agrawal says with almost everybody having been infected once and a large chunk vaccinated, the country should not be too concerned about the recent rise in cases.",
      "url": "https://thewire.in/health/with-small-uptick-in-cases-should-india-be-worried-about-a-fourth-covid-wave",
      "urlToImage": "https://cdn.thewire.in/wp-content/uploads/2022/04/14182312/2022_4img10_Apr_2022_PTI04_10_2022_000191B-800x400.jpg",
      "publishedAt": "2022-04-14T13:10:45Z",
      "content": "This article was originally published on The Wire Science, our website dedicated to science, health and environment reportage and analysis. Follow, read and share.\r\nNew Delhi: There is currently a sm… [+8054 chars]"
    }, {
      "source": {
        "id": "cnn",
        "name": "CNN"
      },
      "author": "By <a href=\"/profiles/travis-caldwell\">Travis Caldwell</a>, <a href=\"/profiles/helen-regan\">Helen Regan</a>, Sana Noor Haq, Jack Bantock, <a href=\"/profiles/laura-smith-spark\">Laura Smith-Spark</a>, <a href=\"/profiles/adrienne-vogt\">Adrienne Vogt</a> and Melissa Macaya, CNN",
      "title": "Russia invades Ukraine: Live updates - CNN",
      "description": "Russian preparations continue in the east for an offensive operation, training additional units and gathering aviation forces, according to Ukraine's armed forces. Follow here for live news updates.",
      "url": "https://www.cnn.com/europe/live-news/ukraine-russia-putin-news-04-15-22/index.html",
      "urlToImage": "https://cdn.cnn.com/cnnnext/dam/assets/220414001151-mariupol-ukraine-burned-building-rivers-0412-super-tease.jpg",
      "publishedAt": "2022-04-15T16:13:00Z",
      "content": "Ukrainian President Volodymyr Zelensky told CNN Friday that \"all of the countries of the world\" should be prepared for the possibility that Russian President Vladimir Putin could use tactical nuclear… [+2268 chars]"
    },
    {
      "source": {
        "id": "the-washington-post",
        "name": "The Washington Post"
      },
      "author": "Steven Zeitchik",
      "title": "A Ukrainian refugee highlights the hope and heartbreak this Passover season - The Washington Post",
      "description": "Dr. Jacob Gaissinovitch and his family first fled their home in Donetsk in 2014 when war arrived. Now they've fled Dnipro.",
      "url": "https://www.washingtonpost.com/world/2022/04/15/ukraine-mohel-circumcision-refugee/",
      "urlToImage": "https://www.washingtonpost.com/wp-apps/imrs.php?src=https://arc-anglerfish-washpost-prod-washpost.s3.amazonaws.com/public/RM7Z2R7JTBDJ7HAYHHVPAJAEQ4.jpg&w=1440",
      "publishedAt": "2022-04-15T16:00:59Z",
      "content": "Placeholder while article actions load\r\nHe has circumcised males aged eight days to 80 years. He has taken out his kit in synagogues and hospitals, made nervous mothers feel calm and geriatric men fe… [+12501 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "The Guardian"
      },
      "author": "Nina Lakhani",
      "title": "‘That was my beloved son’: family of Patrick Lyoya say police killed their son in an ‘execution’ - The Guardian",
      "description": "The family’s lawyer says Grand Rapid police officer broke protocol by using the Taser too close to Lyoya",
      "url": "https://amp.theguardian.com/us-news/2022/apr/15/patrick-lyoya-shooting-family-police-killed-execution",
      "urlToImage": null,
      "publishedAt": "2022-04-15T15:08:00Z",
      "content": "US policingThe familys lawyer says Grand Rapid police officer broke protocol by using the Taser too close to Lyoya\r\nFri 15 Apr 2022 16.08 BST\r\nThe grief-stricken parents of the Black man shot in the … [+5391 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Gizmodo.com"
      },
      "author": "Phillip Tracy",
      "title": "M2 Macs Are Coming to Make Your Brand New Laptop Obsolete - Gizmodo",
      "description": "Developer logs point to 9 M2 Mac models that are currently in testing.",
      "url": "https://gizmodo.com/m2-macbook-air-macbook-pro-mac-mini-coming-1848798141",
      "urlToImage": "https://i.kinja-img.com/gawker-media/image/upload/c_fill,f_auto,fl_progressive,g_center,h_675,pg_1,q_80,w_1200/82e18730953f1c53b02dc5864716636a.jpg",
      "publishedAt": "2022-04-15T14:31:44Z",
      "content": "Apple is readying its next fleet of laptops and desktops as it transitions into the second phase of Mac products powered by custom silicon, a new report from Bloomberg says.\r\nThe company is reportedl… [+3448 chars]"
    },
    {
      "source": {
        "id": "the-wall-street-journal",
        "name": "The Wall Street Journal"
      },
      "author": "Andrew Restuccia, Andrew Duehren",
      "title": "Biden to Nominate Michael Barr as Top Fed Banking Regulator - The Wall Street Journal",
      "description": "If confirmed, former Obama Treasury official would oversee the largest U.S. financial firms",
      "url": "https://www.wsj.com/articles/biden-to-nominate-michael-barr-as-top-fed-banking-regulator-11650013201",
      "urlToImage": "https://images.wsj.net/im-524842/social",
      "publishedAt": "2022-04-15T14:26:00Z",
      "content": "WASHINGTONPresident Biden will nominate Michael Barr, a former Treasury Department official, to serve as the Federal Reserves top banking regulator, the White House said, after Mr. Bidens first pick … [+295 chars]"
    },
    {
      "source": {
        "id": "associated-press",
        "name": "Associated Press"
      },
      "author": "Tom Krisher",
      "title": "Modest-income buyers being priced out of new-vehicle market - The Associated Press",
      "description": "DETROIT (AP) — Two years after the pandemic tore through the economy, America’s auto market looks something like this: Prices are drastically up. Supply is drastically down. And gasoline costs drastically more.",
      "url": "https://apnews.com/7a001990f2d63bf6b3fa0188e37feb12",
      "urlToImage": "https://storage.googleapis.com/afs-prod/media/9c9b5edf18e44fc18bbcbcf5061a2a5c/3000.jpeg",
      "publishedAt": "2022-04-15T14:17:50Z",
      "content": "DETROIT (AP) Two years after the pandemic tore through the economy, Americas auto market looks something like this: Prices are drastically up. Supply is drastically down. And gasoline costs drastical… [+5512 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Salt Lake Tribune"
      },
      "author": null,
      "title": "'Please tell me what I should be saying.' Text messages show Sen. Mike Lee assisting Trump efforts to overturn 2020 election - Salt Lake Tribune",
      "description": "Newly released text messages between Sen. Mike Lee and former White House chief of staff Mark Meadows show Lee's involvement in the effort to overturn the 2020 election results. Lee apparently was aware of a scheme to have states send competing slates of elec…",
      "url": "https://www.sltrib.com/news/politics/2022/04/15/please-tell-me-what-i/",
      "urlToImage": "https://www.sltrib.com/resizer/magQ0P9avDhcS8oCswvTqe0CLf8=/1200x630/cloudfront-us-east-1.images.arcpublishing.com/sltrib/XMRYVL4Q6NFCDH36TY5AHDULVA.jpg",
      "publishedAt": "2022-04-15T14:17:08Z",
      "content": "Newly released text messages between Sen. Mike Lee and former White House chief of staff Mark Meadows show Lee was advising and assisting former President Donald Trumps efforts to overturn the result… [+8474 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "New York Times"
      },
      "author": "Patrick Kingsley, Raja Abdulrahim",
      "title": "Jerusalem Calms Following Israeli-Palestinian Clashes: Latest News and Live Updates - The New York Times",
      "description": "Violence broke out at the Aqsa Mosque compound, known to Jews as the Temple Mount, in the morning on the first day of a rare convergence of Ramadan, Easter and Passover.",
      "url": "https://www.nytimes.com/live/2022/04/15/world/jerusalem-al-aqsa-mosque",
      "urlToImage": "https://static01.nyt.com/images/2022/04/15/world/15jerusalem-briefing-promo02/merlin_205512273_76eafc23-06cc-4997-b821-3b61466d7222-facebookJumbo.jpg",
      "publishedAt": "2022-04-15T14:13:37Z",
      "content": "Palestinians and the Israeli police clashing at the Aqsa Mosque compound in Jerusalem on Friday.Credit...Mahmoud Illean/Associated Press\r\nJERUSALEM The midday Muslim prayers at one of Jerusalems holi… [+4188 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "NBCSports.com"
      },
      "author": "Michael David Smith",
      "title": "Colts sign Stephon Gilmore - NBC Sports",
      "description": "The top available free agent has found his next team.Veteran cornerback Stephon Gilmore is signing with the Colts.Gilmore was the No. 18 player on our list of the NFL’s Top 100 free agents, and the highest-ranked player who wasn’t already spoken for. (Tyrann …",
      "url": "https://profootballtalk.nbcsports.com/2022/04/15/colts-sign-stephon-gilmore/",
      "urlToImage": "https://profootballtalk.nbcsports.com/wp-content/uploads/sites/25/2022/04/GettyImages-1277236370-e1650031375609.jpg",
      "publishedAt": "2022-04-15T14:03:00Z",
      "content": "The top available free agent has found his next team.\r\nVeteran cornerback Stephon Gilmore is signing with the Colts.\r\nGilmore was the No. 18 player on our list of the NFLs Top 100 free agents, and th… [+629 chars]"
    },
    {
      "source": {
        "id": "reuters",
        "name": "Reuters"
      },
      "author": null,
      "title": "Ukrainians hang on at Mariupol steel plant - Reuters",
      "description": "Explosions rumbled and smoke rose this week from a steel making district in besieged Mariupol where dwindling Ukrainian forces are holed up as Russia tries to take full control of its biggest city yet.",
      "url": "https://www.reuters.com/world/europe/fortress-city-ukrainians-cling-steel-plant-mariupol-2022-04-15/",
      "urlToImage": "https://www.reuters.com/resizer/rw9Il5SxtxE7Dovkg1WDcIaOCSM=/1200x628/smart/filters:quality(80)/cloudfront-us-east-2.images.arcpublishing.com/reuters/NGTFN2NGNRIEJGGLDLEY33B2IU.jpg",
      "publishedAt": "2022-04-15T13:45:00Z",
      "content": "April 15 (Reuters) - Explosions rumbled and smoke rose this week from a steel making district in besieged Mariupol where dwindling Ukrainian forces are holed up as Russia tries to take full control o… [+5199 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Behind the Steel Curtain"
      },
      "author": "Andrew_Wilbar",
      "title": "2022 NFL Mock Draft 4.0: 4 rounds, 7 trades, and a new Steelers QB - Behind The Steel Curtain",
      "description": "The Steelers grab a quarterback in round 1 of my latest four-round mock draft.",
      "url": "https://www.behindthesteelcurtain.com/2022/4/15/23025815/2022-nfl-mock-draft-4-0-steelers-qb-malik-willis-logan-hall-tyquan-thornton-isaac-taylor-stuart-news",
      "urlToImage": "https://cdn.vox-cdn.com/thumbor/juHNZDcBSsAh4ZgoQlSw5r1Vxno=/0x227:3163x1883/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/23391571/usa_today_17381077.jpg",
      "publishedAt": "2022-04-15T13:30:00Z",
      "content": "We are less than two weeks away from the 2022 NFL Draft, and the draft rumor mill is beginning to pick up the pace. After a blockbuster trade and several key free agent signings in the past two weeks… [+15704 chars]"
    },
    {
      "source": {
        "id": "cnn",
        "name": "CNN"
      },
      "author": "Lisa Respers France, CNN",
      "title": "Cardi B. and Offset reveal their son's name - CNN",
      "description": "Rappers Cardi B. and Offset welcomed their son seven months ago and we now know his name.",
      "url": "https://www.cnn.com/2022/04/15/entertainment/cardi-offset-son-name/index.html",
      "urlToImage": "https://cdn.cnn.com/cnnnext/dam/assets/220215130428-cardi-b-super-tease.jpg",
      "publishedAt": "2022-04-15T13:20:00Z",
      "content": null
    },
    {
      "source": {
        "id": "cnn",
        "name": "CNN"
      },
      "author": "Katherine Dillinger",
      "title": "FDA authorizes first Covid-19 breath test - CNN",
      "description": "The US Food and Drug Administration has granted emergency use authorization to the first Covid-19 test that spots chemical compounds associated with the coronavirus in breath, the agency said Thursday.",
      "url": "https://www.cnn.com/2022/04/14/health/covid-breath-test/index.html",
      "urlToImage": "https://media.cnn.com/api/v1/images/stellar/prod/220415085321-inspectir-pny-1000-covid-breathalyzer.jpg?c=16x9&q=w_800,c_fill",
      "publishedAt": "2022-04-15T13:01:00Z",
      "content": "The US Food and Drug Administration has granted emergency use authorization to the first Covid-19 test that spots chemical compounds associated with the coronavirus in breath, the agency said Thursda… [+1084 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "New York Post"
      },
      "author": "Samantha Ibrahim",
      "title": "Late Gilbert Gottfried left behind secret 'Aladdin' outtakes, 200 hours of footage - New York Post ",
      "description": "We haven’t seen the last of Gilbert Gottfried, who passed away earlier this month at the age of 67.",
      "url": "https://nypost.com/2022/04/15/gilbert-gottfried-left-behind-secret-aladdin-outtakes-200-cameo-hours/",
      "urlToImage": "https://nypost.com/wp-content/uploads/sites/2/2022/04/gottfried-parrot.jpg?quality=75&strip=all&w=1024",
      "publishedAt": "2022-04-15T12:53:00Z",
      "content": "We haven’t seen the last of Gilbert Gottfried.\r\nThere is apparently unseen footage featuring the comedian, who died on April 12 at the age of 67, in his role of the clever parrot Iago in Disney’s “Al… [+3528 chars]"
    },
    {
      "source": {
        "id": "fox-news",
        "name": "Fox News"
      },
      "author": "Caitlin McFall",
      "title": "Psaki: 'Not sending' Biden to Ukraine, 'we should all be maybe relieved about that' - Fox News",
      "description": "White House press secretary Jen Psaki put a stop to questions about whether President Biden would travel to Ukraine Friday amid its ongoing war with Russia.",
      "url": "https://www.foxnews.com/politics/psaki-not-sending-biden-to-ukraine",
      "urlToImage": "https://static.foxnews.com/foxnews.com/content/uploads/2022/03/BIDEN-VISIT-POLAND-UKRAINE.jpg",
      "publishedAt": "2022-04-15T12:51:32Z",
      "content": "White House press secretary Jen Psaki put a stop to questions Friday about whether President Biden would travel to Ukraine amid its ongoing war with Russia.\r\n\"No. no,\" Psaki said in answer to questio… [+2564 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "ESPN"
      },
      "author": "Doug Glanville",
      "title": "Why Jackie Robinson's story still resonates 75 years after his MLB debut - ESPN",
      "description": "Every time No. 42's tale is told to a new generation, it keeps the lessons he taught us alive.",
      "url": "https://www.espn.com/mlb/story/_/id/33729977/why-jackie-robinson-story-resonates-75-years-mlb-debut",
      "urlToImage": "https://a3.espncdn.com/combiner/i?img=%2Fphoto%2F2022%2F0413%2Fr999249_1296x729_16%2D9.jpg",
      "publishedAt": "2022-04-15T12:02:39Z",
      "content": "FIVE YEARS AGO, during one of my eldest daughter's first Little League games of the season, I noticed her bouncing around at first base on each pitch. It was clear she was imitating someone, and give… [+10230 chars]"
    },
    {
      "source": {
        "id": "cnn",
        "name": "CNN"
      },
      "author": "CNN",
      "title": "READ: Mark Meadows' texts with Mike Lee and Chip Roy - CNN",
      "description": "CNN has obtained text messages of separate conversations that then-White House chief of staff Mark Meadows had with Republican Sen. Mike Lee of Utah and Republican Rep. Chip Roy of Texas after the 2020 presidential election and through early January 2021.",
      "url": "https://www.cnn.com/2022/04/15/politics/read-mark-meadows-texts-mike-lee-chip-roy/index.html",
      "urlToImage": "https://cdn.cnn.com/cnnnext/dam/assets/220414181047-roy-meadows-lee-split-super-tease.jpg",
      "publishedAt": "2022-04-15T11:06:00Z",
      "content": "(CNN)CNN has obtained text messages of separate conversations that then-White House chief of staff Mark Meadows had with Republican Sen. Mike Lee of Utah and Republican Rep. Chip Roy of Texas after t… [+18257 chars]"
    },
    {
      "source": {
        "id": "ars-technica",
        "name": "Ars Technica"
      },
      "author": "Ron Amadeo",
      "title": "OnePlus 10 Pro review: There’s not much left of the original OnePlus appeal - Ars Technica",
      "description": "Janky software and lacking updates mean there's too much compromise for $900.",
      "url": "https://arstechnica.com/gadgets/2022/04/oneplus-10-pro-review-theres-not-much-left-of-the-original-oneplus-appeal/",
      "urlToImage": "https://cdn.arstechnica.net/wp-content/uploads/2022/03/16-1-760x380.jpg",
      "publishedAt": "2022-04-15T11:00:24Z",
      "content": "Enlarge/ The OnePlus 10 Pro. It's a pretty normal smartphone for $900. \r\n24 with 20 posters participating\r\nOnePlus is in a period of upheaval.\r\nThe Chinese company lost its co-founder, Carl Pei, in 2… [+4437 chars]"
    },
    {
      "source": {
        "id": "the-wall-street-journal",
        "name": "The Wall Street Journal"
      },
      "author": "Katherine Bindley",
      "title": "California Considers the Four-Day Workweek - The Wall Street Journal",
      "description": "Lawmakers propose 32 hours for private-sector companies with more than 500 employees",
      "url": "https://www.wsj.com/articles/california-considers-the-four-day-workweek-11649994203",
      "urlToImage": "https://images.wsj.net/im-523697/social",
      "publishedAt": "2022-04-15T11:00:00Z",
      "content": "Companies and governments around the world have been debating that question recently, driven by a tight labor market along with workers seeking more flexibility. A proposal in the California State Le… [+6090 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Electrek"
      },
      "author": null,
      "title": "Elon Musk claims that ‘funding was secured’ in infamous Tesla ‘420 take private’ attempt, but SEC/banks forced him to settle - Electrek.co",
      "description": "Elon Musk opened up in a new interview yesterday about the SEC lawsuit regarding his attempt to take Tesla private back in 2018 and his infamous “funding secured” announcement. The Tesla CEO now claims funding was indeed secured and that he was forced to sett…",
      "url": "https://electrek.co/2022/04/15/elon-musk-claims-funding-was-secured-in-infamous-tesla-420-take-private-attempt-sec-banks-forced-settle/",
      "urlToImage": "https://i0.wp.com/electrek.co/wp-content/uploads/sites/3/2020/09/VW-CEO-Hebert-Diess-Tesla-CEO-Elon-Musk-selfie-hero.jpg?resize=1200%2C628&quality=82&strip=all&ssl=1",
      "publishedAt": "2022-04-15T10:48:00Z",
      "content": "Elon Musk opened up in a new interview yesterday about the SEC lawsuit regarding his attempt to take Tesla private back in 2018 and his infamous “funding secured” announcement.\r\nThe Tesla CEO now cla… [+4861 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "YouTube"
      },
      "author": null,
      "title": "Clashes in Shanghai, China, over Covid lockdown evictions – BBC News - BBC News",
      "description": "Video has emerged of clashes between police and people being forced out of their homes in Shanghai, as the city enters a third week of Covid lockdown.Some re...",
      "url": "https://www.youtube.com/watch?v=nSkSj9H_t9g",
      "urlToImage": "https://i.ytimg.com/vi/nSkSj9H_t9g/hqdefault.jpg",
      "publishedAt": "2022-04-15T10:33:43Z",
      "content": null
    },
    {
      "source": {
        "id": "independent",
        "name": "Independent"
      },
      "author": "Oliver O'Connell",
      "title": "Johnny Depp trial - live: Witness ejected from court as Amber Heard called ‘scum’ in ex’s texts - The Independent",
      "description": "Johnny Depp - Amber Heard trial",
      "url": "https://www.independent.co.uk/news/world/americas/johnny-depp-trial-live-amber-heard-court-b2058640.html",
      "urlToImage": "https://static.independent.co.uk/2022/04/11/10/newFile-8.jpg?quality=75&width=1200&auto=webp",
      "publishedAt": "2022-04-15T09:45:00Z",
      "content": "The trial for Johnny Depps defamation lawsuit against former wife, Amber Heard, has adjourned until Monday after a third day of testimony in Virginia.\r\nOn Thursday, witness Gina Deuters, a friend of … [+6957 chars]"
    },
    {
      "source": {
        "id": "axios",
        "name": "Axios"
      },
      "author": "Dan Primack, Sara Fischer",
      "title": "Billionaires eye parallel media universe - Axios",
      "description": "Tech moguls willing to spend big on \"free speech\" crisis",
      "url": "https://www.axios.com/elon-musk-twitter-billionaires-free-speech-social-media-c74547a6-e51d-48fa-b2bd-3d7458f2adb4.html",
      "urlToImage": "https://images.axios.com/cMrBX7vqRny-xhMZ-Hnm9Febnl8=/0x0:1920x1080/1366x768/2022/04/14/1649968930603.jpg",
      "publishedAt": "2022-04-15T09:03:09Z",
      "content": "Elon Musk doesn't seem to have much of a vision for how to actually run Twitter, if his takeover bid succeeds. He's not alone.\r\nWhy it matters: A small group of tech moguls believe America is in the … [+4393 chars]"
    },
    {
      "source": {
        "id": "cnn",
        "name": "CNN"
      },
      "author": "Radina Gigova, Sharon Braithwaite, Jorge Engels, Sarah Diab and Bethlehem Feleke, CNN",
      "title": "UK announces controversial plan to send asylum-seekers to Rwanda - CNN",
      "description": "People seeking asylum in the UK could now be relocated to Rwanda under a controversial new scheme blasted by international human rights groups as \"shockingly ill-conceived\" and contrary to international obligations.",
      "url": "https://www.cnn.com/2022/04/14/europe/uk-rwanda-migrant-deal-gbr-intl/index.html",
      "urlToImage": "https://cdn.cnn.com/cnnnext/dam/assets/220414165211-boris-johnson-illegal-migrants-041422-super-tease.jpg",
      "publishedAt": "2022-04-15T08:32:00Z",
      "content": null
    },
    {
      "source": {
        "id": "politico",
        "name": "Politico"
      },
      "author": null,
      "title": "Seeking: GOP dealmakers who won't 'burn the House down'. Apply to: Kevin McCarthy. - POLITICO",
      "description": "The undisputed frontrunner for speaker next year is set to lose a half-dozen Republicans from his conference who prefer negotiating to obstinacy. That could be a problem.",
      "url": "https://www.politico.com/news/2022/04/15/gop-dealmakers-house-kevin-mccarthy-00025261",
      "urlToImage": "https://static.politico.com/8a/b4/189aacde4147a61b395017dbf3d6/https-delivery.gettyimages.com/downloads/1177076170",
      "publishedAt": "2022-04-15T08:30:00Z",
      "content": "We cant have this mindset of burn the House down, said Rep. Don Bacon of Nebraska, another Republican with a bipartisan streak who plans to stick around next year. We got to be a governing party when… [+5953 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Cbslocal.com"
      },
      "author": "WCCO-TV Staff",
      "title": "Take Down Your Bird Feeders To Stop Spread Of Bird Flu, U Of M Raptor Center Says - CBS Minnesota",
      "description": "To help mitigate the spread of bird flu, the Raptor Center at the University of Minnesota is asking people to take down bird feeders and stop using bird baths this spring.",
      "url": "https://minnesota.cbslocal.com/2022/04/15/take-down-your-bird-feeders-to-stop-spread-of-bird-flu-u-of-m-raptor-center-says/",
      "urlToImage": "https://minnesota.cbslocal.com/wp-content/uploads/sites/15909630/2022/04/GettyImages-1201463951.jpg?w=1500",
      "publishedAt": "2022-04-15T07:41:00Z",
      "content": "MINNEAPOLIS (WCCO) – To help mitigate the spread of bird flu, the Raptor Center at the University of Minnesota is asking people to take down bird feeders and stop using bird baths this spring.\r\n“Beca… [+1237 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Android Authority"
      },
      "author": null,
      "title": "WhatsApp details upcoming communities feature and other big updates - Android Authority",
      "description": "Communities are coming to WhatsApp and the chat app has detailed how they will work. Several other new features are also headed to the app.",
      "url": "https://www.androidauthority.com/whatsapp-communities-new-features-3153950/",
      "urlToImage": "https://www.androidauthority.com/wp-content/uploads/2020/02/WhatsApp-by-Facebook-stock-photo-3.jpg",
      "publishedAt": "2022-04-15T04:21:26Z",
      "content": "<ul><li>WhatsApp has detailed its new communities feature coming to the app.</li><li>The company has also announced a bunch of other features, including the ability to share larger files, emoji react… [+1557 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "New York Times"
      },
      "author": "Victor Mather",
      "title": "The USFL Has Returned. What to Know as Games Kick Off. - The New York Times",
      "description": "Here’s what to know about the pro football league with the familiar name that begins its season on Saturday.",
      "url": "https://www.nytimes.com/2022/04/15/sports/football/usfl-faq.html",
      "urlToImage": "https://static01.nyt.com/images/2022/04/14/sports/14usfl/14usfl-facebookJumbo.jpg",
      "publishedAt": "2022-04-15T04:01:09Z",
      "content": "Overtime games will be decided by a shootout style competition. Each team will get three shots at the end zone from the 2-yard line. Whichever team reaches pay dirt more will win.\r\nAs an alternative … [+1158 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "SciTechDaily"
      },
      "author": null,
      "title": "Liquid Hydrogen Leak Ends NASA's Third Test Attempt at Fueling the Artemis I SLS Moon Rocket - SciTechDaily",
      "description": "Teams concluded today’s wet dress rehearsal test at approximately 5:10 p.m. EDT after observing a liquid hydrogen (LH2) leak on the tail service mast umbilical, which is located at the base of the mobile launcher and connects to the rocket’s core stage. The l…",
      "url": "https://scitechdaily.com/liquid-hydrogen-leak-ends-nasas-third-test-attempt-at-fueling-the-artemis-i-sls-moon-rocket/",
      "urlToImage": "https://scitechdaily.com/images/Artemis-I-Wet-Dress-Rehearsal-Prelaunch-Test-scaled.jpg",
      "publishedAt": "2022-04-15T02:39:03Z",
      "content": "A sunrise view of the Artemis I Space Launch System (SLS) and Orion spacecraft at Launch Pad 39B at NASAs Kennedy Space Center in Florida on April 11, 2022. Mist rises from a nearby waterway. The SLS… [+1967 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Deadline"
      },
      "author": "Tom Tapp",
      "title": "3 Times More Los Angeles Residents Infected With Covid Than Previously Reported, Says Study - Deadline",
      "description": "A new state study suggests the number of people in Los Angeles County who have been infected with Covid-19 during the pandemic is far greater than the number confirmed through standard testing. That’s due largely to the number of people who never developed sy…",
      "url": "https://deadline.com/2022/04/more-los-angeles-residents-infected-with-covid-1235002878/",
      "urlToImage": "https://deadline.com/wp-content/uploads/2021/03/AdobeStock_90015597.jpeg?w=1024",
      "publishedAt": "2022-04-15T02:36:00Z",
      "content": "A new state study suggests the number of people in Los Angeles County who have been infected with Covid-19 during the pandemic is far greater than the number confirmed through standard testing. That’… [+3981 chars]"
    },
    {
      "source": {
        "id": "cnn",
        "name": "CNN"
      },
      "author": "Max Foster and Amir Vera, CNN",
      "title": "Harry and Meghan visit Queen Elizabeth II on way to Invictus Games - CNN",
      "description": "A spokesperson for the Duke and Duchess of Sussex has confirmed to CNN that the couple visited Queen Elizabeth II on their way to The Hague to attend The Invictus Games, which begin on Saturday.",
      "url": "https://www.cnn.com/2022/04/14/uk/harry-megan-visit-queen-gbr-uk-intl/index.html",
      "urlToImage": "https://cdn.cnn.com/cnnnext/dam/assets/220414175725-prince-harry-meghan-markle-file-restricted-092321-super-tease.jpg",
      "publishedAt": "2022-04-14T23:21:00Z",
      "content": "(CNN)A spokesperson for the Duke and Duchess of Sussex has confirmed to CNN that the couple visited Queen Elizabeth II on their way to The Hague to attend The Invictus Games, which begin on Saturday.… [+1199 chars]"
    },
    {
      "source": {
        "id": "cbs-news",
        "name": "CBS News"
      },
      "author": "Tori B. Powell",
      "title": "How to tell the difference between seasonal allergies and COVID-19 - CBS News",
      "description": "As allergy season collides with another rise in COVID-19 cases, deciphering between symptoms can be tricky.",
      "url": "https://www.cbsnews.com/news/covid-19-seasonal-allergies-symptoms-difference/",
      "urlToImage": "https://cbsnews1.cbsistatic.com/hub/i/r/2022/03/16/eb9a53c9-b935-4bb7-b7c4-6471c9d64e07/thumbnail/1200x630g4/ec28ad1672bc9fd50d7a3b1c9bd61ae3/gettyimages-1208832904.jpg",
      "publishedAt": "2022-04-14T22:54:00Z",
      "content": "As allergy season collides with yet another rise in COVID-19 cases nationwide, deciphering the difference between symptoms of the coronavirus and allergies can be tricky. So how can you tell the diff… [+2079 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "New York Times"
      },
      "author": "Robin George Andrews",
      "title": "Hubble Telescope Zooms In on the Biggest Comet Ever Spotted - The New York Times",
      "description": "The space observatory helped scientists make a more precise measurement of the comet, which has a mass of 500 trillion tons and an appearance like burned toast.",
      "url": "https://www.nytimes.com/2022/04/14/science/biggest-comet-hubble.html",
      "urlToImage": "https://static01.nyt.com/images/2022/04/14/science/14sci-comet1/14sci-comet1-facebookJumbo.jpg",
      "publishedAt": "2022-04-14T20:20:31Z",
      "content": "Last year, scientists announced that they had discovered a colossal comet lingering just inside Neptunes orbit. They estimated its icy core to be between 62 and 125 miles long, based on its brightnes… [+1182 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "Yahoo Entertainment"
      },
      "author": "Reuters",
      "title": "Moldova says reports Russian army trying to recruit its citizens are dangerous - Yahoo News",
      "description": "Moldova said on Wednesday that reports that Russia's army was trying to recruit Moldovan citizens were dangerous and that it was regularly discussing all...",
      "url": "https://news.yahoo.com/moldova-accuses-russian-army-trying-123532157.html",
      "urlToImage": "https://s.yimg.com/uu/api/res/1.2/uNvOeXl_7_h.SI4B01g2uQ--~B/aD01Njg7dz04MDA7YXBwaWQ9eXRhY2h5b24-/https://media.zenfs.com/en/reuters.com/707a738453c3a1e8561b6ee252c55a17",
      "publishedAt": "2022-04-14T12:35:32Z",
      "content": "CHISINAU (Reuters) - (This April 14 story makes clear in headline and first paragraph that Moldova did not directly accuse the Russian army, and corrects day on which minister made comments; in fourt… [+2431 chars]"
    },
    {
      "source": {
        "id": null,
        "name": "San Francisco Chronicle"
      },
      "author": "Lauren Hernández",
      "title": "California lifts five-day quarantine rule for people exposed to COVID-19 but show no symptoms - San Francisco Chronicle",
      "description": "The updated quarantine guidance does not apply to people who live or work in high-risk...",
      "url": "https://www.sfchronicle.com/bayarea/article/California-lifts-five-day-quarantine-rule-for-17079881.php",
      "urlToImage": "https://s.hdnux.com/photos/01/17/27/54/20806403/6/rawImage.jpg",
      "publishedAt": "2022-04-14T04:21:47Z",
      "content": "This file photograph shows medical assistant Frencesca Delprete takes care of a driver during COVID-19 testing at the Alemany Farmers Market in San Francisco, Calif., on Monday, November 30, 2020.\r\nC… [+2006 chars]"
    },
    {
      "source": {
        "id": "national-geographic",
        "name": "National Geographic"
      },
      "author": "Maya Wei-Haas",
      "title": "The bizarre drama behind a pinch of moon dust that just sold for $500,000 - National Geographic",
      "description": "Today’s auction is the culmination of a sordid saga involving Apollo astronauts, multiple lawsuits, and scientists aching for a chance to study rare lunar materials.",
      "url": "https://www.nationalgeographic.com/science/article/the-bizarre-drama-behind-a-pinch-of-moon-dust-that-just-sold-for-500000",
      "urlToImage": "https://i.natgeofe.com/n/f085ec36-dcfb-4473-914b-84905ab13c67/apollo11_0_16x9.jpg?w=1200",
      "publishedAt": "2022-04-13T17:47:09Z",
      "content": "On the precipice of humankind's first step on the moon, Neil Armstrong stood on the lunar module's ladder and described the grounds peculiar texture. Its almost like a powder, he told the Apollo Miss… [+9526 chars]"
    }
  ]
}


================================================
FILE: src/containers/apps/index.js
================================================
import React, {useState, useEffect} from 'react';
import {useSelector, useDispatch} from 'react-redux';

import './index.scss';
import './themes.scss';

export * from './whatsapp';
export * from './youtube';
export * from './playstore';
export * from './google';


================================================
FILE: src/containers/apps/index.scss
================================================
.app-wrapper{
  pointer-events: auto;
  height: 100%;
  border-radius: 0;
  transition: all 200ms ease-in-out;
  animation: popup ease-in-out 200ms;

  &[data-open="false"]{
    border-radius: 1em;
    transform: scale(0.58);
    opacity: 0;
    pointer-events: none;

    *{
      pointer-events: none;
    }
  }
}

.app-inner-wrapper{
  height: 100%;
  overflow: hidden;
  position: relative;
}

.app-icon-container{
  height: 70%;
  display: grid;
  place-items: center;
}

@keyframes popup {
  from{
    border-radius: 1em;
    transform: scale(0.8);
    opacity: 0;
  }

  to{
    border-radius: 0;
    transform: scale(1);
    opacity: 1;
  }
}

.full-hide{
  height: 100%;
  overflow: hidden;
}


================================================
FILE: src/containers/apps/playstore/index.js
================================================
import React, {useState, useEffect} from 'react';
import {useSelector, useDispatch} from 'react-redux';

import {Icon, Image, LazyComponent} from 'components/utils';
import {dispatchAction, dispatchAct} from 'store/actions';

export const PlaystoreApp = () => {
  const app = useSelector(state => state.home.apps.playstore || {});
  const home = useSelector(state => state.home);
  const show = home.ishome==false && home.stack.at(-1)==app.payload;

  return <AppContainer app={app} show={show}/>
}

const AppContainer = ({app, show}) => {
  const clstring = `${app.payload}-wrapper`;

  return (
    <div className={"app-wrapper "+clstring} id={clstring} data-open={show}>
      <div className="app-icon-container">
        <Icon className="mdShad" src={"apps/" + app.icon} w={72} action="home/setHome"/>
        <span>Playstore</span>
      </div>
    </div>
  );
}


================================================
FILE: src/containers/apps/themes.scss
================================================
.app-wrapper{
  --app-bg: #fefefe;
  background: var(--app-bg);
}

.whatsapp-wrapper, .playstore-wrapper{
  --app-bg: #fefefe;
  background: var(--app-bg);

  --teal: #128c7e;
  --teal-dark: #075e54;
  --light-teal: #02a884;
  --light-green: #25d366;
  --white: #fefefe;
  --blue: #34b7f1;
  --check-blue: #34b7f1;
  --link-blue: #5bc2df;
  --bubble-green: #dcf8c6;
}

.whatsapp-viewport, .playstore-viewport{
  --app-bg: #fefefe;
  --teal: #128c7e;
  --teal-dark: #075e54;

  --statusbg: var(--teal);
  --navbg: var(--app-bg);
}

.youtube-wrapper{
  --app-bg: #fefefe;
  --pm-txt: #121212;
  background: var(--app-bg);
  // color: #fefefe;
}

.youtube-viewport{
  --app-bg: #fefefe;
  --statusbg: var(--app-bg);
  --navbg: var(--app-bg);
}


.google-wrapper{
  --app-bg: #fefefe;
  background: var(--app-bg);
}

.google-viewport{
  --app-bg: #fefefe;

  --statusbg: var(--app-bg);
  --navbg: var(--app-bg);
}


================================================
FILE: src/containers/apps/whatsapp/elements/extra.scss
================================================
// media player && status screen

.media-viewer-container, .status-container{
  position: absolute;
  inset: 0;
  background: #010101;
  transition: all 200ms ease-in-out;

  &[value="hide"] {
    opacity: 0;
    pointer-events: none;
  }

  .whatsapp-top-nav {
    background: none;
    color: var(--comp-txt);
    position: absolute;
    top: 0;
    padding: 0.5em;
    z-index: 1;

    .chat-name {
      span:nth-child(1) {
        font-size: 1.2em;
      }

      span:nth-child(2) {
        font-size: 0.64em;
        line-height: 0.75em;
      }
    }
  }

  .media-screen {
    display: grid;
    place-items: center;
    height: 100%;

    .vidCont {
      height: 100%;
    }

    .video-control-container {
      .MuiSlider-thumb,
      .MuiSlider-track {
        background: var(--light-teal);
      }
    }
  }
}

.status-container{

  .whatsapp-top-nav{
    display: flex;
    flex-direction: column;

    .imageCont{
      margin: 0 0.5em;
    }
  }

  .status-msg-container{
    height: 100%;
    background: #2e92ee;
    display: grid;
    place-items: center;
    padding: 1em;
    text-align: center;
    font-size: 2em;
    color: var(--comp-txt);
  }

  .caption-container{
    position: absolute;
    bottom: 1.5em;
    padding: 0.5em 0;
    width: 100%;
    color: var(--white);
    background: rgba(1, 1, 1, 0.5);
    font-weight: 500;
    text-align: center;
  }
}

.progress-bar-container{
  display: flex;
  height: 2px;
  border-radius: 1em;
  margin-bottom: 0.5em;

  .progress-bar{
    flex-grow: 1;
    background: rgba(136, 136, 136, 0.5);
    margin: 0 1.5px;
    overflow: hidden;

    &[data-lit="true"]{
      background: var(--white);
    }

    .progress-fill{
      width: 0;
      height: 100%;
      background: var(--white);
      transition: width 400ms linear;
    }
  }
}

// chat screen

.chat-profile {
  display: flex;
  align-items: center;
  padding: 0.2em;
  border-radius: 2em;
}

.chat-screen-container {
  position: absolute;
  inset: 0;
  --msg-incoming: #fefefe;
  --msg-outgoing: #e7ffdd;
  --msg-toast: #d3eaf7;
  // --msg-outgoing: #e1ffc7;
  .chat-screen {
    flex-grow: 1;
    overflow: hidden;
  }

  .chat-input-container {
    display: flex;
    align-items: flex-end;
    padding: 0.2em 0.4em;

    .chat-input-wrapper {
      display: flex;
      align-items: flex-end;
      background: var(--white);
      color: var(--mid-txt);
      border-radius: 24px;
      padding: 0.2em 0.4em;
      flex-grow: 1;

      .uicon {
        margin: 0.36em 0.24em;
      }
    }

    .chat-input-field {
      padding: 0.2em;
      flex-grow: 1;

      & > div {
        border: none;
        padding: 0.3em 0;
        color: var(--med-txt);
      }

      fieldset {
        display: none;
      }
    }

    .mic-icon,
    .send-icon {
      color: var(--white);
      background: var(--light-teal);
      height: 45px;
      aspect-ratio: 1 / 1;
      margin-left: 0.5em;
      border-radius: 4em;
    }

    .mic-icon {
      animation: pop-up ease-in-out 200ms;
    }

    .send-icon {
      animation: pop-up2 ease-in-out 200ms;
    }
  }
}

.chat-container {
  position: relative;
  flex-grow: 1;
  overflow-y: scroll;
  padding: 0.5em 1em;

  .chat-scroll-container {
    // position: absolute;
    min-height: 100%;
    height: max-content;
  }

  .chat-toast {
    position: relative;
    display: flex;
    flex-direction: column;
    // margin: 0.5em 0;
    &[value="0"] {
      align-items: flex-start;
    }

    &[value="1"] {
      align-items: center;
    }

    &[value="2"] {
      align-items: flex-end;
    }
  }

  .msg-box {
    position: relative;
    min-width: 4em;
    max-width: 80%;
    padding: 0.2em 0.3em;
    border-radius: 0.5em;
    margin: 0.125em 0;
    font-size: 0.9em;

    .imageCont {
      margin-top: 0.125em;

      img {
        border-radius: 0.5em;
      }
    }

    .vidCont {
      margin-top: 0.125em;
      border-radius: 0.5em;
      overflow: hidden;
    }

    pre {
      padding: 0 0.2em;
      font-family: inherit;
      white-space: pre-wrap;
      word-wrap: break-word;
      max-width: 100%;
      color: var(--med-txt);

      a {
        color: var(--link-blue);
        font-weight: 500;
        white-space: pre-wrap;
        overflow-wrap: break-word;
      }
    }

    .chat-date {
      margin-left: 0.5em;
      margin-top: 0.5em;
      float: right;
      font-size: 0.7em;
      font-weight: 500;
      color: var(--sat-txt);
      fill: var(--sat-txt);
      display: flex;
      align-items: center;

      .uicon {
        margin-left: 0.2em;
        margin-bottom: 2px;
      }
    }

    & > .chat-date {
      position: absolute;
      bottom: 0.5em;
      right: 1em;
      -webkit-text-stroke-color: #fefefe;
      -webkit-text-stroke-width: thin;
    }
  }

  .in-msg {
    background: var(--msg-incoming);
  }

  .out-msg {
    background: var(--msg-outgoing);
  }

  .first-msg {
    margin-top: 0.5em;
    position: relative;
    z-index: 0;

    &::after {
      content: "";
      position: absolute;
      top: 0;
      width: 1em;
      height: 1.2em;
      z-index: -1;
    }
  }

  .in-msg.first-msg::after {
    left: -0.5em;
    background: var(--msg-incoming);
    clip-path: polygon(0 0,100% 0,100% 100%);
  }

  .out-msg.first-msg::after {
    right: -0.5em;
    background: var(--msg-outgoing);
    clip-path: polygon(0 0,100% 0,0 100%);
  }

  .msg-toast {
    margin: 0.5em 0;
    width: auto;
    max-width: 90%;
    padding: 0.2em 0.5em;
    border-radius: 0.5em;
    font-size: 0.72em;
    text-align: center;
    background: var(--msg-toast);
    box-shadow: 1px 1px 1px rgba(18, 18, 18, 0.25);
    word-spacing: 2px;
  }
}

// all status
.status-date {
  font-size: 0.8em;
  font-weight: 500;
  color: var(--light-txt);
}

.my-status {
  padding: 0.5em 0;

  .chat-status {
    margin: 0;
  }
}


================================================
FILE: src/containers/apps/whatsapp/elements/index.js
================================================
import React, {useState, useEffect, useRef} from 'react'
import {useSelector, useDispatch} from 'react-redux'

import TextField from '@mui/material/TextField'

import {Icon, Image, Video, isValidURL} from 'components/utils.js'
import {dispatchAction, dispatchAct} from 'store/actions'

import './extra.scss'

export const NavBar = (props)=>{
  const [swidth, setWidth] = useState(0);
  const [offLeft, setLeft] = useState(0);
  const navbar = useRef();

  useEffect(()=>{
    if(navbar.current){
      var childEle = navbar.current.children[props.tab];
      var cwidth = getComputedStyle(childEle).width,
          cleft = childEle.offsetLeft;

      setWidth(cwidth)
      setLeft(cleft)
    }
  }, [props.tab])

  return (
    <div className={`w-nav-tab ${props.className || ""}`} ref={navbar}>
      {props.options && props.options.map((opt,i) => {
        return (
          <div className="tab-option active-light-lit prtclk"  key={i}
              value={props.tab == i} data-id={i} onClick={props.onClick}>
            {opt}
          </div>
        )
      })}
      <div className="nav-slider" style={{
        width: swidth,
        left: offLeft
      }}></div>
    </div>
  )
}

export const MediaViewer = () => {
  const media = useSelector(state => state.whatsapp.media || {});

  return (
    <div className="media-viewer-container" value={!media.vis && "hide"}>
      <div className="whatsapp-top-nav">
        <div className="chat-profile-container flex items-center">
          <div className="chat-profile active-light-lit">
            <Icon mui="ArrowBack" w={20} action="home/goBack"/>
          </div>
          <div className="chat-name flex-column font-thin mx-4">
            <span>{media.name}</span>
            <span>{new Date(media.time).pastdatetime()}</span>
          </div>
        </div>
        <div className="w-nav-icons">
          <Icon mui="Reply" flip/>
          <Icon mui="MoreVert"/>
        </div>
      </div>
      <div className="media-screen">
        {media.type=="Photo" &&
          <Image src={media.src} dir="asset/whatsapp/chats"/>}
        {media.type=="Video" && (
          <Video src={media.src} dir="asset/whatsapp/chats" h="100%" cstmctrl/>
        )}
      </div>
    </div>
  )
}

export const StatusScreen = ()=>{
  const [idx, setIdx] = useState(0)
  const [viewper, setPercent] = useState(0)
  const [paused, setPaused] = useState(false)
  const stdata = useSelector(state => state.whatsapp.status || {})
  const contact = useSelector(state => {
    var tmp = {}
    if(stdata.id == -1) tmp = state.whatsapp.self
    else tmp = state.whatsapp.chats && state.whatsapp.chats[stdata.id]
    return tmp || {}
  })

  const photoProgressBool = ()=>{
    return stdata.vis && contact.status && contact.status[idx].media!="Video" && !paused;
  }

  const progressPhoto = ()=>{
    if(!photoProgressBool()) return
    if(viewper<idx+1) setPercent(viewper + 0.02)
    else{
      setPercent(idx+1)
      if(idx < contact.status.length - 1){
        setIdx(idx + 1)
      }else{
        setIdx(0)
        dispatchAct({type: "home/goBack"})
      }
    }
  }

  const skipNext = ()=>{
    // setPaused(true)
    setPercent(idx+1)
    if(idx < contact.status.length - 1){
      setIdx(idx + 1)
    }else{
      setIdx(0)
      dispatchAct({type: "home/goBack"})
    }
  }

  const handleProg = (e)=>{
    if(e.played<1) setPercent(idx + e.played)
    else{
      setPercent(idx+1)
      if(idx < contact.status.length - 1){
        setIdx(idx + 1)
      }else{
        setIdx(0)
        dispatchAct({type: "home/goBack"})
      }
    }
  }

  useEffect(()=>{
    if(photoProgressBool() && !paused){
      setTimeout(progressPhoto, 100)
    }
  },[viewper,stdata.vis,idx,paused,contact.status])

  useEffect(()=>{
    if(!stdata.vis && (idx!=0 || viewper!=0)){
      setIdx(0)
      setPercent(0)
    }

    if(stdata.id){
      dispatchAct({type: "whatsapp/setViewStatus", payload: {
        id: stdata.id, count: idx + 1
      }})
    }
  }, [stdata.vis, idx, viewper])

  return (
    <div className="status-container" value={!stdata.vis && "hide"}>
      <div className="whatsapp-top-nav">
        <div className="progress-bar-container">
          {stdata.vis && contact.status && contact.status.map((st,i) => {
            return (
              <div className='progress-bar' data-lit={i<idx} key={i}>
                {i==idx && <div className="progress-fill" style={{
                  width: `${(viewper-i)*100}%`
                }}></div>}
              </div>
            )
          })}
        </div>
        <div className="chat-profile-container flex items-center">
          <div className="chat-profile active-light-lit">
            <Icon mui="ArrowBack" w={20} action="home/goBack"/>
            <Image className="rounded-full overflow-hidden"
              src={contact.img} dir="asset/whatsapp/pfp" w={36}/>
          </div>
          <div className="chat-name flex-column font-thin mb-1">
            <span>{stdata.id == -1 ? "You" : contact.name}</span>
            <span>
              {new Date(contact.status && contact.status.at(-1).time).minifyTime()}
            </span>
          </div>
        </div>
      </div>
      {stdata.vis && contact.status?(
        <>
        <div className="media-screen prtclk" onClick={skipNext}>
          {contact.status[idx].media=="Photo" &&
            <Image src={contact.status[idx].src} dir="asset/whatsapp/"/>}
          {contact.status[idx].media=="Video" && (
            <Video src={contact.status[idx].src} h="100%" dir="asset/whatsapp/"
              playIcon autoplay onProgress={handleProg} play={!paused}/>
          )}
          {contact.status[idx].msg && (
            <div className="status-msg-container">
              {contact.status[idx].msg}
            </div>
          )}
        </div>
        {contact.status[idx].caption && (
          <div className="caption-container">{contact.status[idx].caption}</div>
        )}
        </>
      ):null}
    </div>
  )
}

export const AllStatusScreen = (props)=>{
  const wdata = useSelector(state => state.whatsapp);
  const myself = useSelector(state => state.whatsapp.self);
  const contacts = useSelector(state => state.whatsapp.chats);

  const CalculateArc = ({n,i,viewed})=>{
    var gz = n==1 ? 0 : 3;
    var dash = `${300/n - 2*gz}% ${300*(1 - 1/n) + 2*gz}%`,
        offset = `${75 + 300*((i+1)/n) - 2*gz}%`

    return (
      <circle className={viewed?"viewed":"notviewed"} cx="52" cy="52"
        r="50" fill="none" strokeLinecap="round" strokeWidth="4"
        strokeDashoffset={offset} strokeDasharray={dash}></circle>
    )
  }

  return (
    <div className="chats-status-container medScroll">
      <div className="my-status active-dark-lit prtclk" onClick={dispatchAction}
        data-action="whatsapp/setStatus" data-payload={-1}>
        <div className="chat-status">
          <div className="status-preview-container">
            <div className="status-preview">
              <svg viewBox="0 0 104 104" xmlns="http://www.w3.org/2000/svg">
                {myself && myself.status.map((status,i)=>{
                  return <CalculateArc n={myself.status.length}
                                        i={i} viewed key={i}/>
                })}
              </svg>
            </div>
            <Image className="rounded-full rounded" src={myself && myself.img}
                    dir="" w={48}/>
          </div>
          <div className="status-info flex flex-col mx-4">
            <div className="chat-name">My status</div>
            <div className="status-date">
              {myself && new Date(myself.status.at(-1).time).minifyTime()}
            </div>
          </div>
        </div>
      </div>
      <div className="gray-txt text-sm px-6">Recent updates</div>
      <div className="chats-status">
        {contacts && contacts.map((contact,i) => {
          if(!contact.status || !contact.status.length) return null
          return (
            <div className="chat-status active-dark-lit prtclk" data-payload={contact.id}
              onClick={dispatchAction} data-action="whatsapp/setStatus" key={contact.id}>
              <div className="status-preview-container">
                <div className="status-preview">
                  <svg viewBox="0 0 104 104" xmlns="http://www.w3.org/2000/svg">
                    {contact.status.map((status,i)=>{
                      return <CalculateArc n={contact.status.length}
                              i={i} key={i} viewed={status.seen}/>
                    })}
                  </svg>
                </div>
                <Image className="rounded-full rounded" src={contact.img}
                        dir="asset/whatsapp/pfp" w={48}/>
              </div>
              <div className="status-info flex flex-col mx-4">
                <div className="chat-name">{contact.name}</div>
                <div className="status-date">
                  {new Date(contact.status.at(-1).time).minifyTime()}
                </div>
              </div>
            </div>
          )
        })}
      </div>
    </div>
  )
}

export const CallLogs = ()=>{
  const wdata = useSelector(state => state.whatsapp);
  const contacts = useSelector(state => state.whatsapp.chats);

  return (
    <div className="call-logs-container medScroll">
      <div className="call-logs">
        {contacts && contacts.map((chat,i) => {
          var randv = chat.name.split("").map(x => x.charCodeAt()).reduce((b,c) => 2*b + 3*c);
          var callst = randv%3, callsticon = callst!=2?"CallReceived":"CallMade",
              className = callst == 0? "callMissed":(callst==1?"callConnected":"callMade"),
              minago = (randv*13)%1440, calltype = ["ph","vid"][randv%2];

          return(
            <div className="call-log" key={i}>
              <Image className="rounded-full rounded" src={chat.img} dir="asset/whatsapp/pfp" w={48}/>
              <div className="flex flex-col mx-4 flex-grow">
                <div className="chat-name">{chat.name}</div>
                <div className="status-date">
                  <Icon className={className} mui={callsticon} w={14}/>
                  <span>{new Date().minifyTime(minago)}</span>
                </div>
              </div>
              {calltype=="ph"?<Icon className="teal-green" mui="Call" rounded/>:
                              <Icon className="teal-green" mui="Videocam"/>}
            </div>
          )
        })}
      </div>
    </div>
  )
}

export const ChatScreen = (props)=>{
  const [msg, setMsg] = useState('');
  const chatscreen = useRef();
  const wdata = useSelector(state => state.whatsapp);
  const contact = useSelector(state => {
    return (
      state.whatsapp.chats && state.whatsapp.chats[state.whatsapp.curr]
    ) || {}
  })

  const handleMsg = (e)=>{
    setMsg(e.target.value)
  }

  const mediaHandler = (e)=>{
    var ele = e.target;
    const payload = {
      type: ele.dataset.type,
      src: ele.dataset.src,
      name: ele.dataset.name,
      time: ele.dataset.time
    }

    dispatchAct({type: "whatsapp/setMedia", payload: payload})
  }

  const processLinks = (txt)=>{
    var arr = txt.split(' '), tmp = [], txtstr = [];

    for (var i = 0; i < arr.length; i++) {
      try{
        var url = (!arr[i].startsWith("http") ? "https://":"") + arr[i] ,
            urlobj = null;

        if(isValidURL(url)) urlobj = new URL(url)
        if(!urlobj) throw new Error()

        if(txtstr.length) tmp.push(txtstr.join(' '))
        txtstr = []
        tmp.push(<a href={urlobj.href} target="_blank" key={i}> {arr[i]} </a>)
      }catch(e){
        txtstr.push(arr[i])
      }
    }

    if(txtstr.length) tmp.push(txtstr.join(' '))

    return tmp
  }

  const sendMessage = ()=>{
    dispatchAct({
      type: "whatsapp/sendMsg",
      payload: {
        id: wdata.curr,
        msg: msg
      }
    })

    setMsg("")
  }

  const scrollToEnd = ()=>{
    chatscreen.current.scrollBy(0, chatscreen.current.scrollHeight + 100)
  }

  useEffect(()=>{
    if(props.checkstate('chat') && chatscreen.current){
      scrollToEnd()
      setTimeout(scrollToEnd, 200)
      dispatchAct({type: "whatsapp/setChatProp", payload: {
        id: wdata.curr,
        key: "seen",
        value: true
      }})
    }
  }, [wdata.curr, contact.chat])

  return(
    <div className="chat-screen-container flex-column scale-trans"
        data-vis={props.checkstate('chat')}>
      <div className="whatsapp-top-nav downbug">
        <div className="chat-profile-container flex items-center">
          <div className="chat-profile prtclk active-light-lit"
            onClick={dispatchAction} data-action="home/goBack">
            <Icon mui="ArrowBack" w={20}/>
            <Image className="rounded-full overflow-hidden"
                  src={contact.img} dir="asset/whatsapp/pfp" w={36}/>
          </div>
          <div className="chat-name text-lg font-thin mx-2">{contact.name}</div>
        </div>
        <div className="w-nav-icons">
          <Icon mui="Videocam"/>
          <Icon mui="Call" w={20} rounded/>
          <Icon mui="MoreVert"/>
        </div>
      </div>
      <div className="chat-screen flex-column" style={{
        background: 'url(/img/asset/whatsapp/background.png)'
      }}>
        <div className="chat-container medScroll" ref={chatscreen}>
          <div className="chat-scroll-container">
            <div className="chat-toast" value="1">
              <div className="msg-toast">
                🔓 Messages and calls are end-to-end open. It's Facebook, what else would you expect. Privacy go brrr.
              </div>
            </div>
            {contact.chat && contact.chat.map((item, i) => {
              var arr = [], prev = i>0 && contact.chat[i-1];

              if(i==0 || new Date(prev.time).getDate() != new Date(item.time).getDate()){
                arr.push(
                  <div className="chat-toast" value="1" key={"msg-"+i}>
                    <div className="msg-toast">
                      {new Date(item.time).pastdate()}
                    </div>
                  </div>
                )
              }

              arr.push(
                <div className="chat-toast" value={item.type} key={i}>
                  {item.type!="1"?(
                    <div className={'msg-box ' +
                      (item.type=='0'?'in-msg':'out-msg') +
                      (!prev || prev.type!=item.type ? ' first-msg':'')
                    }>
                      {item.media=="Photo"?(
                        <Image src={item.src} dir="asset/whatsapp/chats"
                          data-type={item.media} data-name={
                            item.type=='0'? (item.name || contact.name): "You"
                          } onClick={mediaHandler} data-time={item.time}
                          data-src={item.src}/>
                      ):null}
                      {item.media=="Video"?(
                        <Video
                          src={item.src} inactive
                          dir="asset/whatsapp/chats"
                          data-type={item.media} data-name={
                            item.type=='0'? (item.name || contact.name): "You"
                          } onClick={mediaHandler}
                          data-time={item.time}
                          data-src={item.src}/>
                      ):null}
                      {item.msg?(
                        <pre>
                          {processLinks(item.msg)}
                          <div className="chat-date">
                            <span>{new Date(item.time || 0).time12()}</span>
                            {item.type=="2"?(
                              item.seen?<Icon className="seentick" icon="seentick" w={14} payload={item.seen}/>:
                                  <Icon className="seentick" mui="Done" w={14} payload={0}/>
                            ):null}
                          </div>
                        </pre>
                      ):(
                        <div className="chat-date">
                          <span>{new Date(item.time || 0).time12()}</span>
                          {item.type=="2"?(
                            item.seen?<Icon className="seentick" icon="seentick" w={14} payload={item.seen}/>:
                                <Icon className="seentick" mui="Done" w={14} payload={0}/>
                          ):null}
                        </div>
                      )}
                    </div>
                  ):(
                    <div className="msg-toast">{item.msg}</div>
                  )}
                </div>
              )

              return arr
            })}
          </div>
        </div>
        <div className="chat-input-container">
          <div className="chat-input-wrapper">
            <Icon mui="EmojiEmotions" out/>
            <TextField className="chat-input-field" multiline maxRows={4}
              placeholder="Message" value={msg} onChange={handleMsg}/>
            <Icon mui="AttachFile"/>
            {msg.length==0?<Icon className="CameraIcon" mui="PhotoCamera" round/>:null}
          </div>
          {msg.length==0?(
            <Icon className="mic-icon" mui="Mic" h={24}/>
          ):<Icon className="send-icon press-in" mui="Send" h={24} onClick={sendMessage}/>}
        </div>
      </div>
    </div>
  )
}

export const AllContacts = (props)=>{
  const wdata = useSelector(state => state.whatsapp);
  const contacts = useSelector(state => state.whatsapp.chats || []);

  const clickContact = (e)=>{
    var id = e.target.getAttribute("value");

    dispatchAct({
      type: "whatsapp/setProp",
      payload: {key: "curr", value: id}
    })

    dispatchAct({
      type: "home/navApp",
      payload: "whatsapp.chat"
    })
  }

  return (
    <div className="contacts-container scale-trans"
        data-vis={props.checkstate('contact')}>
      <div className="whatsapp-top-nav">
        <div className="chat-profile-container flex items-center">
          <div className="chat-profile active-light-lit">
            <Icon mui="ArrowBack" w={20} action="home/goBack"/>
          </div>
          <div className="chat-name flex-column font-thin mx-4">
            <span className="text-lg">Select Contacts</span>
            <span className="text-xs">
              {contacts.length} contact{contacts.length>1?"s":""}
            </span>
          </div>
        </div>
        <div className="w-nav-icons">
          <Icon mui="Search"/>
          <Icon mui="MoreVert"/>
        </div>
      </div>
      <div className="all-contacts medScroll">
        <div className="contact-cotainer active-dark-lit prtclk">
          <Icon className="rounded-full rounded" mui="Group" w={24}/>
          <div className="flex flex-col ml-4 flex-grow">
            <div className="chat-name">New group</div>
          </div>
        </div>
        <div className="contact-cotainer active-dark-lit prtclk">
          <Icon className="rounded-full rounded" mui="PersonAdd" w={22}/>
          <div className="flex flex-col ml-4 flex-grow">
            <div className="chat-name">New contact</div>
          </div>
        </div>
        {contacts && [...contacts].sort((a,b)=> {
          return a.name>b.name?1:-1
        }).map(contact => {
          return(
            <div className="contact-cotainer active-dark-lit prtclk"
                  key={contact.id} onClick={clickContact} value={contact.id}>
              <Image className="rounded-full rounded"
                src={contact.img} dir="asset/whatsapp/pfp" w={38}/>
              <div className="flex flex-col ml-4 flex-grow">
                <div className="chat-name">{contact.name}</div>
                <div className="status-date">
                  {contact.bio}
                </div>
              </div>
            </div>
          )
        })}
      </div>
    </div>
  )
}


================================================
FILE: src/containers/apps/whatsapp/index.js
================================================
import React, {useState, useEffect, useRef} from 'react';
import {useSelector, useDispatch} from 'react-redux';

import {Icon, Image, LazyComponent} from 'components/utils';
import {dispatchAction, dispatchAct} from 'store/actions';
import Swiper from 'react-slick';

import './whatsapp.scss';

import {
  NavBar,
  AllStatusScreen,
  AllContacts,
  StatusScreen,
  ChatScreen,
  CallLogs,
  MediaViewer
} from './elements'

export const WhatsappApp = () => {
  const app = useSelector(state => state.home.apps.whatsapp || {});
  const home = useSelector(state => state.home);
  const show = home.ishome==false && home.stack.at(-1)==app.payload;
  const pagetree = app && app.pagetree || {
    "main": {
      "chat" : {},
      "contact" : {}
    }
  }

  useEffect(()=>{
    if(app && !app.pagetree){
      dispatchAct({type: "home/setApp", payload: {
        id: app.payload,
        data: {
          ... app,
          pagetree: pagetree,
          path: ['main']
        }
      }})
    }
  }, [app])

  return <AppContainer app={app} show={show}/>
}

const AppContainer = ({app, show}) => {
  const [tab, setTab] = useState(1);
  const homeSwiper = useRef();
  const clstring = `${app.payload}-wrapper`;
  const path = app.path || ["main"];

  const tabSetter = (hmswiper)=>{
    if(!hmswiper) return
    var swidth = hmswiper.style.width,
        swleft = hmswiper.style.transform;

    swidth = swidth.replace("px","")
    swleft = swleft.replace("translate3d(","").replace("px, 0px, 0px)","")

    try{
      swidth = parseInt(swidth)
      swleft = Math.abs(parseInt(swleft))

      var tb = Math.round((swleft*4)/swidth)
      tb = Math.max(0, Math.min(3, tb))
      setTab(tb)
    }catch(err){
      console.log(err)
    }
  }

  const clickTabHandler = (e)=>{
    var id = e.target.dataset.id
    if(homeSwiper.current){
      setTab(id)
      homeSwiper.current.slickGoTo(id, false)
    }
  }

  const swipehandler = ()=>{
    if(homeSwiper.current){
      var hmswiper = document.querySelector('.app-wrapper .whatsapp-home-swiper .slick-track');
      tabSetter(hmswiper)
      setTimeout(()=>{
        tabSetter(hmswiper)
      },200)
    }
  }

  const checkstate = (comp) => {
    return path.includes(comp) ? (
      path.at(-1) == comp ? 1 : 2
    ) : 0;
  }

  return (
    <div className={"app-wrapper " + clstring} id={clstring} data-open={show}>
      <div className="app-inner-wrapper wp-inner-wrapper">
        <div className='whatsapp-home full-hide upbug scale-trans' data-vis={checkstate('main')}>
          <div className="whatsapp-top-nav downbug">
            <div className="brand-name">WhatsApp</div>
            <div className="w-nav-icons">
              <Icon mui="Search"/>
              <Icon mui="MoreVert"/>
            </div>
          </div>
          <NavBar className="home-nav-tab" tab={tab} options={[
            <Icon mui="PhotoCamera" round w={22}/>,"CHATS","STATUS","CALLS"
          ]} onClick={clickTabHandler}/>
          <div className="whatsapp-home-page">
            <Swiper className="whatsapp-home-swiper full-height-swiper" {...{
              dots: false,
              arrows: false,
              infinite: false,
              initialSlide: tab,
              speed: 200
            }} onSwipe={swipehandler} ref={homeSwiper}>
              <CameraScreen/>
              <AllChatScreen/>
              <AllStatusScreen/>
              <CallLogs/>
            </Swiper>
            {tab!=0 && (
              <div className="quick-wtool-container press-in">
                {tab==1 && <Icon action="home/navApp" mui="Chat"
                              payload="whatsapp.contact" w={24}/>}
                {tab==2 && <Icon mui="PhotoCamera" round w={24}/>}
                {tab==3 && <Icon mui="AddIcCall" rounded w={24}/>}
              </div>
            )}
          </div>
        </div>
        <ChatScreen checkstate={checkstate}/>
        <AllContacts checkstate={checkstate}/>
        <MediaViewer/>
        <StatusScreen/>
      </div>
    </div>
  );
}

const CameraScreen = ()=>{
  return (
    <div className="camera-container"></div>
  )
}

const AllChatScreen = ()=>{
  const wdata = useSelector(state => state.whatsapp);
  const contacts = useSelector(state => state.whatsapp.chats);

  const clickChat = (e)=>{
    var id = e.target.getAttribute("value");

    dispatchAct({
      type: "whatsapp/setProp",
      payload: {key: "curr", value: id}
    })

    dispatchAct({
      type: "home/navApp",
      payload: "whatsapp.chat"
    })
  }

  useEffect(()=>{
    dispatchAct({type: 'home/setAppKey', payload:{
      id: 'whatsapp', key: 'comp', value: wdata.comp
    }})
  }, [wdata.comp])

  return (
    <div className="home-chats-container medScroll">
      <div className="home-chats">
        {contacts && [...contacts].sort((a,b)=>{
          if(!a.chat || !b.chat) return 1
          if(!a.chat.length) return -1
          if(!b.chat.length) return 1

          var a_lastmsg = a.chat.at(-1),
              b_lastmsg = b.chat.at(-1)

          var a_seen = a_lastmsg.type=="0" && !a.seen,
              b_seen = b_lastmsg.type=="0" && !b.seen

          if(a_seen ^ b_seen){
            if(!a_seen) return 1
            else return -1
          }else{
            if(new Date(a_lastmsg.time) > new Date(b_lastmsg.time)) return -1
            else return 1
          }
        }).map((contact,i) => {
          if(!contact.chat || !contact.chat.length) return null
          var lastmsg = contact.chat.at(-1)

          return(
            <div className="all-chat-container prtclk active-dark-lit"
                key={contact.id} onClick={clickChat} value={contact.id}>
              <Image src={contact.img} dir="asset/whatsapp/pfp" w={48}/>
              <div className="short-info">
                <div className="chat-info">
                  <div className="chat-name">{contact.name}</div>
                  <div className="chat-date">{new Date(lastmsg.time).time12()}</div>
                </div>
                <div className="latest-message-container">
                  <div className="latest-message">
                    {lastmsg.type=="2"?(
                      <Icon className="seentick" mui={lastmsg.seen==0?"Done":null}
                        icon={lastmsg.seen>0?"seentick":null} w={14} payload={lastmsg.seen}/>
                    ):null}
                    {lastmsg.media=="Photo"?<Icon mui="Photo" w={14}/>:null}
                    {lastmsg.media=="Video"?<Icon mui="Videocam" w={14}/>:null}
                    <span className="last-msg-txt">{lastmsg.msg || lastmsg.media}</span>
                  </div>
                  <div className="chat-acts">
                    {lastmsg.type=="0" && !contact.seen && <div className="unread">{1}</div>}
                    {/* {i<3 ? <Icon icon="pinned" fill="#687881"/>: null} */}
                  </div>
                </div>
              </div>
            </div>
          )
        })}
      </div>
    </div>
  )
}


================================================
FILE: src/containers/apps/whatsapp/whatsapp.scss
================================================
.teal-green {
  color: var(--teal);
}

.wp-inner-wrapper {
  // font-family: "Sans-serif";
}

.whatsapp-home {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.whatsapp-top-nav {
  width: 100%;
  display: flex;
  position: relative;
  justify-content: space-between;
  padding: 0.5em 0.6em 0.5em 0.2em;
  background: var(--teal);
  color: var(--white);

  .brand-name {
    font-weight: 400;
    font-size: 1.25em;
    padding-left: 0.4em;
    letter-spacing: 1px;
  }

  .w-nav-icons {
    display: flex;

    .uicon {
      margin-left: 1em;
    }
  }
}

.w-nav-tab {
  width: 100%;
  display: flex;
  justify-content: space-between;
  position: relative;

  .nav-slider {
    height: 4px;
    position: absolute;
    background: var(--white);
    bottom: 0;
    transition: all 200ms ease-in-out;
  }

  .tab-option {
    font-weight: 200;
    font-size: 0.88em;
    padding: 0.5em;
    opacity: 0.5;
    flex-grow: 1;
    text-align: center;
    margin: 0 0.5em;

    &[value="true"] {
      opacity: 1;
    }
  }
}

.home-nav-tab {
  background: var(--teal);
  color: var(--white);

  .tab-option:first-child {
    flex-grow: 0;
    margin: 0;
  }
}

.whatsapp-home-page {
  width: 100%;
  flex-grow: 1;
  overflow: hidden;

  .call-logs-container,
  .chats-status-container,
  .home-chats-container {
    height: 100%;
    overflow-y: scroll;
  }
}

.home-chats {
  height: max-content;
  padding: 0.5em;
  color: var(--med-txt);

  .all-chat-container {
    padding: 0.5em 0;
    display: flex;

    .short-info {
      flex-grow: 1;
      margin-left: 0.8em;
      position: relative;
    }

    .imageCont {
      border-radius: 50%;
      overflow: hidden;
    }

    .chat-info {
      display: flex;

      .chat-name {
        flex-grow: 1;
        font-size: 1.1em;
        font-weight: 600;
      }

      .chat-date {
        font-size: 0.72em;
        font-weight: 400;
        color: var(--sat-txt);
      }
    }

    .latest-message-container {
      display: flex;
      position: absolute;
      width: 100%;
    }

    .latest-message {
      flex-grow: 1;
      display: flex;
      font-size: 0.84em;
      font-weight: 500;
      color: var(--sat-txt);
      fill: var(--sat-txt);
      position: relative;
      max-width: 100%;

      .uicon {
        margin-right: 0.2em;
      }

      span {
        overflow: hidden;
        display: -webkit-box;
        -webkit-line-clamp: 1;
        -webkit-box-orient: vertical;
        text-overflow: ellipsis;
      }
    }

    .chat-acts {
      display: flex;

      .unread {
        font-size: 0.64em;
        padding: 0.2em 0.76em;
        border-radius: 50%;
        margin: 0 0.5em;
        color: var(--white);
        background: var(--light-green);
      }
    }
  }
}

.seentick {
  &[data-payload="2"] {
    color: var(--blue);
    fill: var(--blue);
  }
}

.quick-wtool-container {
  position: absolute;
  bottom: 1em;
  right: 1em;
  padding: 0.8em;
  border-radius: 3em;
  background: var(--light-teal);
  color: var(--white);
}

.camera-container {
  height: 100%;
  overflow: hidden;
  background: black;
}

.all-contacts,
.call-logs,
.chats-status,
.my-status {
  height: max-content;
  color: var(--med-txt);

  .call-log,
  .chat-status,
  .contact-cotainer {
    display: flex;
    align-items: center;
    padding: 0.5em 1em;
    // margin-bottom: 1em;
    .status-preview-container {
      position: relative;
      // background: var(--teal);
      border-radius: 50%;
      padding: 3px;
      // overflow: hidden;
      .status-preview {
        position: absolute;
        width: 100%;
        height: 100%;
        inset: 0;
        // transform: scale(-1, 1);
        svg {
          width: 100%;
          height: 100%;
        }

        .notviewed {
          stroke: var(--teal);
        }

        .viewed {
          stroke: var(--light-txt);
        }
      }

      .imageCont {
        border: 1px solid var(--white);
      }
    }
  }
}

.call-logs {
  padding-top: 0.5em;

  .status-date {
    display: flex;

    .callConnected,
    .callMade {
      color: var(--light-green);
    }

    .callMissed {
      color: #eb3434;
    }
  }
}

.scale-trans {
  opacity: 1;
  transform: scale(1);
  transition: 200ms ease-in-out;
  pointer-events: auto;
  transition-property: transform, opacity;

  &[data-vis="0"] {
    transform: scale(0.9);
    pointer-events: none;
    opacity: 0;
  }

  &[data-vis="2"] {
    transform: scale(1.1);
    pointer-events: none;
    opacity: 0;
  }
}

// all contacts container
.contacts-container {
  position: absolute;
  inset: 0;

  .contact-cotainer {
    .imageCont, .uicon{
      min-width: 38px;
      min-height: 38px;
      color: #fefefe;
      background: var(--light-teal);
    }
  }
}

.all-contacts {
  padding-top: 0.5em;
  height: 100%;
  overflow-y: scroll;

  .status-date {
    flex-grow: 1;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    text-overflow: ellipsis;
  }
}


================================================
FILE: src/containers/apps/youtube/extra.js
================================================
import React, {useState, useEffect, useRef} from 'react';
import {useSelector, useDispatch} from 'react-redux';

import {Icon, Image, Video} from 'components/utils.js';
import {dispatchAction, dispatchAct} from 'store/actions';
import './extra.scss';

const posneg = (a,b,seed) => {
  var sa = a.csum(), sb = b.csum(), sd = seed.csum()
  return (sa + sb)%2 == sd%2==0 ? 1:-1
}

const pseudorandom = (arr,seed)=>{
  arr = arr.filter(x => x!= seed)
  return arr.sort((a,b) => posneg(a,b,seed))
}

export const ViewPage = (props)=>{
  const [liked, setLiked] = useState(-1)
  const [loadSugg, setLoad] = useState(false)
  const ytwatch = useRef()
  const ydata = useSelector(state => state.youtube)
  const watvid = useSelector(state => {
    var id = state.youtube.watch && state.youtube.watch.id
    return state.youtube.vids && state.youtube.vids[id]
  })

  const vidch = useSelector(state => {
    var id = state.youtube.watch && state.youtube.watch.id,
        channel = {...state.youtube.channels && state.youtube.channels[
          state.youtube.vids[id] && state.youtube.vids[id].channel
        ]}

    if(channel) channel.subd = state.youtube.subd && state.youtube.subd.includes(channel.id)
    return channel
  })

  const handleFullReq = (e)=>{
    if(ydata.comp) return
    dispatchAct({type: 'youtube/setProp', payload:{
      key: 'comp', value: true
    }})
  }

  const handleLike = (e)=>{
    setLiked(e.target.dataset.payload)
  }

  useEffect(()=>{
    if(watvid && watvid.id){
      setLoad(true)
      setLiked(-1)
      if(ytwatch.current) ytwatch.current.scrollTo(0,0)
      setTimeout(()=> {
        setLoad(false)
      }, 1000)
    }
  },[watvid])

  useEffect(()=>{
    dispatchAct({type: 'home/setAppKey', payload:{
      id: 'youtube', key: 'comp', value: ydata.comp
    }})
  }, [ydata.comp])

  return watvid ? (
    <div className={`yt-vid-view ${!ydata.comp ? 'mini-yt-vid':''}`}>
      <div className="yt-vid-box">
        <Video src={"https://youtube.com/embed/"+watvid.id}
                playIcon w="100%" h="100%"/>
      </div>
      <div className="yt-watch-container" ref={ytwatch}>
        <div className="yt-vid-info" onClick={handleFullReq}>
          <div className="vid-title txt-ovf">{watvid.title}</div>
          <div className="vid-stat">
            {ydata.comp ? (
              `${watvid.views.quantf()} views • ${new Date(watvid.upload).minifyDate()}`
            ): `${vidch.name}`}
          </div>
          {ydata.comp && (
            <div className="vid-acts">
              <div className="flex flex-col items-center prtclk"
                      onClick={handleLike} data-payload="1">
                <Icon mui="ThumbUp" out={liked!=1}/>
                <span>{watvid.likes.quantf()}</span>
              </div>
              <div className="flex flex-col items-center prtclk"
                      onClick={handleLike} data-payload="0">
                <Icon mui="ThumbDown" out={liked!=0}/>
                <span>{watvid.dislikes.quantf()}</span>
              </div>
              <div className="flex flex-col items-center">
                <Icon mui="Reply" flip/><span>Share</span>
              </div>
              <div className="flex flex-col items-center">
                <Icon mui="Download"/><span>Download</span>
              </div>
              <div className="flex flex-col items-center">
                <Icon mui="LibraryAdd" out/><span>Save</span>
              </div>
            </div>
          )}
        </div>
        {!ydata.comp && (
          <div className="yt-mini-ctrl">
            <Icon className="mx-1" mui="PlayArrow" w={28} onClick={handleFullReq}/>
            <Icon className="mx-1" mui="Close" w={28} action="youtube/closeVid"/>
          </div>
        )}
        {ydata.comp && (
          <>
          <div className="yt-channel-row">
            <div className="flex items-center">
              <Image src={vidch.pfp} dir="asset/youtube/pfp" w={36}/>
              <div>
                <div className="leading-4">{vidch.name}</div>
                <div className="text-xss light-txt">{vidch.subs.quantf()} subscribers</div>
              </div>
            </div>
            <div className="subscribed-container">
              <div className={`text-sm mr-4 ${!vidch.subd?'red-txt':''}`} onClick={dispatchAction}
                  data-action="youtube/toggleSub" data-payload={vidch.id}>
                {vidch.subd ? "SUBSCRIBED":"SUBSCRIBE"}
              </div>
              {vidch.subd && <Icon mui="NotificationsActive"/>}
            </div>
          </div>
          <div className="yt-comment-box">
            <div className="text-sm">Comments are turned off&nbsp;
              {/* <span className="light-txt font-thin text-sm">{269}</span> */}
            </div>
          </div>
          <div className="yt-sugg-container">
            {!loadSugg && ydata.home && pseudorandom(ydata.home, watvid.id).splice(0,10).map((item,i)=>{
              var vid = ydata.vids[item],
                  channel = ydata.channels[vid.channel] || {}

              return (
                <div className="yt-item-container prtclk" onClick={dispatchAction}
                  data-action="youtube/watchVideo" data-payload={vid.id} key={i}>
                  <div className="yt-vid-thumbnail">
                    <Image src={vid.thumb} dir="asset/youtube/thumbs"/>
                  </div>
                  <div className="yt-vid-info">
                    <Image src={channel.pfp} dir="asset/youtube/pfp" w={36}/>
                    <div className="flex-grow pl-2">
                      <div className="txt-ovf yt-vid-title">{vid.title}</div>
                      <div className="yt-vid-extra">
                        {channel.name} • {vid.views.quantf()} views • {new Date(vid.upload).minifyDate()}
                      </div>
                    </div>
                  </div>
                </div>
              )
            })}
            {loadSugg && (
              <div className="yt-item-container">
                <div className="yt-vid-thumbnail">
                  <Image src="" dir="asset/youtube/thumbs"/>
                </div>
              </div>
            )}
          </div>
          </>
        )}
      </div>
    </div>
  ) : null
}

export const TrendingPage = (props)=>{
  const ydata = useSelector(state => state.youtube)
  const [tab, setTab] = useState(0)

  const changeTab = (e) => setTab(e.target.dataset.id)
  const catg = ["Trending", "Music", "Gaming", "News", "Fashion and Beauty", "Learning"]

  return (
    <div className="yt-page yt-trending-page">
      <div className="yt-trending-catg">
        {catg.map((item, i) => {
          return (
            <div className="yt-catg prtclk softpress-in" key={item} data-id={i} onClick={changeTab}>
              <img src={"img/icon/other/" + item.toLowerCase().split(" ")[0] + ".svg"} alt="yticon"/>
              <span>{item}</span>
            </div>
          )
        })}
      </div>
      <hr/>
      <div className="yt-trending-container">
        <div className="yt-trending-title p-4">{catg[tab]}</div>
        {ydata.explore && ydata.explore[catg[tab].toLowerCase().split(" ")[0]].map((item,i)=>{
          var vid = ydata.vids[item],
              channel = ydata.channels[vid.channel] || {}

          return (
            <div className="yt-item-container prtclk" onClick={dispatchAction}
              data-action="youtube/watchVideo" data-payload={vid.id} key={i}>
              <div className="yt-vid-thumbnail">
                <Image src={vid.thumb} dir="asset/youtube/thumbs"/>
              </div>
              <div className="yt-vid-info">
                <Image src={channel.pfp} dir="asset/youtube/pfp" w={36}/>
                <div className="flex-grow pl-2">
                  <div className="txt-ovf yt-vid-title">{vid.title}</div>
                  <div className="yt-vid-extra">
                    {channel.name} • {vid.views.quantf()} views • {new Date(vid.upload).minifyDate()}
                  </div>
                </div>
              </div>
            </div>
          )
        })}
      </div>
    </div>
  )
}

export const SubsPage = (props)=>{
  const [chid, setId] = useState("")
  const ydata = useSelector(state => state.youtube)

  const handleChClick = (e)=>{
    var id = e.target.dataset.id
    if(chid==id) setId("")
    else setId(id)
  }

  return (
    <div className="yt-page">
      <div className="yt-subs-container">
        <div className="yt-channels-sc noscroll">
          <div className="yt-ch-body">
            {ydata.subd && ydata.subd.map((item, i)=>{
              var channel = ydata.channels[item] || {}
              return (
                <div className="yt-channel prtclk" onClick={handleChClick} key={i}
                  data-state={chid==""?0:(chid==item?1:2)} data-id={item}>
                  <Image src={channel.pfp} dir="asset/youtube/pfp" w={48}/>
                  <div className="txt-ovf ch-name">{channel.name}</div>
                </div>
              )
            })}
          </div>
        </div>
        <div className="ch-text-all">ALL</div>
      </div>
      <div className="yt-sub-vids">
        {ydata.vids && Object.keys(ydata.vids).sort((ka,kb)=>{
          return new Date(ydata.vids[ka].upload) < new Date(ydata.vids[kb].upload)?1:-1
        }).map((key, i)=>{
          var vid = ydata.vids[key],
              channel = ydata.channels[vid.channel] || {}

          return ydata.subd.includes(channel.id) && (
            chid=="" || chid==channel.id ) && (
            <div className="yt-item-container prtclk" onClick={dispatchAction}
                  data-action="youtube/watchVideo" data-payload={vid.id} key={i}>
              <div className="yt-vid-thumbnail">
                <Image src={vid.thumb} dir="asset/youtube/thumbs"/>
              </div>
              <div className="yt-vid-info">
                <Image src={channel.pfp} dir="asset/youtube/pfp" w={36}/>
                <div className="flex-grow pl-2">
                  <div className="txt-ovf yt-vid-title">{vid.title}</div>
                  <div className="yt-vid-extra">
                    {channel.name} • {vid.views.quantf()} views • {new Date(vid.upload).minifyDate()}
                  </div>
                </div>
              </div>
            </div>
          )
        })}
      </div>
    </div>
  )
}

export const LibPage = (props)=>{
  const ydata = useSelector(state => state.youtube)

  return (
    <div className="yt-page">
      <div className="yt-his-container">
        <div className="yt-his-title">
          <span>History</span>
          <span className="ch-text-all">VIEW ALL</span>
        </div>
        <div className="yt-his-vids noscroll">
          <div className="yt-his-scbody">
            {ydata.library && ydata.library.hist.map((key, i)=>{
              var vid = ydata.vids[key],
                  channel = ydata.channels[vid.channel] || {}

              return (
                <div className="yt-his-vid prtclk" onClick={dispatchAction}
                  data-action="youtube/watchVideo" data-payload={vid.id} key={i}>
                  <Image src={vid.thumb} dir="asset/youtube/thumbs"/>
                  <div className="txt-ovf his-vid-title">{vid.title}</div>
                  <div className="his-chname">{channel.name}</div>
                </div>
              )
            })}
          </div>
        </div>
      </div>
      <hr className="my-2"/>
      <div className="flex flex-col yt-lib-cont">
        <div className="flex px-4 py-3 items-center">
          <Icon className="yt-lib-icon" mui="Download" w={20}/>
          <div className="yt-dock-name text-sm ml-4">Downloads</div>
        </div>
        <div className="flex px-4 py-3 items-center">
          <Icon className="yt-lib-icon" mui="Theaters" w={20} out/>
          <div className="yt-dock-name text-sm ml-4">Your Movies</div>
        </div>
        <div className="flex px-4 py-3 items-center">
          <Icon className="yt-lib-icon" mui="AccessTime" w={20} out/>
          <div className="yt-dock-name text-sm ml-4">Watch Later</div>
        </div>
        <div className="flex px-4 py-3 items-center">
          <Icon className="yt-lib-icon" mui="ThumbUp" w={20} out/>
          <div className="yt-dock-name text-sm ml-4">Liked Videos</div>
        </div>
      </div>
      <hr className="my-4"/>
    </div>
  )
}

export const Home = (props)=>{
  const ydata = useSelector(state => state.youtube)

  return (
    <div className="yt-page yt-feed">
      {ydata.home && ydata.home.map((item,i)=>{
        var vid = ydata.vids[ydata.home[i%ydata.home.length]],
            channel = ydata.channels[vid.channel] || {}

        return (
          <div className="yt-item-container prtclk" onClick={dispatchAction}
            data-action="youtube/watchVideo" data-payload={vid.id} key={i}>
            <div className="yt-vid-thumbnail">
              <Image src={vid.thumb} dir="asset/youtube/thumbs"/>
            </div>
            <div className="yt-vid-info">
              <Image src={channel.pfp} dir="asset/youtube/pfp" w={36}/>
              <div className="flex-grow pl-2">
                <div className="txt-ovf yt-vid-title">{vid.title}</div>
                <div className="yt-vid-extra">
                  {channel.name} • {vid.views.quantf()} views • {new Date(vid.upload).minifyDate()}
                </div>
              </div>
            </div>
          </div>
        )
      })}
    </div>
  )
}


================================================
FILE: src/containers/apps/youtube/extra.scss
================================================
.yt-vid-view {
  position: absolute;
  width: 100%;
  height: 100%;
  max-height: 100%;
  bottom: 0;
  background: var(--app-bg);
  transition: max-height 200ms ease-in-out;

  display: flex;
  flex-direction: column;
  align-items: center;
}

.mini-yt-vid {
  bottom: 56px;
  max-height: 120px;
  height: auto;
  flex-direction: row;

  .yt-vid-box {
    width: 25%;
    height: 3.2em;
    overflow: hidden;
  }

  .yt-watch-container {
    width: 75%;
    flex-direction: row;
    justify-content: space-between;

    & > div{
      width: auto;
    }

    .yt-vid-info {
      padding: 0.5em;

      .vid-title {
        font-size: 0.8em;
        -webkit-line-clamp: 1;
      }
    }
  }
}

.yt-vid-box {
  width: 100%;
  height: 14em;
  background: #343434;

  .vidCont{
    width: 100%;
    height: 100%;
  }
}

.yt-watch-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  height: 50%;
  flex-grow: 1;
  overflow-y: scroll;

  & > div{
    width: 100%;
  }

  .yt-vid-info {
    padding: 0.5em 0.8em;

    .vid-title {
      font-size: 0.92em;
      -webkit-line-clamp: 2;
    }

    .vid-stat {
      font-weight: 500;
      color: var(--light-txt);
      font-size: 0.72em;
    }

    .vid-acts {
      display: flex;
      justify-content: space-around;
      font-size: 0.75em;
      color: var(--med-txt);
      margin: 1em 0;
    }
  }

  .yt-mini-ctrl{
    display: flex;
    padding: 0 4px;
    color: var(--med-txt);
  }

  .yt-channel-row {
    display: flex;
    align-items: center;
    position: relative;
    justify-content: space-between;
    border: solid 0 #ddd;
    border-width: 1px 0;

    .imageCont {
      padding: 0.5em;
      overflow: hidden;

      img {
        border-radius: 50%;
      }
    }

    .text-xss {
      font-weight: 500;
    }

    .subscribed-container {
      display: flex;
      align-items: center;
      padding: 0 1em;
    }

    .red-txt {
      color: #df0404;
    }
  }

  .yt-comment-box{
    padding: 0.5em 1em;
  }
}


================================================
FILE: src/containers/apps/youtube/index.js
================================================
import React, {useState, useEffect} from 'react';
import {useSelector, useDispatch} from 'react-redux';

import {Icon, Image, LazyComponent} from 'components/utils';
import {dispatchAction, dispatchAct} from 'store/actions';
import {Home, ViewPage, TrendingPage, SubsPage, LibPage} from './extra';
import './youtube.scss';

export const YoutubeApp = () => {
  const app = useSelector(state => state.home.apps.youtube || {})
  const home = useSelector(state => state.home)
  const show = home.ishome==false && home.stack.at(-1)==app.payload
  const pagetree = app && app.pagetree || {
    "main": {
      "home" : {},
      "subs" : {},
      "lib" : {}
    }
  }

  useEffect(()=>{
    if(app && !app.pagetree){
      dispatchAct({type: "home/setApp", payload: {
        id: app.payload,
        data: {
          ... app,
          pagetree: pagetree,
          path: ['main']
   
Download .txt
gitextract_7ne9rzeo/

├── .github/
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.yml
│   ├── dependabot.yml
│   └── workflows/
│       ├── Build-Deploy.yml
│       ├── Issue-text.yml
│       ├── PR-Preview.yml
│       ├── desktop.yml
│       ├── package-lock.yml
│       ├── virus.yml
│       └── winget.yml
├── .gitignore
├── LICENSE
├── README.md
├── jsconfig.json
├── package.json
├── public/
│   ├── _redirects
│   ├── img/
│   │   └── asset/
│   │       └── whatsapp/
│   │           └── pfp/
│   │               ├── apoc.jfif
│   │               ├── ava.jfif
│   │               ├── ceaser.jfif
│   │               ├── check.jfif
│   │               ├── fine.jfif
│   │               ├── liam.jfif
│   │               ├── lion.jfif
│   │               ├── pose.jfif
│   │               ├── real.jfif
│   │               └── viva.jfif
│   ├── index.html
│   ├── manifest.json
│   ├── robots.txt
│   └── site.webmanifest
├── src/
│   ├── App.css
│   ├── App.js
│   ├── components/
│   │   ├── background/
│   │   │   ├── back.scss
│   │   │   └── index.js
│   │   ├── bottomnav/
│   │   │   ├── bottom.scss
│   │   │   └── index.js
│   │   ├── icons.js
│   │   ├── main.scss
│   │   ├── quickpanel/
│   │   │   ├── index.js
│   │   │   └── qkpanel.scss
│   │   ├── statusbar.js
│   │   ├── utils.js
│   │   └── widgets/
│   │       ├── index.js
│   │       └── widget.scss
│   ├── containers/
│   │   ├── apps/
│   │   │   ├── google/
│   │   │   │   ├── google.scss
│   │   │   │   ├── index.js
│   │   │   │   └── news.json
│   │   │   ├── index.js
│   │   │   ├── index.scss
│   │   │   ├── playstore/
│   │   │   │   └── index.js
│   │   │   ├── themes.scss
│   │   │   ├── whatsapp/
│   │   │   │   ├── elements/
│   │   │   │   │   ├── extra.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── index.js
│   │   │   │   └── whatsapp.scss
│   │   │   └── youtube/
│   │   │       ├── extra.js
│   │   │       ├── extra.scss
│   │   │       ├── index.js
│   │   │       └── youtube.scss
│   │   └── home/
│   │       ├── home.scss
│   │       └── index.js
│   ├── index.css
│   ├── index.js
│   ├── service-worker.js
│   ├── serviceWorkerRegistration.js
│   └── store/
│       ├── actions/
│       │   ├── data/
│       │   │   ├── apps.js
│       │   │   ├── preset.js
│       │   │   ├── whatsapp.json
│       │   │   └── youtube.json
│       │   ├── index.js
│       │   └── prototypes.js
│       ├── index.js
│       └── reducers/
│           ├── apps/
│           │   ├── whatsapp.js
│           │   └── youtube.js
│           ├── global.js
│           ├── home.js
│           ├── quickpanel.js
│           ├── wallpaper.js
│           └── widget.js
├── src-tauri/
│   ├── .gitignore
│   ├── Cargo.toml
│   ├── build.rs
│   ├── icons/
│   │   └── icon.icns
│   ├── src/
│   │   └── main.rs
│   └── tauri.conf.json
├── tailwind.config.js
└── timeline.md
Download .txt
SYMBOL INDEX (9 symbols across 6 files)

FILE: src-tauri/build.rs
  function main (line 1) | fn main() {

FILE: src-tauri/src/main.rs
  function main (line 6) | fn main() {

FILE: src/App.js
  function App (line 11) | function App() {

FILE: src/components/quickpanel/index.js
  function QuickPanel (line 36) | function QuickPanel() {

FILE: src/containers/home/index.js
  function Home (line 21) | function Home() {

FILE: src/serviceWorkerRegistration.js
  function register (line 21) | function register(config) {
  function registerValidSW (line 55) | function registerValidSW(swUrl, config) {
  function checkValidServiceWorker (line 99) | function checkValidServiceWorker(swUrl, config) {
  function unregister (line 127) | function unregister() {
Condensed preview — 88 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (341K chars).
[
  {
    "path": ".github/CODEOWNERS",
    "chars": 48,
    "preview": ".github/* @andrewstech\nsrc-tauri/* @andrewstech\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 52,
    "preview": "custom: https://www.buymeacoffee.com/blueedgetechno\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 1023,
    "preview": "name: Bug Report\ndescription: File a bug report\ntitle: \"[Bug]: \"\nlabels: [\"bug\"]\nassignees:\n  - android69420\nbody:\n  - t"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 192,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: npm\n    directory: /\n    schedule:\n      interval: daily\n\n  - package-ecosyst"
  },
  {
    "path": ".github/workflows/Build-Deploy.yml",
    "chars": 572,
    "preview": "name: Build & Deploy\n\non:\n  push:\n    branches: [master]\n  workflow_dispatch:\n\njobs:\n  Build-Deploy:\n    runs-on: ubuntu"
  },
  {
    "path": ".github/workflows/Issue-text.yml",
    "chars": 866,
    "preview": "name: issue-text\n\non:\n  issues:\n    types: [opened]\n\njobs:\n  comment:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses:"
  },
  {
    "path": ".github/workflows/PR-Preview.yml",
    "chars": 2104,
    "preview": "name: Generate PR Preview\n\non:\n  pull_request_target:\n    types: [opened, reopened, synchronize]\n\njobs:\n  Build:\n    run"
  },
  {
    "path": ".github/workflows/desktop.yml",
    "chars": 1720,
    "preview": "name: publish\non:\n  issues:\n    types:\n      - labeled\njobs:\n  publish-tauri:\n    if: github.event.label.name == 'deskto"
  },
  {
    "path": ".github/workflows/package-lock.yml",
    "chars": 713,
    "preview": "name: \"Re generate the package-lock file\"\non:\n  workflow_dispatch:\n  \njobs:\n  rebuild:\n    name: reBuild\n    runs-on: wi"
  },
  {
    "path": ".github/workflows/virus.yml",
    "chars": 361,
    "preview": "name: released\n\non:\n  release:\n  workflow_dispatch:  \n\njobs:\n  virustotal:\n    runs-on: ubuntu-latest\n    steps:\n      -"
  },
  {
    "path": ".github/workflows/winget.yml",
    "chars": 303,
    "preview": "name: Publish to WinGet\non:\n  release:\n    types: [released]\njobs:\n  publish:\n    runs-on: windows-latest # action can o"
  },
  {
    "path": ".gitignore",
    "chars": 350,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\nnode_modules\n/.pnp"
  },
  {
    "path": "LICENSE",
    "chars": 7048,
    "preview": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n"
  },
  {
    "path": "README.md",
    "chars": 3557,
    "preview": "# Android on Web\n\n[![Follow me](https://img.shields.io/github/followers/blueedgetechno?label=follow%20me&style=social)]("
  },
  {
    "path": "jsconfig.json",
    "chars": 74,
    "preview": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \"src\"\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "package.json",
    "chars": 1803,
    "preview": "{\n  \"name\": \"Android11React\",\n  \"description\": \"Android made in React\",\n  \"repository\": \"https://github.com/blueedgetech"
  },
  {
    "path": "public/_redirects",
    "chars": 21,
    "preview": "/*  /index.html  200\n"
  },
  {
    "path": "public/index.html",
    "chars": 853,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"utf-8\" />\n  <link rel=\"icon\" href=\"%PUBLIC_URL%/favicon.ico\" /"
  },
  {
    "path": "public/manifest.json",
    "chars": 684,
    "preview": "{\n  \"short_name\": \"Android In React\",\n  \"name\": \"Android on web\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \""
  },
  {
    "path": "public/robots.txt",
    "chars": 57,
    "preview": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\n"
  },
  {
    "path": "public/site.webmanifest",
    "chars": 332,
    "preview": "{\n  \"name\": \"\",\n  \"short_name\": \"\",\n  \"icons\": [{\n    \"src\": \"/android-chrome-192x192.png\",\n    \"sizes\": \"192x192\",\n    "
  },
  {
    "path": "src/App.css",
    "chars": 2374,
    "preview": ".App {\n  --stbar-h: 2.4em;\n}\n\n*[data-ninja=\"true\"] {\n  pointer-events: none;\n}\n\n.uicon {\n  position: relative;\n  display"
  },
  {
    "path": "src/App.js",
    "chars": 646,
    "preview": "import React, {useState, useEffect} from 'react';\n\nimport {Background, OverLay} from 'components/background';\nimport Hom"
  },
  {
    "path": "src/components/background/back.scss",
    "chars": 343,
    "preview": ".background {\n  min-width: 100vw;\n  min-height: 100%;\n  background-color: #fefefe;\n  background-repeat: no-repeat;\n  bac"
  },
  {
    "path": "src/components/background/index.js",
    "chars": 599,
    "preview": "import React, { useState, useEffect } from 'react';\nimport { useSelector } from 'react-redux';\nimport \"./back.scss\";\n\nex"
  },
  {
    "path": "src/components/bottomnav/bottom.scss",
    "chars": 514,
    "preview": ".bottom-nav{\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  transition: background-color 200ms ease"
  },
  {
    "path": "src/components/bottomnav/index.js",
    "chars": 1503,
    "preview": "import React, { useState, useEffect, useRef} from 'react';\nimport { useSelector, useDispatch } from 'react-redux';\nimpor"
  },
  {
    "path": "src/components/icons.js",
    "chars": 3847,
    "preview": "export const NetworkIcon = (props) => {\n  // network icon that is responsive to a status props indicating network streng"
  },
  {
    "path": "src/components/main.scss",
    "chars": 3274,
    "preview": "body {\n  --dark-txt: #000;\n  --med-dark: #111;\n  --txt-col: #222;\n  --med-txt: #3c3c3c;\n  --comp-txt: #ddd;\n  --light-tx"
  },
  {
    "path": "src/components/quickpanel/index.js",
    "chars": 4805,
    "preview": "import React, { useState, useEffect } from 'react';\nimport { useSelector, useDispatch } from 'react-redux';\nimport Hamme"
  },
  {
    "path": "src/components/quickpanel/qkpanel.scss",
    "chars": 3521,
    "preview": ".quickpanel-container {\n  position: absolute;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  pointer-events: none;\n  backgro"
  },
  {
    "path": "src/components/statusbar.js",
    "chars": 2318,
    "preview": "import React, {useState, useEffect, useRef} from 'react';\nimport { useSelector } from 'react-redux';\nimport Hammer from "
  },
  {
    "path": "src/components/utils.js",
    "chars": 7792,
    "preview": "import React, { useState, useEffect, useRef } from 'react';\nimport {useSelector} from 'react-redux';\nimport { FontAwesom"
  },
  {
    "path": "src/components/widgets/index.js",
    "chars": 6187,
    "preview": "import React, { useState, useEffect } from 'react'\nimport { useSelector, useDispatch } from 'react-redux'\nimport Calenda"
  },
  {
    "path": "src/components/widgets/widget.scss",
    "chars": 4336,
    "preview": ".google-search-container {\n  width: 100%;\n  padding: 0 1em;\n  display: flex;\n}\n\n.google-input-container {\n  background: "
  },
  {
    "path": "src/containers/apps/google/google.scss",
    "chars": 1976,
    "preview": ".google-home, .google-spage, .search-frame {\n  padding: 1em 0;\n  height: 100%;\n  display: flex;\n  flex-direction: column"
  },
  {
    "path": "src/containers/apps/google/index.js",
    "chars": 5716,
    "preview": "import React, {useState, useEffect} from 'react'\nimport {useSelector, useDispatch} from 'react-redux'\n\nimport TextField "
  },
  {
    "path": "src/containers/apps/google/news.json",
    "chars": 63972,
    "preview": "{\n  \"articles\": [{\n      \"source\": {\n        \"id\": null,\n        \"name\": \"NDTV News\"\n      },\n      \"author\": \"NDTV Spor"
  },
  {
    "path": "src/containers/apps/index.js",
    "chars": 263,
    "preview": "import React, {useState, useEffect} from 'react';\nimport {useSelector, useDispatch} from 'react-redux';\n\nimport './index"
  },
  {
    "path": "src/containers/apps/index.scss",
    "chars": 702,
    "preview": ".app-wrapper{\n  pointer-events: auto;\n  height: 100%;\n  border-radius: 0;\n  transition: all 200ms ease-in-out;\n  animati"
  },
  {
    "path": "src/containers/apps/playstore/index.js",
    "chars": 868,
    "preview": "import React, {useState, useEffect} from 'react';\nimport {useSelector, useDispatch} from 'react-redux';\n\nimport {Icon, I"
  },
  {
    "path": "src/containers/apps/themes.scss",
    "chars": 910,
    "preview": ".app-wrapper{\n  --app-bg: #fefefe;\n  background: var(--app-bg);\n}\n\n.whatsapp-wrapper, .playstore-wrapper{\n  --app-bg: #f"
  },
  {
    "path": "src/containers/apps/whatsapp/elements/extra.scss",
    "chars": 5856,
    "preview": "// media player && status screen\n\n.media-viewer-container, .status-container{\n  position: absolute;\n  inset: 0;\n  backgr"
  },
  {
    "path": "src/containers/apps/whatsapp/elements/index.js",
    "chars": 19898,
    "preview": "import React, {useState, useEffect, useRef} from 'react'\nimport {useSelector, useDispatch} from 'react-redux'\n\nimport Te"
  },
  {
    "path": "src/containers/apps/whatsapp/index.js",
    "chars": 6983,
    "preview": "import React, {useState, useEffect, useRef} from 'react';\nimport {useSelector, useDispatch} from 'react-redux';\n\nimport "
  },
  {
    "path": "src/containers/apps/whatsapp/whatsapp.scss",
    "chars": 5019,
    "preview": ".teal-green {\n  color: var(--teal);\n}\n\n.wp-inner-wrapper {\n  // font-family: \"Sans-serif\";\n}\n\n.whatsapp-home {\n  display"
  },
  {
    "path": "src/containers/apps/youtube/extra.js",
    "chars": 13530,
    "preview": "import React, {useState, useEffect, useRef} from 'react';\nimport {useSelector, useDispatch} from 'react-redux';\n\nimport "
  },
  {
    "path": "src/containers/apps/youtube/extra.scss",
    "chars": 2024,
    "preview": ".yt-vid-view {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  max-height: 100%;\n  bottom: 0;\n  background: var(-"
  },
  {
    "path": "src/containers/apps/youtube/index.js",
    "chars": 2771,
    "preview": "import React, {useState, useEffect} from 'react';\nimport {useSelector, useDispatch} from 'react-redux';\n\nimport {Icon, I"
  },
  {
    "path": "src/containers/apps/youtube/youtube.scss",
    "chars": 4326,
    "preview": ".youtube-home {\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  height: 100%;\n"
  },
  {
    "path": "src/containers/home/home.scss",
    "chars": 4179,
    "preview": ".viewport {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  top: 0;\n  overflow: hidden;\n  display: flex;\n  flex-d"
  },
  {
    "path": "src/containers/home/index.js",
    "chars": 9096,
    "preview": "import React, {useState, useEffect, useRef} from 'react';\nimport {useSelector} from 'react-redux';\nimport Hammer from '@"
  },
  {
    "path": "src/index.css",
    "chars": 2445,
    "preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@import url('https://fonts.googleapis.com/css2?family=Poppin"
  },
  {
    "path": "src/index.js",
    "chars": 1064,
    "preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport store"
  },
  {
    "path": "src/service-worker.js",
    "chars": 2837,
    "preview": "/* eslint-disable no-restricted-globals */\n\n// This service worker can be customized!\n// See https://developers.google.c"
  },
  {
    "path": "src/serviceWorkerRegistration.js",
    "chars": 5064,
    "preview": "// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the ap"
  },
  {
    "path": "src/store/actions/data/apps.js",
    "chars": 4768,
    "preview": "var apps = [\n  {\n    name: 'Assistant',\n    icon: 'assistant',\n    type: 'app',\n    padd: true\n  },\n  {\n    name: 'BuyMe"
  },
  {
    "path": "src/store/actions/data/preset.js",
    "chars": 1439,
    "preview": "import apps from './apps'\n\nconst srandom = (seed)=>{\n  var x = Math.sin(seed++) * 10000\n  return x - Math.floor(x)\n}\n\nco"
  },
  {
    "path": "src/store/actions/data/whatsapp.json",
    "chars": 38600,
    "preview": "{\n  \"self\": {\n    \"name\": \"Blue\",\n    \"img\": \"blue.jpg\",\n    \"status\": [{\n      \"caption\": \"Particles - Blender 2.8\",\n  "
  },
  {
    "path": "src/store/actions/data/youtube.json",
    "chars": 16542,
    "preview": "{\n  \"home\": [\n    \"3izFMB91K_Q\",\n    \"KRvv0QdruMQ\",\n    \"6GlFF4BsO10\",\n    \"TRxf5hLZih4\",\n    \"6SrsZVdU740\"\n  ],\n  \"expl"
  },
  {
    "path": "src/store/actions/index.js",
    "chars": 8899,
    "preview": "import store from 'store';\nimport apps from './data/apps';\nimport {\n  favbar,\n  page1apps,\n  page2bar,\n  page2apps,\n  pa"
  },
  {
    "path": "src/store/actions/prototypes.js",
    "chars": 2984,
    "preview": "const {round, floor, random, min, max, abs} = Math;\n\nArray.prototype.remove = function(a) {\n  var idx = this.indexOf(a)\n"
  },
  {
    "path": "src/store/index.js",
    "chars": 827,
    "preview": "import { configureStore } from \"@reduxjs/toolkit\";\nimport thunk from \"redux-thunk\";\n\n// reducers who will be repsonsible"
  },
  {
    "path": "src/store/reducers/apps/whatsapp.js",
    "chars": 2224,
    "preview": "import { createSlice } from \"@reduxjs/toolkit\";\nvar initialState = {};\n\nconst closeExtras = (state, action) => {\n  if(ac"
  },
  {
    "path": "src/store/reducers/apps/youtube.js",
    "chars": 1012,
    "preview": "import { createSlice } from \"@reduxjs/toolkit\";\n\nvar initialState = {}\n\nconst YouTube = createSlice({\n  name: \"youtube\","
  },
  {
    "path": "src/store/reducers/global.js",
    "chars": 949,
    "preview": "import { createSlice } from \"@reduxjs/toolkit\";\n\nconst initialState = {\n  time: {\n    hours: 0,\n    minutes: 0,\n    abb:"
  },
  {
    "path": "src/store/reducers/home.js",
    "chars": 3335,
    "preview": "import { createSlice } from \"@reduxjs/toolkit\";\n\nvar initialState = {\n  slides: {\n    count: 4,\n    list: {}\n  },\n  apps"
  },
  {
    "path": "src/store/reducers/quickpanel.js",
    "chars": 2236,
    "preview": "import {\n  createSlice\n} from \"@reduxjs/toolkit\";\n\nconst intialState = {\n  open: false,\n  extended: false,\n  config: [{\n"
  },
  {
    "path": "src/store/reducers/wallpaper.js",
    "chars": 444,
    "preview": "import {createSlice} from '@reduxjs/toolkit';\n\nconst walls = [\"default.jpg\"]\nconst intialState = {wps: 0,src: walls[0]}\n"
  },
  {
    "path": "src/store/reducers/widget.js",
    "chars": 809,
    "preview": "import { createSlice } from \"@reduxjs/toolkit\";\n\nconst initialState = {\n  weather: {\n    city: \"New york\",\n    temperatu"
  },
  {
    "path": "src-tauri/.gitignore",
    "chars": 82,
    "preview": "# Generated by Cargo\n# will have compiled files and executables\n/target/\nWixTools\n"
  },
  {
    "path": "src-tauri/Cargo.toml",
    "chars": 868,
    "preview": "[package]\nname = \"app\"\nversion = \"0.1.0\"\ndescription = \"A Tauri App\"\nauthors = [\"you\"]\nlicense = \"\"\nrepository = \"\"\ndefa"
  },
  {
    "path": "src-tauri/build.rs",
    "chars": 37,
    "preview": "fn main() {\n  tauri_build::build()\n}\n"
  },
  {
    "path": "src-tauri/src/main.rs",
    "chars": 235,
    "preview": "#![cfg_attr(\n  all(not(debug_assertions), target_os = \"windows\"),\n  windows_subsystem = \"windows\"\n)]\n\nfn main() {\n  taur"
  },
  {
    "path": "src-tauri/tauri.conf.json",
    "chars": 1691,
    "preview": "{\n  \"$schema\": \"..\\\\node_modules/@tauri-apps/cli\\\\schema.json\",\n  \"build\": {\n    \"beforeBuildCommand\": \"\",\n    \"beforeDe"
  },
  {
    "path": "tailwind.config.js",
    "chars": 120,
    "preview": "module.exports = {\n  content: [\n    \"./src/**/*.{js,jsx,ts,tsx}\",\n  ],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n}\n"
  },
  {
    "path": "timeline.md",
    "chars": 2889,
    "preview": "## The major problems I faced during the development\n\n(ps: This is just the tip of iceberg)\n\n##### How do you change app"
  }
]

// ... and 11 more files (download for full content)

About this extraction

This page contains the full source code of the blueedgetechno/androidInReact GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 88 files (307.9 KB), approximately 91.7k tokens, and a symbol index with 9 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!