native-update
Version:
Foundation package for building a comprehensive update system for Capacitor apps. Provides architecture and interfaces but requires backend implementation.
164 lines • 6.07 kB
JavaScript
import { Logger } from '../core/logger';
export class ReviewRateLimiter {
constructor(config) {
this.storageKey = 'cap_review_rate_limit';
// Platform-specific limits
this.IOS_MAX_PROMPTS_PER_YEAR = 3;
this.ANDROID_MIN_DAYS_BETWEEN = 30; // Recommended
this.DEFAULT_MIN_DAYS_BETWEEN = 90;
this.config = config;
this.logger = new Logger('ReviewRateLimiter');
}
async canRequestReview() {
const data = await this.getRateLimitData();
const platform = this.getPlatform();
// Check last request date
if (data.lastRequestDate) {
const daysSinceLastRequest = this.getDaysSince(data.lastRequestDate);
const minDays = this.getMinDaysBetweenPrompts(platform);
if (daysSinceLastRequest < minDays) {
return {
canRequest: false,
reason: `Only ${daysSinceLastRequest} days since last prompt, need ${minDays}`,
};
}
}
// iOS-specific: Check yearly limit
if (platform === 'ios') {
const currentYear = new Date().getFullYear().toString();
const requestsThisYear = data.requestsByYear[currentYear] || 0;
if (requestsThisYear >= this.IOS_MAX_PROMPTS_PER_YEAR) {
return {
canRequest: false,
reason: `iOS limit reached: ${requestsThisYear}/${this.IOS_MAX_PROMPTS_PER_YEAR} prompts this year`,
};
}
}
// Check version-specific limits
const currentVersion = await this.getCurrentVersion();
const requestsThisVersion = data.requestsByVersion[currentVersion] || 0;
const maxPerVersion = this.config.maxPromptsPerVersion || 1;
if (requestsThisVersion >= maxPerVersion) {
return {
canRequest: false,
reason: `Version limit reached: ${requestsThisVersion}/${maxPerVersion} prompts for v${currentVersion}`,
};
}
return {
canRequest: true,
};
}
async recordRequest(wasDisplayed = true) {
const data = await this.getRateLimitData();
const currentVersion = await this.getCurrentVersion();
const currentYear = new Date().getFullYear().toString();
// Update counters
data.totalRequests++;
data.lastRequestDate = Date.now();
if (wasDisplayed) {
data.successfulDisplays++;
}
// Update version-specific count
if (!data.requestsByVersion[currentVersion]) {
data.requestsByVersion[currentVersion] = 0;
}
data.requestsByVersion[currentVersion]++;
// Update year-specific count (for iOS)
if (!data.requestsByYear[currentYear]) {
data.requestsByYear[currentYear] = 0;
}
data.requestsByYear[currentYear]++;
await this.saveRateLimitData(data);
this.logger.log('Recorded review request', {
total: data.totalRequests,
version: currentVersion,
year: currentYear,
});
}
async reset() {
await this.saveRateLimitData({
totalRequests: 0,
successfulDisplays: 0,
requestsByVersion: {},
requestsByYear: {},
});
}
async getMetrics() {
const data = await this.getRateLimitData();
const installDate = await this.getInstallDate();
return {
totalRequests: data.totalRequests,
successfulDisplays: data.successfulDisplays,
lastRequestDate: data.lastRequestDate
? new Date(data.lastRequestDate)
: undefined,
requestsByVersion: data.requestsByVersion,
significantEvents: [], // This would come from conditions checker
appLaunches: 0, // This would come from conditions checker
installDate: new Date(installDate),
};
}
async getRateLimitData() {
try {
const stored = localStorage.getItem(this.storageKey);
if (stored) {
return JSON.parse(stored);
}
}
catch (error) {
this.logger.error('Failed to read rate limit data', error);
}
return {
totalRequests: 0,
successfulDisplays: 0,
requestsByVersion: {},
requestsByYear: {},
};
}
async saveRateLimitData(data) {
try {
localStorage.setItem(this.storageKey, JSON.stringify(data));
}
catch (error) {
this.logger.error('Failed to save rate limit data', error);
}
}
getDaysSince(timestamp) {
const msPerDay = 24 * 60 * 60 * 1000;
return Math.floor((Date.now() - timestamp) / msPerDay);
}
getMinDaysBetweenPrompts(platform) {
if (platform === 'android') {
return (this.config.minimumDaysSinceLastPrompt || this.ANDROID_MIN_DAYS_BETWEEN);
}
return (this.config.minimumDaysSinceLastPrompt || this.DEFAULT_MIN_DAYS_BETWEEN);
}
getPlatform() {
if (typeof window !== 'undefined') {
const userAgent = window.navigator.userAgent;
if (/android/i.test(userAgent)) {
return 'android';
}
else if (/iPad|iPhone|iPod/.test(userAgent)) {
return 'ios';
}
}
return 'web';
}
async getCurrentVersion() {
// This would be provided by the platform
return '1.0.0';
}
async getInstallDate() {
// This would be stored on first app launch
const installKey = 'cap_app_install_date';
const stored = localStorage.getItem(installKey);
if (stored) {
return parseInt(stored, 10);
}
const now = Date.now();
localStorage.setItem(installKey, now.toString());
return now;
}
}
//# sourceMappingURL=review-rate-limiter.js.map