diff --git a/docs/how-to-rate.md b/docs/how-to-rate.md new file mode 100644 index 0000000000..6498843a33 --- /dev/null +++ b/docs/how-to-rate.md @@ -0,0 +1,62 @@ +# How to Rate _Super Productivity_ + +## 🪟 Windows (Microsoft Store) + +1. Go to [webpage](https://apps.microsoft.com/detail/9nhfvg8361tw) OR open the **Microsoft Store** app and search for **Super Productivity** +2. Scroll to the **Rate and review** section +3. Click your star rating (⭐–⭐⭐⭐⭐⭐) +4. (Optional) Enter your feedback +5. Click **Submit** + +--- + +## 🖥️ macOS (Mac App Store) + +1. Open **App Store** on your Mac +2. Search for **Super Productivity** +3. Click the app’s page +4. Scroll down to **Ratings & Reviews** +5. Click the number of ⭐ stars you want to give +6. (Optional) Write a review +7. Click **Send** + +--- + +## 🤖 Android (Google Play Store) + +1. [Go to play store page](https://play.google.com/store/apps/details?id=com.superproductivity.superproductivity&hl=de) +2. Under **Rate this app**, tap the stars +3. (Optional) Tap **Write a review** +4. Tap **Post** + +--- + +## 🐧 Ubuntu Software Center + +1. Open **Ubuntu Software** +2. Search for **Super Productivity** +3. Click the app’s page +4. Click the ⭐ stars under the title +5. Sign in with your Ubuntu One account if prompted +6. (Optional) Add a review +7. Click **Submit** + +--- + +## 🧩 GNOME Software Center + +1. Launch **GNOME Software** +2. Search for or locate **Super Productivity** +3. Click the app to view its page +4. Click the ⭐ stars to rate +5. Authenticate if prompted +6. (Optional) Write a review +7. Click **Submit** + +--- + +# Other places to leave review + +https://www.producthunt.com/products/super-productivity +https://play.google.com/store/apps/details?id=com.superproductivity.superproductivity +https://www.pling.com/p/1352584/ diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 00bcd086d9..9cbb844882 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -62,6 +62,8 @@ import { LocalBackupService } from './imex/local-backup/local-backup.service'; import { DEFAULT_META_MODEL } from './pfapi/api/model-ctrl/meta-model-ctrl'; import { AppDataCompleteNew } from './pfapi/pfapi-config'; import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { DialogPleaseRateComponent } from './features/dialog-please-rate/dialog-please-rate.component'; const w = window as any; const productivityTip: string[] = w.productivityTips && w.productivityTips[w.randomIndex]; @@ -111,6 +113,7 @@ export class AppComponent implements OnDestroy { private _persistenceLegacyService = inject(PersistenceLegacyService); private _persistenceLocalService = inject(PersistenceLocalService); private _localBackupService = inject(LocalBackupService); + private _matDialog = inject(MatDialog); readonly syncTriggerService = inject(SyncTriggerService); readonly imexMetaService = inject(ImexViewService); @@ -193,6 +196,13 @@ export class AppComponent implements OnDestroy { (window as any).productivityTips[(window as any).randomIndex][1], }); } + + const appStarts = +(localStorage.getItem(LS.APP_START_COUNT) || 0); + if (appStarts === 45 || appStarts === 120) { + this._matDialog.open(DialogPleaseRateComponent); + } else { + localStorage.setItem(LS.APP_START_COUNT, (appStarts + 1).toString()); + } }, 1000); if (IS_ELECTRON) { diff --git a/src/app/core/persistence/storage-keys.const.ts b/src/app/core/persistence/storage-keys.const.ts index 40413eff9e..ea6cbfc2a9 100644 --- a/src/app/core/persistence/storage-keys.const.ts +++ b/src/app/core/persistence/storage-keys.const.ts @@ -13,6 +13,7 @@ export enum DB { // REAL LS export enum LS { + APP_START_COUNT = 'APP_START_COUNT', LAST_LOCAL_SYNC_MODEL_CHANGE = 'SUP_LAST_LOCAL_SYNC_MODEL_CHANGE', LOCAL_UI_HELPER = 'SUP_UI_HELPER', diff --git a/src/app/features/dialog-please-rate/dialog-please-rate.component.html b/src/app/features/dialog-please-rate/dialog-please-rate.component.html new file mode 100644 index 0000000000..03af1fcc6a --- /dev/null +++ b/src/app/features/dialog-please-rate/dialog-please-rate.component.html @@ -0,0 +1,27 @@ +

