{"id":18070074,"url":"https://github.com/polatengin/budapest","last_synced_at":"2026-05-09T09:52:27.798Z","repository":{"id":144741886,"uuid":"213463102","full_name":"polatengin/budapest","owner":"polatengin","description":"Logout user when inactive for a certain amount of time in a Typescript project","archived":false,"fork":false,"pushed_at":"2024-08-28T09:50:07.000Z","size":36,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-11T13:47:43.342Z","etag":null,"topics":["devcontainer","docker","github-action","idle","timeout","typescript","webpack"],"latest_commit_sha":null,"homepage":"https://polatengin-budapest.netlify.com","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/polatengin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-07T18:59:20.000Z","updated_at":"2020-07-02T11:18:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"633763c4-33bb-4977-a8da-904fc88b4a16","html_url":"https://github.com/polatengin/budapest","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polatengin%2Fbudapest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polatengin%2Fbudapest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polatengin%2Fbudapest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polatengin%2Fbudapest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/polatengin","download_url":"https://codeload.github.com/polatengin/budapest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247369927,"owners_count":20927927,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devcontainer","docker","github-action","idle","timeout","typescript","webpack"],"created_at":"2024-10-31T08:24:02.401Z","updated_at":"2026-05-09T09:52:27.766Z","avatar_url":"https://github.com/polatengin.png","language":"Dockerfile","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Netlify Status](https://api.netlify.com/api/v1/badges/ab879ede-6acc-4f7e-9337-44e14858ba45/deploy-status)](https://app.netlify.com/sites/polatengin-budapest/deploys)\r\n\r\n[![GitHub Actions Status](https://github.com/polatengin/budapest/workflows/Build%20and%20Publish/badge.svg)](https://github.com/polatengin/budapest/workflows/ci-and-cd)\r\n\r\n# Determine idle time in WepApps\r\n\r\nYou can use the running version of this project at [https://polatengin-budapest.netlify.com/](https://polatengin-budapest.netlify.com/)\r\n\r\n# What is idle timeout\r\n\r\n_Idle time_ is duration the user is remaining inactive on the web page. During _idle time_, user does nothing on the web page, he/she may even away from the computer.\r\n\r\nMany web apps, such as _banking_, _gaming_, etc. need to detect if the user is _idle_ to increase _performance_, _security_, etc.\r\n\r\nFor _high risky_ web apps, 2-3 minutes inactivity is risky, such as, finance apps, personal password vault apps, etc.\r\n\r\nFor _low risky_ web apps, sometimes 15-20 minutes inactivity is OK., such as, news apps, blog apps, etc.\r\n\r\nAlso, many web apps, need to have some calls to the backend or need to establish communication channels to backend, but when user is idle, it doesn't a requirement anymore. So, if the web app can detect the user is idle, then it can close communication channel and stop calling backend services to free-up resources on the backend side.\r\n\r\n# How to detect if the user is idle\r\n\r\nWhen user is on the page, he/she usually use peripherals to interact with the page, such as, _keyboard_, _mouse_, _touch screen_, etc.\r\n\r\nIt's possible to attach couple of [Events](https://developer.mozilla.org/en-US/docs/Web/API/Event) from [DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) and reset the timer if one of them triggered. If nothing happened in the past _for example 20 seconds_, we can conclude that the user is _idle_.\r\n\r\nHere is the few _events_ we can hook,\r\n\r\n* [Mouse Events](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)\r\n* [Keyboard Events](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent)\r\n* [Touch Events](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent)\r\n* [Global Events](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers)\r\n\r\n# Implementation\r\n\r\nStart by creating _npm project_ with executing following command in your favorite _Terminal_ (my favorite is [Microsoft Terminal](https://github.com/microsoft/terminal) 😀)\r\n\r\n```bash\r\nnpm init --force\r\n```\r\n\r\nIt's possible to develop code in [Typescript](https://www.typescriptlang.org/) and _transpile_ the code to [Javascript](https://en.wikipedia.org/wiki/JavaScript) with [webpack](https://webpack.js.org/). In order to prepare the project for that, let's add couple of _DevDependencies_;\r\n\r\n```bash\r\nnpm install typescript webpack webpack-cli --save-dev\r\n```\r\n\r\nAlso, it'll be needed to serve project locally for debug purposes, let's add [http-server](https://www.npmjs.com/package/http-server) as a _DevDependency_;\r\n\r\n```bash\r\nnpm install http-server --save-dev\r\n```\r\n\r\nAlso, it'll be needed to pre-process _html_ and _typescript_ files with _webpack_, for example to _minify_ them. Let's add couple of more _DevDependencies_;\r\n\r\n```bash\r\nnpm install clean-webpack-plugin copy-webpack-plugin html-minifier-webpack-plugin html-webpack-plugin ts-loader --save-dev\r\n```\r\n\r\nFinally, it'll be needed to _produce_ and _emit_ events to notify the page that user is inactive for certain amount of time, let's add [rxjs](https://rxjs.dev/) as a final dependency;\r\n\r\n```bash\r\nnpm install rxjs\r\n```\r\n\r\nNow, we're ready to develop code.\r\n\r\n\u003e Since we're good software engineers, we ❤ reusable code.\r\n\r\nLet's create [idle_timeout.src](./src/idle_timeout.ts) file in [src](./src/) folder.\r\n\r\nFirst, we need a class definition;\r\n\r\n```typescript\r\nimport { Observable, Observer } from 'rxjs';\r\n\r\nexport class IdleTimeOut {\r\n}\r\n```\r\n\r\nNow, we need a mechanism to prevent developers to instantiate the class more than 1 time. We're going to handle this requirement by implementing [Singleton Design Pattern](https://www.dofactory.com/javascript/singleton-design-pattern);\r\n\r\n```typescript\r\npublic static Current: IdleTimeOut = new IdleTimeOut();\r\n\r\nprivate constructor() {\r\n};\r\n```\r\n\r\nNow we need another mechanism to tap to few events;\r\n\r\n```typescript\r\n['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'load'].forEach((event) =\u003e {\r\n  document.addEventListener(event, this.Reset , true);\r\n});\r\n```\r\n\r\nSo, we can reset _idle timer_;\r\n\r\n```typescript\r\npublic Reset = () =\u003e {\r\n  this._tick_count = 0;\r\n  if (this._observer) {\r\n    this._observer.next(this._tick_count);\r\n  }\r\n\r\n  clearInterval(this._interval);\r\n  this._interval = setInterval(() =\u003e { this._observer.next(++this._tick_count); }, 1000);\r\n}\r\n```\r\n\r\nFinally, we only need to have an [Observable](http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html) to alert outside-world about the progress of idle time;\r\n\r\n```typescript\r\nprivate _tick_count: number = 0;\r\nprivate _interval: ReturnType\u003ctypeof setInterval\u003e;\r\n\r\nprivate _observer: Observer\u003cnumber\u003e;\r\n\r\npublic OnTick: Observable\u003cnumber\u003e = new Observable();\r\n\r\nthis.OnTick = Observable.create((observer: Observer\u003cnumber\u003e) =\u003e { this._observer = observer });\r\n```\r\n\r\nThat's it for the [IdleTimeOut](./src/idle_timeout.ts) component, continue with creating [index.html](./src/index.html) file under [src](./src/) folder with following content;\r\n\r\n```html\r\n\u003c!DOCTYPE html\u003e\r\n\u003chtml lang=\"en\"\u003e\r\n\u003chead\u003e\r\n  \u003cmeta charset=\"UTF-8\"\u003e\r\n  \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\r\n  \u003ctitle\u003eBudapest - Detect if the user is idle\u003c/title\u003e\r\n\u003c/head\u003e\r\n\u003cbody\u003e\r\n  \u003cspan\u003eIdle time: \u003c/span\u003e\u003cspan id=\"idle_time\"\u003e\u003c/span\u003e\r\n\u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\nNow, we can continue with creating [index.ts](./src/index.ts) file under [src](./src/) folder.\r\n\r\nFirst of all, we need to _import_ [IdleTimeOut](./src/idle_timeout.ts) component;\r\n\r\n```typescript\r\nimport { IdleTimeOut } from './idle_timeout';\r\n\r\nconst $idle_time = document.getElementById('idle_time');\r\n\r\nconsole.log(IdleTimeOut.Current);\r\n```\r\n\r\nWe can call `Reset()` method and reset the _idle timer_ programmatically;\r\n\r\n```typescript\r\nIdleTimeOut.Current.Reset();\r\n```\r\n\r\nAlso, we can attach to `OnTick` event and decide the actions, such as, when everything is fine, when to logout, etc.\r\n\r\n```typescript\r\nIdleTimeOut.Current.OnTick.subscribe(seconds =\u003e {\r\n  console.log(IdleTimeOut.Current);\r\n\r\n  if (seconds \u003c= 4) {\r\n    $idle_time.innerText = 'everything is fine! 😀';\r\n  } else if (seconds \u003e 20) {\r\n    document.location.href = '/logout';\r\n  } else {\r\n    $idle_time.innerText = `page has not been used for ${seconds} seconds`;\r\n  }\r\n});\r\n```\r\n\r\nHere is the example screenshot;\r\n\r\n![Sample Screenshot](./screen-shot.gif \"Sample Screenshot\")\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolatengin%2Fbudapest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpolatengin%2Fbudapest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolatengin%2Fbudapest/lists"}