support for desktop versions #20
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
import { Component, Vue, Prop } from 'vue-property-decorator';
|
||||
import { ApplicationState } from '@/application/State/ApplicationState';
|
||||
import TheHeader from '@/presentation/TheHeader.vue';
|
||||
import TheFooter from '@/presentation/TheFooter.vue';
|
||||
import TheFooter from '@/presentation/TheFooter/TheFooter.vue';
|
||||
import TheCodeArea from '@/presentation/TheCodeArea.vue';
|
||||
import TheCodeButtons from '@/presentation/TheCodeButtons.vue';
|
||||
import TheSearchBar from '@/presentation/TheSearchBar.vue';
|
||||
|
||||
54
src/application/Environment/BrowserOs/BrowserOsDetector.ts
Normal file
54
src/application/Environment/BrowserOs/BrowserOsDetector.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { OperatingSystem } from '../OperatingSystem';
|
||||
import { DetectorBuilder } from './DetectorBuilder';
|
||||
import { IBrowserOsDetector } from './IBrowserOsDetector';
|
||||
|
||||
export class BrowserOsDetector implements IBrowserOsDetector {
|
||||
private readonly detectors = BrowserDetectors;
|
||||
public detect(userAgent: string): OperatingSystem {
|
||||
if (!userAgent) {
|
||||
return OperatingSystem.Unknown;
|
||||
}
|
||||
for (const detector of this.detectors) {
|
||||
const os = detector.detect(userAgent);
|
||||
if (os !== OperatingSystem.Unknown) {
|
||||
return os;
|
||||
}
|
||||
}
|
||||
return OperatingSystem.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Reference: https://github.com/keithws/browser-report/blob/master/index.js#L304
|
||||
const BrowserDetectors =
|
||||
[
|
||||
define(OperatingSystem.KaiOS, (b) =>
|
||||
b.mustInclude('KAIOS')),
|
||||
define(OperatingSystem.ChromeOS, (b) =>
|
||||
b.mustInclude('CrOS')),
|
||||
define(OperatingSystem.BlackBerryOS, (b) =>
|
||||
b.mustInclude('BlackBerry')),
|
||||
define(OperatingSystem.BlackBerryTabletOS, (b) =>
|
||||
b.mustInclude('RIM Tablet OS')),
|
||||
define(OperatingSystem.BlackBerry, (b) =>
|
||||
b.mustInclude('BB10')),
|
||||
define(OperatingSystem.Android, (b) =>
|
||||
b.mustInclude('Android').mustNotInclude('Windows Phone')),
|
||||
define(OperatingSystem.Android, (b) =>
|
||||
b.mustInclude('Adr').mustNotInclude('Windows Phone')),
|
||||
define(OperatingSystem.iOS, (b) =>
|
||||
b.mustInclude('like Mac OS X')),
|
||||
define(OperatingSystem.Linux, (b) =>
|
||||
b.mustInclude('Linux').mustNotInclude('Android').mustNotInclude('Adr')),
|
||||
define(OperatingSystem.Windows, (b) =>
|
||||
b.mustInclude('Windows').mustNotInclude('Windows Phone')),
|
||||
define(OperatingSystem.WindowsPhone, (b) =>
|
||||
b.mustInclude('Windows Phone')),
|
||||
define(OperatingSystem.macOS, (b) =>
|
||||
b.mustInclude('OS X').mustNotInclude('Android').mustNotInclude('like Mac OS X')),
|
||||
];
|
||||
|
||||
function define(os: OperatingSystem, applyRules: (builder: DetectorBuilder) => DetectorBuilder): IBrowserOsDetector {
|
||||
const builder = new DetectorBuilder(os);
|
||||
applyRules(builder);
|
||||
return builder.build();
|
||||
}
|
||||
49
src/application/Environment/BrowserOs/DetectorBuilder.ts
Normal file
49
src/application/Environment/BrowserOs/DetectorBuilder.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { IBrowserOsDetector } from './IBrowserOsDetector';
|
||||
import { OperatingSystem } from '../OperatingSystem';
|
||||
|
||||
export class DetectorBuilder {
|
||||
private readonly existingPartsInUserAgent = new Array<string>();
|
||||
private readonly notExistingPartsInUserAgent = new Array<string>();
|
||||
|
||||
constructor(private readonly os: OperatingSystem) { }
|
||||
|
||||
public mustInclude(str: string): DetectorBuilder {
|
||||
if (!str) {
|
||||
throw new Error('part to include is empty or undefined');
|
||||
}
|
||||
this.existingPartsInUserAgent.push(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public mustNotInclude(str: string): DetectorBuilder {
|
||||
if (!str) {
|
||||
throw new Error('part to not include is empty or undefined');
|
||||
}
|
||||
this.notExistingPartsInUserAgent.push(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public build(): IBrowserOsDetector {
|
||||
if (!this.existingPartsInUserAgent.length) {
|
||||
throw new Error('Must include at least a part');
|
||||
}
|
||||
return {
|
||||
detect: (userAgent) => {
|
||||
if (!userAgent) {
|
||||
throw new Error('User agent is null or undefined');
|
||||
}
|
||||
for (const exitingPart of this.existingPartsInUserAgent) {
|
||||
if (!userAgent.includes(exitingPart)) {
|
||||
return OperatingSystem.Unknown;
|
||||
}
|
||||
}
|
||||
for (const notExistingPart of this.notExistingPartsInUserAgent) {
|
||||
if (userAgent.includes(notExistingPart)) {
|
||||
return OperatingSystem.Unknown;
|
||||
}
|
||||
}
|
||||
return this.os;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { OperatingSystem } from '../OperatingSystem';
|
||||
|
||||
export interface IBrowserOsDetector {
|
||||
detect(userAgent: string): OperatingSystem;
|
||||
}
|
||||
80
src/application/Environment/Environment.ts
Normal file
80
src/application/Environment/Environment.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { BrowserOsDetector } from './BrowserOs/BrowserOsDetector';
|
||||
import { IBrowserOsDetector } from './BrowserOs/IBrowserOsDetector';
|
||||
import { IEnvironment } from './IEnvironment';
|
||||
import { OperatingSystem } from './OperatingSystem';
|
||||
|
||||
interface IEnvironmentVariables {
|
||||
readonly window: Window & typeof globalThis;
|
||||
readonly process: NodeJS.Process;
|
||||
readonly navigator: Navigator;
|
||||
}
|
||||
|
||||
export class Environment implements IEnvironment {
|
||||
public static readonly CurrentEnvironment: IEnvironment = new Environment({
|
||||
window,
|
||||
process,
|
||||
navigator,
|
||||
});
|
||||
public readonly isDesktop: boolean;
|
||||
public readonly os: OperatingSystem;
|
||||
protected constructor(
|
||||
variables: IEnvironmentVariables,
|
||||
browserOsDetector: IBrowserOsDetector = new BrowserOsDetector()) {
|
||||
if (!variables) {
|
||||
throw new Error('variables is null or empty');
|
||||
}
|
||||
this.isDesktop = isDesktop(variables);
|
||||
this.os = this.isDesktop ?
|
||||
getDesktopOsType(getProcessPlatform(variables))
|
||||
: browserOsDetector.detect(getUserAgent(variables));
|
||||
}
|
||||
}
|
||||
|
||||
function getUserAgent(variables: IEnvironmentVariables): string {
|
||||
if (!variables.window || !variables.window.navigator) {
|
||||
return undefined;
|
||||
}
|
||||
return variables.window.navigator.userAgent;
|
||||
}
|
||||
|
||||
function getProcessPlatform(variables: IEnvironmentVariables): string {
|
||||
if (!variables.process || !variables.process.platform) {
|
||||
return undefined;
|
||||
}
|
||||
return variables.process.platform;
|
||||
}
|
||||
|
||||
function getDesktopOsType(processPlatform: string): OperatingSystem {
|
||||
// https://nodejs.org/api/process.html#process_process_platform
|
||||
if (processPlatform === 'darwin') {
|
||||
return OperatingSystem.macOS;
|
||||
} else if (processPlatform === 'win32') {
|
||||
return OperatingSystem.Windows;
|
||||
} else if (processPlatform === 'linux') {
|
||||
return OperatingSystem.Linux;
|
||||
}
|
||||
return OperatingSystem.Unknown;
|
||||
}
|
||||
|
||||
function isDesktop(variables: IEnvironmentVariables): boolean {
|
||||
// More: https://github.com/electron/electron/issues/2288
|
||||
// Renderer process
|
||||
if (variables.window
|
||||
&& variables.window.process
|
||||
&& variables.window.process.type === 'renderer') {
|
||||
return true;
|
||||
}
|
||||
// Main process
|
||||
if (variables.process
|
||||
&& variables.process.versions
|
||||
&& Boolean(variables.process.versions.electron)) {
|
||||
return true;
|
||||
}
|
||||
// Detect the user agent when the `nodeIntegration` option is set to true
|
||||
if (variables.navigator
|
||||
&& variables.navigator.userAgent
|
||||
&& variables.navigator.userAgent.includes('Electron')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
6
src/application/Environment/IEnvironment.ts
Normal file
6
src/application/Environment/IEnvironment.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { OperatingSystem } from './OperatingSystem';
|
||||
|
||||
export interface IEnvironment {
|
||||
isDesktop: boolean;
|
||||
os: OperatingSystem;
|
||||
}
|
||||
14
src/application/Environment/OperatingSystem.ts
Normal file
14
src/application/Environment/OperatingSystem.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export enum OperatingSystem {
|
||||
macOS,
|
||||
Windows,
|
||||
Linux,
|
||||
KaiOS,
|
||||
ChromeOS,
|
||||
BlackBerryOS,
|
||||
BlackBerry,
|
||||
BlackBerryTabletOS,
|
||||
Android,
|
||||
iOS,
|
||||
WindowsPhone,
|
||||
Unknown,
|
||||
}
|
||||
133
src/background.ts
Normal file
133
src/background.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
'use strict';
|
||||
|
||||
import { app, protocol, BrowserWindow, shell } from 'electron';
|
||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';
|
||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
|
||||
import path from 'path';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import log from 'electron-log';
|
||||
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
declare const __static: string; // https://github.com/electron-userland/electron-webpack/issues/172
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win: BrowserWindow | null;
|
||||
|
||||
// Scheme must be registered before the app is ready
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{ scheme: 'app', privileges: { secure: true, standard: true } },
|
||||
]);
|
||||
|
||||
// Setup logging
|
||||
autoUpdater.logger = log; // https://www.electron.build/auto-update#debugging
|
||||
log.transports.file.level = 'silly';
|
||||
if (!process.env.IS_TEST) {
|
||||
Object.assign(console, log.functions); // override console.log, console.warn etc.
|
||||
}
|
||||
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({
|
||||
width: 1350,
|
||||
height: 1005,
|
||||
webPreferences: {
|
||||
// Use pluginOptions.nodeIntegration, leave this alone
|
||||
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
|
||||
nodeIntegration: (process.env
|
||||
.ELECTRON_NODE_INTEGRATION as unknown) as boolean,
|
||||
},
|
||||
// https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#icons
|
||||
icon: path.join(__static, `favicon.ico`),
|
||||
});
|
||||
|
||||
win.setMenuBarVisibility(false);
|
||||
configureExternalsUrlsOpenBrowser(win);
|
||||
loadApplication(win);
|
||||
|
||||
win.on('closed', () => {
|
||||
win = null;
|
||||
});
|
||||
}
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', async () => {
|
||||
if (isDevelopment && !process.env.IS_TEST) {
|
||||
// Install Vue Devtools
|
||||
try {
|
||||
await installExtension(VUEJS_DEVTOOLS);
|
||||
} catch (e) {
|
||||
console.error('Vue Devtools failed to install:', e.toString()); // tslint:disable-line:no-console
|
||||
}
|
||||
}
|
||||
createWindow();
|
||||
});
|
||||
|
||||
// See electron-builder issue "checkForUpdatesAndNotify updates but does not notify on Windows 10"
|
||||
// https://github.com/electron-userland/electron-builder/issues/2700
|
||||
// https://github.com/electron/electron/issues/10864
|
||||
if (process.platform === 'win32') {
|
||||
// https://docs.microsoft.com/en-us/windows/win32/shell/appid#how-to-form-an-application-defined-appusermodelid
|
||||
app.setAppUserModelId('Undergroundwires.PrivacySexy');
|
||||
}
|
||||
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', (data) => {
|
||||
if (data === 'graceful-exit') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
process.on('SIGTERM', () => {
|
||||
app.quit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function loadApplication(window: BrowserWindow) {
|
||||
if (process.env.WEBPACK_DEV_SERVER_URL) {
|
||||
// Load the url of the dev server if in development mode
|
||||
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string);
|
||||
if (!process.env.IS_TEST) {
|
||||
win.webContents.openDevTools();
|
||||
}
|
||||
} else {
|
||||
createProtocol('app');
|
||||
// Load the index.html when not in development
|
||||
win.loadURL('app://./index.html');
|
||||
// tslint:disable-next-line:max-line-length
|
||||
autoUpdater.checkForUpdatesAndNotify(); // https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#check-for-updates-in-background-js-ts
|
||||
}
|
||||
}
|
||||
|
||||
function configureExternalsUrlsOpenBrowser(window: BrowserWindow) {
|
||||
window.webContents.on('new-window', (event, url) => { // handle redirect
|
||||
if (url !== win.webContents.getURL()) {
|
||||
event.preventDefault();
|
||||
shell.openExternal(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
51
src/presentation/TheFooter/DownloadUrlList.vue
Normal file
51
src/presentation/TheFooter/DownloadUrlList.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<span v-bind:class="{ 'unsupported': !hasCurrentOsDesktopVersion, 'supported': hasCurrentOsDesktopVersion }">
|
||||
For desktop:
|
||||
<span class="urls">
|
||||
<span class="urls__url" v-for="os of supportedDesktops" v-bind:key="os">
|
||||
<DownloadUrlListItem :operatingSystem="os" />
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { Environment } from '@/application/Environment/Environment';
|
||||
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||
import DownloadUrlListItem from './DownloadUrlListItem.vue';
|
||||
|
||||
@Component({
|
||||
components: { DownloadUrlListItem },
|
||||
})
|
||||
export default class DownloadUrlList extends Vue {
|
||||
public readonly supportedDesktops: ReadonlyArray<OperatingSystem>;
|
||||
public readonly hasCurrentOsDesktopVersion: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const supportedOperativeSystems = [OperatingSystem.Windows, OperatingSystem.Linux, OperatingSystem.macOS];
|
||||
const currentOs = Environment.CurrentEnvironment.os;
|
||||
this.supportedDesktops = supportedOperativeSystems.sort((os) => os === currentOs ? 0 : 1);
|
||||
this.hasCurrentOsDesktopVersion = supportedOperativeSystems.includes(currentOs);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.unsupported {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.supported {
|
||||
font-size: 1em;
|
||||
}
|
||||
.urls {
|
||||
&__url {
|
||||
&:not(:first-child)::before {
|
||||
content: "|";
|
||||
font-size: 0.6rem;
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
94
src/presentation/TheFooter/DownloadUrlListItem.vue
Normal file
94
src/presentation/TheFooter/DownloadUrlListItem.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<span class="url">
|
||||
<a :href="downloadUrl"
|
||||
v-bind:class="{
|
||||
'url__active': hasCurrentOsDesktopVersion && isCurrentOs,
|
||||
'url__inactive': hasCurrentOsDesktopVersion && !isCurrentOs,
|
||||
}">{{ operatingSystemName }}</a>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||
import { Environment } from '@/application/Environment/Environment';
|
||||
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||
|
||||
@Component
|
||||
export default class DownloadUrlListItem extends StatefulVue {
|
||||
@Prop() public operatingSystem!: OperatingSystem;
|
||||
public OperatingSystem = OperatingSystem;
|
||||
|
||||
public downloadUrl: string = '';
|
||||
public operatingSystemName: string = '';
|
||||
public isCurrentOs: boolean = false;
|
||||
public hasCurrentOsDesktopVersion: boolean = false;
|
||||
|
||||
public async mounted() {
|
||||
await this.onOperatingSystemChangedAsync(this.operatingSystem);
|
||||
}
|
||||
|
||||
@Watch('operatingSystem')
|
||||
public async onOperatingSystemChangedAsync(os: OperatingSystem) {
|
||||
const currentOs = Environment.CurrentEnvironment.os;
|
||||
this.isCurrentOs = os === currentOs;
|
||||
this.downloadUrl = await this.getDownloadUrlAsync(os);
|
||||
this.operatingSystemName = getOperatingSystemName(os);
|
||||
this.hasCurrentOsDesktopVersion = hasDesktopVersion(currentOs);
|
||||
}
|
||||
|
||||
private async getDownloadUrlAsync(os: OperatingSystem): Promise<string> {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
return `${state.app.repositoryUrl}/releases/download/${state.app.version}/${getFileName(os, state.app.version)}`;
|
||||
}
|
||||
}
|
||||
|
||||
function hasDesktopVersion(os: OperatingSystem): boolean {
|
||||
return os === OperatingSystem.Windows
|
||||
|| os === OperatingSystem.Linux
|
||||
|| os === OperatingSystem.macOS;
|
||||
}
|
||||
|
||||
function getOperatingSystemName(os: OperatingSystem): string {
|
||||
switch (os) {
|
||||
case OperatingSystem.Linux:
|
||||
return 'Linux';
|
||||
case OperatingSystem.macOS:
|
||||
return 'macOS';
|
||||
case OperatingSystem.Windows:
|
||||
return 'Windows';
|
||||
default:
|
||||
throw new Error(`Unsupported os: ${OperatingSystem[os]}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getFileName(os: OperatingSystem, version: string): string {
|
||||
switch (os) {
|
||||
case OperatingSystem.Linux:
|
||||
return `privacy.sexy-${version}.dmg`;
|
||||
case OperatingSystem.macOS:
|
||||
return `privacy.sexy-${version}-mac.zip`;
|
||||
case OperatingSystem.Windows:
|
||||
return `privacy.sexy-Setup-${version}.exe`;
|
||||
default:
|
||||
throw new Error(`Unsupported os: ${OperatingSystem[os]}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.url {
|
||||
&__active {
|
||||
font-size: 1em;
|
||||
}
|
||||
&__inactive {
|
||||
font-size: 0.70em;
|
||||
}
|
||||
a {
|
||||
color:inherit;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
81
src/presentation/TheFooter/PrivacyPolicy.vue
Normal file
81
src/presentation/TheFooter/PrivacyPolicy.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="privacy-policy">
|
||||
<div v-if="!isDesktop" class="line">
|
||||
<div class="line__emoji">🚫🍪</div>
|
||||
<div>No cookies!</div>
|
||||
</div>
|
||||
<div v-if="isDesktop" class="line">
|
||||
<div class="line__emoji">🚫🌐</div>
|
||||
<div>Everything is offline, except single request GitHub toto check for updates on application start.</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="line__emoji">🚫👀</div>
|
||||
<div>No user behavior / IP adress collection!</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="line__emoji">🤖</div>
|
||||
<div>All transparent: Deployed automatically from master branch
|
||||
of the <a :href="repositoryUrl" target="_blank">source code</a> with no changes.</div>
|
||||
</div>
|
||||
<div v-if="!isDesktop" class="line">
|
||||
<div class="line__emoji">📈</div>
|
||||
<div>Basic <a href="https://aws.amazon.com/cloudfront/reporting/" target="_blank">CDN statistics</a>
|
||||
are collected by AWS but they cannot be related to you or your behavior. You can download the offline version if you don't want CDN data collection.</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="line__emoji">🎉</div>
|
||||
<div>As almost no data is colected, the application gets better only with your active feedback.
|
||||
Feel free to <a :href="feedbackUrl" target="_blank">create an issue</a> 😊</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||
import { Environment } from '@/application/Environment/Environment';
|
||||
|
||||
@Component
|
||||
export default class TheFooter extends StatefulVue {
|
||||
public repositoryUrl: string = '';
|
||||
public feedbackUrl: string = '';
|
||||
public isDesktop: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.isDesktop = Environment.CurrentEnvironment.isDesktop;
|
||||
}
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.repositoryUrl = state.app.repositoryUrl;
|
||||
this.feedbackUrl = `${state.app.repositoryUrl}/issues`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/presentation/styles/fonts.scss";
|
||||
.privacy-policy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: $normal-font;
|
||||
text-align:center;
|
||||
|
||||
.line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top:0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color:inherit;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +1,24 @@
|
||||
<template>
|
||||
<div class="footer">
|
||||
<div class="item">
|
||||
<a :href="releaseUrl" target="_blank">{{ version }}</a>
|
||||
<div>
|
||||
<div class="footer">
|
||||
<div class="footer__section">
|
||||
<div class="item">
|
||||
<a :href="releaseUrl" target="_blank">{{ version }}</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a @click="$modal.show(modalName)">Privacy</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer__section">
|
||||
<span v-if="isDesktop">
|
||||
Online version at <a href="https://privacy.sexy" target="_blank">https://privacy.sexy</a>
|
||||
</span>
|
||||
<DownloadUrlList v-else />
|
||||
</div>
|
||||
<div class="item">
|
||||
<a @click="$modal.show(modalName)">Privacy</a> <!-- href to #privacy to avoid scrolling to top -->
|
||||
</div>
|
||||
<modal :name="modalName" height="auto" :scrollable="true" :adaptive="true">
|
||||
<div class="modal">
|
||||
<ThePrivacyPolicy class="modal__content"/>
|
||||
<PrivacyPolicy class="modal__content"/>
|
||||
<div class="modal__close-button">
|
||||
<font-awesome-icon :icon="['fas', 'times']" @click="$modal.hide(modalName)"/>
|
||||
</div>
|
||||
@@ -19,18 +29,28 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { StatefulVue } from './StatefulVue';
|
||||
import ThePrivacyPolicy from './ThePrivacyPolicy.vue';
|
||||
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||
import { Environment } from '@/application/Environment/Environment';
|
||||
import PrivacyPolicy from './PrivacyPolicy.vue';
|
||||
import DownloadUrlList from './DownloadUrlList.vue';
|
||||
import { OperatingSystem } from '@/application/Environment/OperatingSystem';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
ThePrivacyPolicy,
|
||||
PrivacyPolicy, DownloadUrlList,
|
||||
},
|
||||
})
|
||||
export default class TheFooter extends StatefulVue {
|
||||
private readonly modalName = 'privacy-policy';
|
||||
private version: string = '';
|
||||
private releaseUrl: string = '';
|
||||
public readonly modalName = 'privacy-policy';
|
||||
public readonly isDesktop: boolean;
|
||||
|
||||
public version: string = '';
|
||||
public releaseUrl: string = '';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.isDesktop = Environment.CurrentEnvironment.isDesktop;
|
||||
}
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
@@ -43,13 +63,26 @@ export default class TheFooter extends StatefulVue {
|
||||
<style scoped lang="scss">
|
||||
@import "@/presentation/styles/colors.scss";
|
||||
@import "@/presentation/styles/fonts.scss";
|
||||
|
||||
$medium-screen-width: 767px;
|
||||
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@media screen and (max-width: $medium-screen-width) {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
flex-wrap: wrap;
|
||||
&__section {
|
||||
display: flex;
|
||||
color: $dark-gray;
|
||||
font-size: 1rem;
|
||||
font-family: $normal-font;
|
||||
align-self: center;
|
||||
|
||||
&:not(:first-child) {
|
||||
padding-top: 3px;
|
||||
}
|
||||
a {
|
||||
color:inherit;
|
||||
text-decoration: underline;
|
||||
@@ -63,6 +96,7 @@ export default class TheFooter extends StatefulVue {
|
||||
content: "|";
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.modal {
|
||||
margin-bottom: 10px;
|
||||
@@ -1,70 +0,0 @@
|
||||
<template>
|
||||
<div class="privacy-policy">
|
||||
<div class="line">
|
||||
<div class="line__emoji">🚫🍪</div>
|
||||
<div>No cookies!</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="line__emoji">🚫👀</div>
|
||||
<div>No user behavior / IP adress collection!</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="line__emoji">🤖</div>
|
||||
<div>Website is deployed automatically from master branch
|
||||
of the <a :href="repositoryUrl" target="_blank">source code</a> with no changes.</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="line__emoji">📈</div>
|
||||
<div>Basic <a href="https://aws.amazon.com/cloudfront/reporting/" target="_blank">CDN statistics</a>
|
||||
are collected by AWS but they cannot be related to you or your behavior.</div>
|
||||
</div>
|
||||
<div class="line">
|
||||
<div class="line__emoji">🎉</div>
|
||||
<div>As almost no data is colected, the website gets better only with your active feedback.
|
||||
Feel free to <a :href="feedbackUrl" target="_blank">create an issue</a> 😊</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { StatefulVue } from '@/presentation/StatefulVue';
|
||||
|
||||
@Component
|
||||
export default class TheFooter extends StatefulVue {
|
||||
private repositoryUrl: string = '';
|
||||
private feedbackUrl: string = '';
|
||||
|
||||
public async mounted() {
|
||||
const state = await this.getCurrentStateAsync();
|
||||
this.repositoryUrl = state.app.repositoryUrl;
|
||||
this.feedbackUrl = `${state.app.repositoryUrl}/issues`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/presentation/styles/fonts.scss";
|
||||
.privacy-policy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: $normal-font;
|
||||
text-align:center;
|
||||
|
||||
.line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top:0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color:inherit;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user