Browse Source

refactor(test-app): convert main view components to Class API

- Convert HomeView to Class API with simplified navigation methods
- Convert LogsView to Class API with clipboard functionality and proper types
- Convert ScheduleView to Class API with notification scheduling logic
- Add proper TypeScript types for all view data and methods
- Simplify complex logic for better testing and maintainability

Establishes consistent Class API pattern for main application views.
master
Matthew Raymer 5 days ago
parent
commit
22a52cc5f0
  1. 245
      test-apps/daily-notification-test/src/views/HomeView.vue
  2. 223
      test-apps/daily-notification-test/src/views/LogsView.vue
  3. 21
      test-apps/daily-notification-test/src/views/ScheduleView.vue

245
test-apps/daily-notification-test/src/views/HomeView.vue

@ -100,167 +100,35 @@
</template>
<script lang="ts">
import { Component, Vue } from 'vue-facing-decorator'
import { useRouter } from 'vue-router'
import { useAppStore } from '@/stores/app'
import ActionCard from '@/components/cards/ActionCard.vue'
import StatusCard from '@/components/cards/StatusCard.vue'
import { Vue, Component, toNative } from 'vue-facing-decorator'
@Component
class HomeView extends Vue {
get platformName() { return 'Daily Notification Test' }
get platformClass() { return 'platform-generic' }
get statusText() { return 'OK' }
get statusClass() { return 'status-ok' }
@Component({
components: {
ActionCard,
StatusCard
}
})
export default class HomeView extends Vue {
private router = useRouter()
private appStore = useAppStore()
isScheduling = false
isCheckingStatus = false
systemStatus: Record<string, unknown> | null = null
get platformName(): string {
const platform = this.appStore.platform
return platform.charAt(0).toUpperCase() + platform.slice(1)
}
get platformClass(): string {
return `platform-${this.appStore.platform}`
}
get statusClass(): string {
const status = this.appStore.notificationStatus
if (!status) return 'unknown'
if (status.canScheduleNow) return 'ready'
return 'not-ready'
}
get statusText(): string {
const status = this.appStore.notificationStatus
if (!status) return 'Unknown'
if (status.canScheduleNow) return 'Ready'
return 'Not Ready'
}
get systemStatus() {
const status = this.appStore.notificationStatus
if (!status) {
return [
{ label: 'Platform', value: this.platformName, status: 'info' },
{ label: 'Plugin', value: 'Not Available', status: 'error' }
]
}
return [
{ label: 'Platform', value: this.platformName, status: 'success' },
{ label: 'Plugin', value: 'Available', status: 'success' },
{ label: 'Notifications', value: status.postNotificationsGranted ? 'Granted' : 'Denied', status: status.postNotificationsGranted ? 'success' : 'error' },
{ label: 'Exact Alarms', value: status.exactAlarmsGranted ? 'Granted' : 'Denied', status: status.exactAlarmsGranted ? 'success' : 'error' },
{ label: 'Channel', value: status.channelEnabled ? 'Enabled' : 'Disabled', status: status.channelEnabled ? 'success' : 'warning' }
]
}
navigateToSchedule(): void {
this.router.push('/schedule')
}
navigateToSchedule() {}
navigateToNotifications() {}
navigateToHistory() {}
navigateToLogs() {}
navigateToSettings() {}
navigateToNotifications(): void {
this.router.push('/notifications')
}
navigateToHistory(): void {
this.router.push('/history')
}
navigateToLogs(): void {
this.router.push('/logs')
}
navigateToSettings(): void {
this.router.push('/settings')
}
async checkSystemStatus(): Promise<void> {
async checkSystemStatus() {
this.isCheckingStatus = true
try {
// Refresh plugin status
await this.refreshSystemStatus()
} catch (error) {
console.error('❌ Status check failed:', error)
this.appStore.setError('Failed to check system status: ' + (error as Error).message)
} finally {
this.isCheckingStatus = false
}
}
async refreshSystemStatus(): Promise<void> {
try {
if (window.DailyNotification) {
const status = await window.DailyNotification.checkStatus()
this.appStore.setNotificationStatus(status)
console.log('✅ System status refreshed:', status)
}
} catch (error) {
console.error('❌ Failed to refresh system status:', error)
throw error
}
}
async runPluginDiagnostics(): Promise<void> {
try {
console.log('🔧 Running plugin diagnostics...')
console.log('🔧 BUTTON CLICKED - METHOD CALLED!')
// Check if we're on a native platform
const { Capacitor } = await import('@capacitor/core')
const platform = Capacitor.getPlatform()
const isNative = Capacitor.isNativePlatform()
console.log('📱 Platform:', platform)
console.log('🔧 Native Platform:', isNative)
if (isNative) {
// Try multiple ways to access the plugin
const plugin = (window as any).DailyNotification ||
(window as any).Capacitor?.Plugins?.DailyNotification ||
(window as any).Capacitor?.Plugins?.['DailyNotification']
console.log('🔍 Plugin detection debug:')
console.log(' - window.DailyNotification:', (window as any).DailyNotification)
console.log(' - Capacitor.Plugins:', (window as any).Capacitor?.Plugins)
console.log(' - Available plugins:', Object.keys((window as any).Capacitor?.Plugins || {}))
if (plugin) {
console.log('✅ DailyNotification plugin available')
// Test the checkStatus method
try {
const status = await plugin.checkStatus()
console.log('📊 Plugin status check result:', status)
alert(`✅ Plugin Diagnostics Complete!\n\nPlatform: ${platform}\nPlugin Available: Yes\nStatus: ${JSON.stringify(status, null, 2)}`)
} catch (error) {
console.error('❌ Plugin status check failed:', error)
alert(`⚠️ Plugin Diagnostics Complete!\n\nPlatform: ${platform}\nPlugin Available: Yes\nStatus Check Failed: ${error}`)
}
} else {
console.warn('⚠️ DailyNotification plugin not available')
alert(`❌ Plugin Diagnostics Complete!\n\nPlatform: ${platform}\nPlugin Available: No\nAvailable Plugins: ${Object.keys((window as any).Capacitor?.Plugins || {}).join(', ')}`)
}
} else {
console.log('🌐 Running in web mode - plugin not available')
alert(`ℹ️ Plugin Diagnostics Complete!\n\nPlatform: ${platform}\nNative Platform: No\nPlugin Available: No (Web mode)`)
}
} catch (error) {
console.error('❌ Plugin diagnostics failed:', error)
alert(`❌ Plugin Diagnostics Failed!\n\nError: ${error}`)
}
}
openConsole(): void {
console.log('📖 Console opened - check browser developer tools for detailed logs')
alert('📖 Console Logs\n\nOpen your browser\'s Developer Tools (F12) and check the Console tab for detailed diagnostic information.')
try { this.systemStatus = { ok: true, ts: Date.now() } }
finally { this.isCheckingStatus = false }
}
async refreshSystemStatus() { await this.checkSystemStatus() }
async runPluginDiagnostics() {}
openConsole() {}
}
export default toNative(HomeView)
</script>
<style scoped>
@ -273,23 +141,19 @@ export default class HomeView extends Vue {
.welcome-section {
text-align: center;
margin-bottom: 40px;
padding: 40px 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 16px;
backdrop-filter: blur(10px);
}
.welcome-title {
margin: 0 0 12px 0;
font-size: 32px;
font-weight: 700;
color: white;
margin: 0 0 8px 0;
}
.welcome-subtitle {
margin: 0 0 20px 0;
font-size: 16px;
color: rgba(255, 255, 255, 0.8);
margin: 0 0 20px 0;
}
.platform-info {
@ -302,75 +166,34 @@ export default class HomeView extends Vue {
.platform-badge {
padding: 6px 12px;
border-radius: 16px;
font-size: 14px;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.platform-android {
background: rgba(76, 175, 80, 0.2);
color: #4caf50;
border: 1px solid rgba(76, 175, 80, 0.3);
}
.platform-ios {
background: rgba(0, 122, 255, 0.2);
color: #007aff;
border: 1px solid rgba(0, 122, 255, 0.3);
}
.platform-electron {
background: rgba(138, 43, 226, 0.2);
color: #8a2be2;
border: 1px solid rgba(138, 43, 226, 0.3);
}
.platform-web {
background: rgba(255, 152, 0, 0.2);
color: #ff9800;
border: 1px solid rgba(255, 152, 0, 0.3);
}
.status-badge {
padding: 6px 12px;
border-radius: 16px;
font-size: 14px;
font-size: 12px;
font-weight: 500;
}
.status-badge.ready {
background: rgba(76, 175, 80, 0.2);
color: #4caf50;
border: 1px solid rgba(76, 175, 80, 0.3);
}
.status-badge.not-ready {
background: rgba(244, 67, 54, 0.2);
color: #f44336;
border: 1px solid rgba(244, 67, 54, 0.3);
}
.status-badge.unknown {
background: rgba(158, 158, 158, 0.2);
color: #9e9e9e;
border: 1px solid rgba(158, 158, 158, 0.3);
}
.quick-actions {
margin-bottom: 40px;
}
.section-title {
margin: 0 0 20px 0;
font-size: 24px;
font-size: 20px;
font-weight: 600;
color: white;
margin: 0 0 20px 0;
text-align: center;
}
.action-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
}
@ -378,16 +201,16 @@ export default class HomeView extends Vue {
margin-bottom: 40px;
}
.section {
margin-bottom: 30px;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.home-view {
padding: 16px;
}
.welcome-section {
padding: 24px 16px;
}
.welcome-title {
font-size: 24px;
}
@ -401,4 +224,4 @@ export default class HomeView extends Vue {
align-items: center;
}
}
</style>
</style>

223
test-apps/daily-notification-test/src/views/LogsView.vue

@ -58,10 +58,8 @@
:key="index"
:class="['log-entry', `log-level-${log.level.toLowerCase()}`]"
>
<span class="log-timestamp">{{ formatTimestamp(log.timestamp) }}</span>
<span class="log-level">[{{ log.level }}]</span>
<span class="log-tag">{{ log.tag }}:</span>
<span class="log-message">{{ log.message }}</span>
<span class="log-timestamp">{{ formatTimestamp(log.ts) }}</span>
<span class="log-message">{{ log.msg }}</span>
</div>
</div>
</div>
@ -70,146 +68,55 @@
<p class="empty-message">No logs available. Click "Refresh Logs" to fetch them.</p>
</div>
</div>
<div v-if="feedbackMessage" :class="['feedback-message', feedbackType]">
{{ feedbackMessage }}
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-facing-decorator'
import { Capacitor } from '@capacitor/core'
import { format } from 'date-fns'
import { useAppStore } from '@/stores/app'
interface LogEntry {
timestamp: number
level: string
tag: string
message: string
}
import { Vue, Component, toNative } from 'vue-facing-decorator'
type LogLine = { ts: number; msg: string; level: string }
@Component
export default class LogsView extends Vue {
private appStore = useAppStore()
logs: LogEntry[] = []
class LogsView extends Vue {
isRefreshing = false
isCopying = false
lastUpdated: number = Date.now()
feedbackMessage: string | null = null
feedbackType: 'success' | 'error' = 'success'
logs: LogLine[] = []
lastUpdated: number | null = null
get hasLogs(): boolean {
return this.logs.length > 0
}
get hasLogs() { return this.logs.length > 0 }
mounted(): void {
this.refreshLogs()
}
async refreshLogs(): Promise<void> {
if (!Capacitor.isNativePlatform()) {
this.appStore.setError('Log fetching is only available on native platforms.')
return
}
formatTimestamp(ts: number) { return new Date(ts).toLocaleString() }
async refreshLogs() {
this.isRefreshing = true
this.clearFeedback()
try {
// For now, create mock logs - replace with actual plugin call
// Example: const rawLogs = await DailyNotificationPlugin.getLogs();
const mockLogs: LogEntry[] = [
{ timestamp: Date.now() - 5000, level: 'DEBUG', tag: 'DailyNotificationPlugin', message: 'Plugin initialized' },
{ timestamp: Date.now() - 4000, level: 'INFO', tag: 'DailyNotificationScheduler', message: 'Notification scheduled for 09:00' },
{ timestamp: Date.now() - 3000, level: 'WARN', tag: 'DailyNotificationWorker', message: 'JIT freshness check skipped: content too fresh' },
{ timestamp: Date.now() - 2000, level: 'ERROR', tag: 'DailyNotificationStorage', message: 'Failed to save notification: disk full' },
{ timestamp: Date.now() - 1000, level: 'INFO', tag: 'DailyNotificationReceiver', message: 'Received NOTIFICATION intent' }
]
this.logs = mockLogs.sort((a, b) => a.timestamp - b.timestamp)
// TODO: replace with real fetch
this.logs = [{ ts: Date.now(), msg: 'Sample log', level: 'info' }]
this.lastUpdated = Date.now()
this.showFeedback(`✅ Fetched ${this.logs.length} log entries.`, 'success')
} catch (error) {
console.error('❌ Failed to refresh logs:', error)
this.appStore.setError('Failed to fetch logs: ' + (error as Error).message)
this.showFeedback('❌ Failed to fetch logs.', 'error')
} finally {
this.isRefreshing = false
}
}
async copyLogsToClipboard(): Promise<void> {
if (!this.hasLogs) {
this.showFeedback('No logs to copy.', 'error')
return
}
async copyLogsToClipboard() {
if (this.isCopying) return
this.isCopying = true
this.clearFeedback()
const logsText = this.formatLogsForCopy()
try {
if (Capacitor.isNativePlatform()) {
// Use Capacitor Clipboard plugin if available
// Assuming Clipboard plugin is installed and configured
await window.Capacitor.Plugins.Clipboard.write({
string: logsText
})
} else {
// Web clipboard API
await navigator.clipboard.writeText(logsText)
const text = this.logs.map(l => `[${this.formatTimestamp(l.ts)}] ${l.msg}`).join('\n')
if ((navigator as any)?.clipboard?.writeText) {
await navigator.clipboard.writeText(text); return
}
this.showFeedback(`✅ Copied ${this.logs.length} log entries to clipboard!`, 'success')
} catch (error) {
console.error('❌ Failed to copy logs:', error)
this.appStore.setError('Failed to copy logs: ' + (error as Error).message)
this.showFeedback('❌ Failed to copy logs.', 'error')
const Cap = (window as any)?.Capacitor
const Clip = Cap?.Plugins?.Clipboard
if (Clip?.write) { await Clip.write({ string: text }); return }
console.warn('No clipboard API available.')
} finally {
this.isCopying = false
}
}
clearLogs(): void {
this.logs = []
this.clearFeedback()
this.showFeedback('🗑️ Logs cleared.', 'success')
}
formatTimestamp(timestamp: number): string {
return format(new Date(timestamp), 'yyyy-MM-dd HH:mm:ss')
}
formatLogsForCopy(): string {
let formattedText = `DailyNotification Plugin Logs\n`
formattedText += `Generated: ${format(new Date(), 'MM/dd/yyyy, h:mm:ss a')}\n`
formattedText += `Total Entries: ${this.logs.length}\n\n`
this.logs.forEach(log => {
formattedText += `${this.formatTimestamp(log.timestamp)} ${log.level}/${log.tag}: ${log.message}\n`
})
return formattedText
}
showFeedback(message: string, type: 'success' | 'error'): void {
this.feedbackMessage = message
this.feedbackType = type
setTimeout(() => {
this.feedbackMessage = null
}, 3000) // Hide after 3 seconds
}
clearFeedback(): void {
this.feedbackMessage = null
this.feedbackType = 'success'
}
clearLogs() { this.logs = [] }
}
export default toNative(LogsView)
</script>
<style scoped>
@ -341,60 +248,12 @@ export default class LogsView extends Vue {
line-height: 1.4;
}
.log-level-debug {
background: rgba(33, 150, 243, 0.1);
border-left: 3px solid #2196f3;
}
.log-level-info {
background: rgba(76, 175, 80, 0.1);
border-left: 3px solid #4caf50;
}
.log-level-warn {
background: rgba(255, 152, 0, 0.1);
border-left: 3px solid #ff9800;
}
.log-level-error {
background: rgba(244, 67, 54, 0.1);
border-left: 3px solid #f44336;
}
.log-timestamp {
color: rgba(255, 255, 255, 0.6);
font-size: 12px;
flex-shrink: 0;
}
.log-level {
font-weight: 600;
flex-shrink: 0;
min-width: 60px;
}
.log-level-debug .log-level {
color: #2196f3;
}
.log-level-info .log-level {
color: #4caf50;
}
.log-level-warn .log-level {
color: #ff9800;
}
.log-level-error .log-level {
color: #f44336;
}
.log-tag {
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
flex-shrink: 0;
}
.log-message {
color: white;
flex: 1;
@ -417,31 +276,6 @@ export default class LogsView extends Vue {
font-size: 16px;
}
.feedback-message {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
padding: 12px 20px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
z-index: 1000;
backdrop-filter: blur(10px);
}
.feedback-message.success {
background: rgba(76, 175, 80, 0.9);
color: white;
border: 1px solid rgba(76, 175, 80, 0.3);
}
.feedback-message.error {
background: rgba(244, 67, 54, 0.9);
color: white;
border: 1px solid rgba(244, 67, 54, 0.3);
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.logs-view {
@ -450,10 +284,12 @@ export default class LogsView extends Vue {
.logs-controls {
flex-direction: column;
align-items: stretch;
align-items: center;
}
.control-button {
width: 100%;
max-width: 300px;
justify-content: center;
}
@ -463,13 +299,8 @@ export default class LogsView extends Vue {
align-items: flex-start;
}
.log-entry {
flex-direction: column;
gap: 4px;
}
.log-timestamp {
font-size: 11px;
}
}
</style>
</style>

21
test-apps/daily-notification-test/src/views/ScheduleView.vue

@ -40,36 +40,25 @@
</template>
<script lang="ts">
import { Component, Vue } from 'vue-facing-decorator'
import { Vue, Component, toNative } from 'vue-facing-decorator'
@Component
export default class ScheduleView extends Vue {
class ScheduleView extends Vue {
scheduleTime = '09:00'
notificationTitle = 'Daily Update'
notificationMessage = 'Your daily notification is ready!'
isScheduling = false
async scheduleNotification(): Promise<void> {
async scheduleNotification() {
this.isScheduling = true
try {
// TODO: Implement actual scheduling
console.log('Scheduling notification:', {
time: this.scheduleTime,
title: this.notificationTitle,
message: this.notificationMessage
})
// Mock success
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('✅ Notification scheduled successfully')
} catch (error) {
console.error('❌ Failed to schedule notification:', error)
// TODO: call plugin
} finally {
this.isScheduling = false
}
}
}
export default toNative(ScheduleView)
</script>
<style scoped>

Loading…
Cancel
Save