last-open
v1.0.0
Published
Track last visit times with support for React, Vue, and Angular using unstorage
Downloads
4
Maintainers
Readme
last-open
Track last visit times with support for React, Vue, and Angular using unstorage.
Features
- 🔄 Cross-framework: Works with React, Vue, Angular, and vanilla JavaScript
- 💾 Flexible storage: Uses unstorage with localStorage by default, supports any storage backend
- ⚙️ Configurable: Custom keys, key factories, and storage instances
- 📦 Lightweight: Minimal dependencies, tree-shakeable
- 🔒 Type-safe: Written in TypeScript with full type definitions
- ✅ Well-tested: Comprehensive test coverage
Installation
npm install last-open unstorage
# or
pnpm add last-open unstorage
# or
yarn add last-open unstorageUsage
Vanilla JavaScript/TypeScript
import { initLastOpen, getLastOpen, getTimeSinceLastOpen } from 'last-open'
// Initialize on app start - records current timestamp
await initLastOpen()
// Get the last open timestamp
const lastOpen = await getLastOpen()
console.log('Last opened at:', new Date(lastOpen))
// Get time since last open (in milliseconds)
const timeSince = await getTimeSinceLastOpen()
console.log('Time since last open:', timeSince, 'ms')React
import { useLastOpen } from 'last-open/react'
function App() {
const { lastOpen, timeSince, isLoading, error } = useLastOpen()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
<h1>Welcome back!</h1>
{lastOpen && <p>Last visit: {new Date(lastOpen).toLocaleString()}</p>}
{timeSince && (
<p>Time since last visit: {Math.floor(timeSince / 1000)}s ago</p>
)}
</div>
)
}With custom configuration:
import { createStorage } from 'unstorage'
import indexedDbDriver from 'unstorage/drivers/indexeddb'
import { useLastOpen } from 'last-open/react'
const customStorage = createStorage({
driver: indexedDbDriver({ base: 'myapp:' }),
})
function UserDashboard({ userId }) {
const { lastOpen, timeSince, refresh } = useLastOpen({
storage: customStorage,
key: () => `user:${userId}:last-visit`, // Dynamic key per user
})
return (
<div>
<h2>Welcome back, User {userId}!</h2>
{lastOpen && <p>Last visit: {new Date(lastOpen).toLocaleString()}</p>}
<button onClick={refresh}>Refresh</button>
</div>
)
}Or use the provider component:
import { LastOpenProvider } from 'last-open/react'
function App() {
return (
<LastOpenProvider>
<YourApp />
</LastOpenProvider>
)
}With custom storage:
import { createStorage } from 'unstorage'
import indexedDbDriver from 'unstorage/drivers/indexeddb'
import { LastOpenProvider } from 'last-open/react'
const customStorage = createStorage({
driver: indexedDbDriver({ base: 'myapp:' }),
})
function App() {
return (
<LastOpenProvider
config={{
storage: customStorage,
key: 'my-last-visit',
}}
>
<YourApp />
</LastOpenProvider>
)
}Vue 3
<script setup>
import { useLastOpen } from 'last-open/vue'
const { lastOpen, timeSince, isLoading, error } = useLastOpen()
</script>
<template>
<div>
<div v-if="isLoading">Loading...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else>
<h1>Welcome back!</h1>
<p v-if="lastOpen">
Last visit: {{ new Date(lastOpen).toLocaleString() }}
</p>
<p v-if="timeSince">
Time since last visit: {{ Math.floor(timeSince / 1000) }}s ago
</p>
</div>
</div>
</template>With custom configuration:
<script setup>
import { createStorage } from 'unstorage'
import indexedDbDriver from 'unstorage/drivers/indexeddb'
import { useLastOpen } from 'last-open/vue'
const customStorage = createStorage({
driver: indexedDbDriver({ base: 'myapp:' }),
})
const props = defineProps<{ userId: string }>()
const { lastOpen, timeSince, refresh, clear } = useLastOpen({
storage: customStorage,
key: () => `user:${props.userId}:last-visit`,
})
</script>
<template>
<div>
<h2>Welcome back, User {{ userId }}!</h2>
<p v-if="lastOpen">Last visit: {{ new Date(lastOpen).toLocaleString() }}</p>
<button @click="refresh">Refresh</button>
<button @click="clear">Clear History</button>
</div>
</template>Or use the plugin:
import { createApp } from 'vue'
import { LastOpenPlugin } from 'last-open/vue'
import App from './App.vue'
const app = createApp(App)
app.use(LastOpenPlugin)
app.mount('#app')With custom configuration for the plugin:
import { createApp } from 'vue'
import { createStorage } from 'unstorage'
import indexedDbDriver from 'unstorage/drivers/indexeddb'
import { LastOpenPlugin } from 'last-open/vue'
import App from './App.vue'
const customStorage = createStorage({
driver: indexedDbDriver({ base: 'myapp:' }),
})
const app = createApp(App)
app.use(LastOpenPlugin, {
storage: customStorage,
key: 'my-last-visit',
})
app.mount('#app')Angular
import { Component, OnInit } from '@angular/core'
import { LastOpenService } from 'last-open/angular'
@Component({
selector: 'app-root',
template: `
<div *ngIf="lastOpenService.isLoading">Loading...</div>
<div *ngIf="lastOpenService.error">
Error: {{ lastOpenService.error.message }}
</div>
<div *ngIf="!lastOpenService.isLoading && !lastOpenService.error">
<h1>Welcome back!</h1>
<p *ngIf="lastOpenService.lastOpen">
Last visit: {{ lastOpenService.lastOpen | date: 'medium' }}
</p>
<p *ngIf="lastOpenService.timeSince">
Time since last visit:
{{ lastOpenService.timeSince / 1000 | number: '1.0-0' }}s ago
</p>
</div>
`,
})
export class AppComponent implements OnInit {
constructor(public lastOpenService: LastOpenService) {}
ngOnInit() {
// Service automatically initializes
}
}With custom configuration:
import { Component, OnInit } from '@angular/core'
import { createStorage } from 'unstorage'
import indexedDbDriver from 'unstorage/drivers/indexeddb'
import { LastOpenService } from 'last-open/angular'
const customStorage = createStorage({
driver: indexedDbDriver({ base: 'myapp:' }),
})
@Component({
selector: 'app-dashboard',
template: `
<div>
<h2>User Dashboard</h2>
<p *ngIf="lastOpenService.lastOpen">
Last visit: {{ lastOpenService.lastOpen | date: 'medium' }}
</p>
<button (click)="lastOpenService.refresh()">Refresh</button>
<button (click)="lastOpenService.clear()">Clear History</button>
</div>
`,
})
export class DashboardComponent implements OnInit {
constructor(public lastOpenService: LastOpenService) {}
ngOnInit() {
// Initialize with custom config
this.lastOpenService.initWithConfig({
storage: customStorage,
key: () => `user:${this.getUserId()}:last-visit`,
})
}
private getUserId(): string {
// Get user ID from auth service or route params
return 'user123'
}
}Configuration
Custom Storage Backend
import { createStorage } from 'unstorage'
import indexedDbDriver from 'unstorage/drivers/indexeddb'
import { initLastOpen } from 'last-open'
const storage = createStorage({
driver: indexedDbDriver({ base: 'app:' }),
})
await initLastOpen({ storage })Custom Key
import { initLastOpen } from 'last-open'
// Static key
await initLastOpen({ key: 'my-custom-key' })
// Dynamic key (e.g., per-user)
await initLastOpen({
key: () => `last-open:${getCurrentUserId()}`,
})Framework-Specific Configuration
React
const { lastOpen } = useLastOpen({
storage: myCustomStorage,
key: 'custom-key',
})Vue
const { lastOpen } = useLastOpen({
storage: myCustomStorage,
key: () => `user:${userId}:last-open`,
})Angular
constructor(private lastOpenService: LastOpenService) {
this.lastOpenService.initWithConfig({
storage: myCustomStorage,
key: 'custom-key'
})
}API
Core API
createLastOpenTracker(config?)
Creates a tracker instance with methods to manage last open timestamps.
const tracker = createLastOpenTracker({ key: 'my-key' })
await tracker.init()
const lastOpen = await tracker.getLastOpen()initLastOpen(config?)
Initialize and record current timestamp.
getLastOpen(config?)
Get the last open timestamp (milliseconds since epoch).
getTimeSinceLastOpen(config?)
Get time elapsed since last open (in milliseconds).
clearLastOpen(config?)
Clear the stored timestamp.
React API
useLastOpen(config?)
Hook that returns:
lastOpen: Last open timestamptimeSince: Time since last open (ms)isLoading: Loading stateerror: Error if anyrefresh(): Manually refresh valuesclear(): Clear stored timestamp
<LastOpenProvider>
Component that automatically initializes tracking on mount.
Vue API
useLastOpen(config?)
Composable that returns reactive refs:
lastOpen: Last open timestamptimeSince: Time since last open (ms)isLoading: Loading stateerror: Error if anyrefresh(): Manually refresh valuesclear(): Clear stored timestamp
LastOpenPlugin
Vue plugin that initializes tracking when the app mounts.
Angular API
LastOpenService
Injectable service with:
lastOpen: Last open timestamp (getter)timeSince: Time since last open (getter)isLoading: Loading state (getter)error: Error if any (getter)refresh(): Manually refresh valuesclear(): Clear stored timestampinitWithConfig(config): Initialize with custom config
TypeScript
Full TypeScript support with exported types:
import type { LastOpenConfig, LastOpenTracker, Storage } from 'last-open'License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