{{ T.F.D_RATE.TITLE | translate }}

+ + +
+

+ +

+ open_in_new + {{ T.F.D_RATE.A_HOW | translate }} +

+
+
+ + + + diff --git a/src/app/features/dialog-please-rate/dialog-please-rate.component.scss b/src/app/features/dialog-please-rate/dialog-please-rate.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/features/dialog-please-rate/dialog-please-rate.component.ts b/src/app/features/dialog-please-rate/dialog-please-rate.component.ts new file mode 100644 index 0000000000..de6cef5d20 --- /dev/null +++ b/src/app/features/dialog-please-rate/dialog-please-rate.component.ts @@ -0,0 +1,43 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { + MatDialogActions, + MatDialogClose, + MatDialogContent, + MatDialogTitle, +} from '@angular/material/dialog'; +import { T } from '../../t.const'; +import { MatAnchor, MatButton } from '@angular/material/button'; +import { TranslatePipe } from '@ngx-translate/core'; +import { MatIcon } from '@angular/material/icon'; + +@Component({ + selector: 'dialog-please-rate', + standalone: true, + imports: [ + MatDialogTitle, + MatDialogContent, + MatDialogActions, + MatButton, + MatIcon, + MatDialogClose, + MatAnchor, + TranslatePipe, + ], + templateUrl: './dialog-please-rate.component.html', + styleUrl: './dialog-please-rate.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DialogPleaseRateComponent { + protected readonly T = T; + + // IS_ELECTRON = IS_ELECTRON; + // IS_MAC = /Mac|iPod|iPhone|iPad/.test(navigator.platform); + // IS_WINDOWS = /Win32|Win64|Windows/.test(navigator.platform); + // IS_LINUX = /Linux/.test(navigator.platform); + // + // // For Ubuntu or GNOME: + // IS_UBUNTU = this.IS_LINUX && /Ubuntu/i.test(navigator.userAgent); + // IS_GNOME = this.IS_LINUX && /gnome/i.test(navigator.userAgent); + // IS_SNAP = IS_ELECTRON && window.ea.isSnap(); + // IS_ANDROID_WEBVIEW = IS_ANDROID_WEB_VIEW; +} diff --git a/src/app/t.const.ts b/src/app/t.const.ts index 42c2aa5e9b..076c25e655 100644 --- a/src/app/t.const.ts +++ b/src/app/t.const.ts @@ -164,6 +164,12 @@ const T = { 'F.DROPBOX.S.UNABLE_TO_GENERATE_PKCE_CHALLENGE', }, }, + D_RATE: { + A_HOW: 'F.D_RATE.A_HOW', + BTN_DONT_BOTHER: 'F.D_RATE.BTN_DONT_BOTHER', + TITLE: 'F.D_RATE.TITLE', + TXT: 'F.D_RATE.TXT', + }, FINISH_DAY_BEFORE_EXIT: { C: { FINISH_DAY_BEFORE_EXIT: 'F.FINISH_DAY_BEFORE_EXIT.C.FINISH_DAY_BEFORE_EXIT', diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index bd4444dc05..403878349f 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -163,6 +163,12 @@ "UNABLE_TO_GENERATE_PKCE_CHALLENGE": "Dropbox: Unable to generate PKCE challenge." } }, + "D_RATE": { + "A_HOW": "How and where to rate", + "BTN_DONT_BOTHER": "Don't bother me again", + "TITLE": "\uD83D\uDE48 Please forgive us, but...", + "TXT": "You would help the project immensely by giving it a good rating, if you like it!" + }, "FINISH_DAY_BEFORE_EXIT": { "C": { "FINISH_DAY_BEFORE_EXIT": "There are {{nr}} done tasks in your today list not yet moved to the archive. Do you really want to quit without finishing your day?"