Merge frontend repository into app/ subdirectory
17
app/.editorconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# We recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
35
app/.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Specifies intentionally untracked files to ignore when using Git
|
||||
# http://git-scm.com/docs/gitignore
|
||||
|
||||
*~
|
||||
*.sw[mnpcod]
|
||||
*.log
|
||||
*.tmp
|
||||
*.tmp.*
|
||||
log.txt
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.vscode/
|
||||
npm-debug.log*
|
||||
|
||||
.idea/
|
||||
.sourcemaps/
|
||||
.sass-cache/
|
||||
.tmp/
|
||||
.versions/
|
||||
coverage/
|
||||
dist/
|
||||
node_modules/
|
||||
tmp/
|
||||
temp/
|
||||
hooks/
|
||||
platforms/
|
||||
plugins/
|
||||
plugins/android.json
|
||||
plugins/ios.json
|
||||
www/
|
||||
$RECYCLE.BIN/
|
||||
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
UserInterfaceState.xcuserstate
|
||||
6
app/ionic.config.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "grindd",
|
||||
"app_id": "",
|
||||
"type": "ionic-angular",
|
||||
"integrations": {}
|
||||
}
|
||||
40
app/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "urge",
|
||||
"version": "0.0.1",
|
||||
"author": "Ionic Framework",
|
||||
"homepage": "http://ionicframework.com/",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "ionic-app-scripts clean",
|
||||
"build": "ionic-app-scripts build",
|
||||
"lint": "ionic-app-scripts lint",
|
||||
"ionic:build": "ionic-app-scripts build",
|
||||
"ionic:serve": "ionic-app-scripts serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/common": "5.0.3",
|
||||
"@angular/compiler": "5.0.3",
|
||||
"@angular/compiler-cli": "5.0.3",
|
||||
"@angular/core": "5.0.3",
|
||||
"@angular/forms": "5.0.3",
|
||||
"@angular/http": "5.0.3",
|
||||
"@angular/platform-browser": "5.0.3",
|
||||
"@angular/platform-browser-dynamic": "5.0.3",
|
||||
"@ionic-native/core": "4.5.3",
|
||||
"@ionic-native/splash-screen": "4.5.3",
|
||||
"@ionic-native/status-bar": "4.5.3",
|
||||
"@ionic/storage": "2.1.3",
|
||||
"ionic-angular": "3.9.2",
|
||||
"ionic-swipe-all": "^1.2.0",
|
||||
"ionicons": "3.0.0",
|
||||
"moment": "^2.21.0",
|
||||
"rxjs": "5.5.2",
|
||||
"sw-toolbox": "3.6.0",
|
||||
"zone.js": "0.8.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ionic/app-scripts": "3.1.8",
|
||||
"typescript": "2.4.2"
|
||||
},
|
||||
"description": "Nick Pfosi's exploration at modern gay app-based meeting and dating"
|
||||
}
|
||||
22
app/src/app/app.component.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Platform } from 'ionic-angular';
|
||||
import { StatusBar } from '@ionic-native/status-bar';
|
||||
import { SplashScreen } from '@ionic-native/splash-screen';
|
||||
|
||||
import { TabsPage } from '../pages/tabs/tabs';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'app.html'
|
||||
})
|
||||
export class Urnings {
|
||||
rootPage: any = TabsPage;
|
||||
|
||||
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
|
||||
platform.ready().then(() => {
|
||||
// Okay, so the platform is ready and our plugins are available.
|
||||
// Here you can do any higher level native things you might need.
|
||||
statusBar.styleDefault();
|
||||
splashScreen.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
1
app/src/app/app.html
Normal file
@@ -0,0 +1 @@
|
||||
<ion-nav [root]="rootPage"></ion-nav>
|
||||
65
app/src/app/app.module.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { NgModule, ErrorHandler } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
|
||||
import { IonicSwipeAllModule } from 'ionic-swipe-all';
|
||||
import { Urnings } from './app.component';
|
||||
|
||||
import { ChatPage } from '../pages/chat/chat';
|
||||
import { GridPage } from '../pages/grid/grid';
|
||||
import { InformationPage } from '../pages/information/information';
|
||||
import { LightboxPage } from '../pages/lightbox/lightbox';
|
||||
import { MessagesPage } from '../pages/messages/messages';
|
||||
import { ProfilePage } from '../pages/profile/profile';
|
||||
import { TabsPage } from '../pages/tabs/tabs';
|
||||
import { TellYourStoryPage } from '../pages/tell/tell';
|
||||
import { UsersPage } from '../pages/users/users';
|
||||
|
||||
import { StatusBar } from '@ionic-native/status-bar';
|
||||
import { SplashScreen } from '@ionic-native/splash-screen';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
Urnings,
|
||||
ChatPage,
|
||||
InformationPage,
|
||||
GridPage,
|
||||
LightboxPage,
|
||||
MessagesPage,
|
||||
ProfilePage,
|
||||
TabsPage,
|
||||
TellYourStoryPage,
|
||||
UsersPage
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
HttpModule,
|
||||
IonicSwipeAllModule,
|
||||
IonicModule.forRoot(Urnings, {
|
||||
iconMode: 'ios',
|
||||
modalEnter: 'modal-slide-in',
|
||||
modalLeave: 'modal-slide-out',
|
||||
tabsPlacement: 'bottom',
|
||||
pageTransition: 'ios-transition'
|
||||
})
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
entryComponents: [
|
||||
Urnings,
|
||||
ChatPage,
|
||||
InformationPage,
|
||||
GridPage,
|
||||
LightboxPage,
|
||||
MessagesPage,
|
||||
ProfilePage,
|
||||
TabsPage,
|
||||
TellYourStoryPage,
|
||||
UsersPage
|
||||
],
|
||||
providers: [
|
||||
StatusBar,
|
||||
SplashScreen,
|
||||
{provide: ErrorHandler, useClass: IonicErrorHandler}
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
91
app/src/app/app.scss
Normal file
@@ -0,0 +1,91 @@
|
||||
// http://ionicframework.com/docs/theming/
|
||||
|
||||
|
||||
// App Global Sass
|
||||
// --------------------------------------------------
|
||||
// Put style rules here that you want to apply globally. These
|
||||
// styles are for the entire app and not just one component.
|
||||
// Additionally, this file can be also used as an entry point
|
||||
// to import other Sass files to be included in the output CSS.
|
||||
//
|
||||
// Shared Sass variables, which can be used to adjust Ionic's
|
||||
// default Sass variables, belong in "theme/variables.scss".
|
||||
//
|
||||
// To declare rules for a specific mode, create a child rule
|
||||
// for the .md, .ios, or .wp mode classes. The mode class is
|
||||
// automatically applied to the <body> element in the app.
|
||||
|
||||
body {
|
||||
font-family: 'PT Sans', sans-serif;
|
||||
}
|
||||
|
||||
ion-toolbar {
|
||||
color: #fff;
|
||||
|
||||
.toolbar-background {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
.bar-button,
|
||||
.toolbar-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
ion-title {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #191b1c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.item {
|
||||
background-color: #1d1e1f;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.list {
|
||||
|
||||
.item-block .item-inner {
|
||||
border-bottom-color: #333435;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.tabs {
|
||||
|
||||
.tabbar {
|
||||
background-color: #090a0a;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
|
||||
.tab-button-icon {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&[aria-selected=true] {
|
||||
|
||||
.tab-button-icon {
|
||||
color: #fdb315;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 769px) {
|
||||
body {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
ion-app.app-root {
|
||||
height: 480px;
|
||||
left: 50%;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
width: 320px;
|
||||
}
|
||||
}
|
||||
8
app/src/app/main.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
enableProdMode();
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
BIN
app/src/assets/data/cruises.cje/StoreContent/persistentStore
Normal file
1
app/src/assets/data/cruises.json
Normal file
1
app/src/assets/data/profiles initial.json
Normal file
1
app/src/assets/data/profiles placeholder.json
Normal file
1
app/src/assets/data/profiles.json
Normal file
BIN
app/src/assets/icon/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/assets/imgs/launch-screen-1125x2436.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
app/src/assets/imgs/launch-screen-1242x2208.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
app/src/assets/imgs/launch-screen-640x1136.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
app/src/assets/imgs/launch-screen-750x1334.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
app/src/assets/imgs/logo.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app/src/assets/imgs/splash.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
57
app/src/index.html
Normal file
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Urnings</title>
|
||||
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="assets/icon/favicon.ico">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<meta name="theme-color" content="#4e8ef7">
|
||||
|
||||
<!-- add to homescreen for ios -->
|
||||
<link rel="apple-touch-icon" href="assets/imgs/logo.png">
|
||||
<link rel="apple-touch-startup-image" sizes="640x1136" href="assets/imgs/launch-screen-640x1136.png">
|
||||
<link rel="apple-touch-startup-image" sizes="750x1334" href="assets/imgs/launch-screen-750x1334.png">
|
||||
<link rel="apple-touch-startup-image" sizes="1125x2436" href="assets/imgs/launch-screen-1125x2436.png">
|
||||
<link rel="apple-touch-startup-image" sizes="1242x2208" href="assets/imgs/launch-screen-1242x2208.png">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="Urge">
|
||||
|
||||
<!-- cordova.js required for cordova apps (remove if not needed) -->
|
||||
<!--script src="cordova.js"></script-->
|
||||
|
||||
<!-- un-comment this code to enable service worker
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('service-worker.js')
|
||||
.then(() => console.log('service worker installed'))
|
||||
.catch(err => console.error('Error', err));
|
||||
}
|
||||
</script>-->
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=PT+Sans" rel="stylesheet">
|
||||
<link href="build/main.css" rel="stylesheet">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Ionic's root component and where the app will load -->
|
||||
<ion-app></ion-app>
|
||||
|
||||
<!-- The polyfills js is generated during the build process -->
|
||||
<script src="build/polyfills.js"></script>
|
||||
|
||||
<!-- The vendor js is generated during the build process
|
||||
It contains all of the dependencies in node_modules -->
|
||||
<script src="build/vendor.js"></script>
|
||||
|
||||
<!-- The main bundle js is generated during the build process -->
|
||||
<script src="build/main.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
13
app/src/manifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "urge",
|
||||
"short_name": "urge",
|
||||
"start_url": "index.html",
|
||||
"display": "standalone",
|
||||
"icons": [{
|
||||
"src": "assets/imgs/logo.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}],
|
||||
"background_color": "#191b1c",
|
||||
"theme_color": "#191b1c"
|
||||
}
|
||||
25
app/src/pages/chat/chat.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons left>
|
||||
<button ion-button icon-only (tap)="closeChat($event)">
|
||||
<ion-icon name="arrow-back"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
<ion-title><img class="title-profile-avatar" [src]="'https://appsby.fitz.guru/urge/' + this.profile.details.pic.thumb" height="24" width="24"> {{this.profile.details.name}}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item class="message-bubble" *ngFor="let message of this.profile.messages" [ngClass]="{ 'is-user': (message.isUser == true) }">
|
||||
<img *ngIf="message.image" [src]="'https://appsby.fitz.guru/urge/' + message.image" (press)="showLightbox($event, message.image)">
|
||||
<p *ngIf="message.text != ''">{{message.text}}</p>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
48
app/src/pages/chat/chat.scss
Normal file
@@ -0,0 +1,48 @@
|
||||
page-chat {
|
||||
|
||||
.toolbar-title {
|
||||
|
||||
.title-profile-avatar {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
|
||||
.message-bubble {
|
||||
background-color: #fdb315;
|
||||
border-radius: 0.5rem;
|
||||
font-family: 'Helvetica Neue', HelveticaNeue, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
margin: 1rem 1rem 2.5rem auto;
|
||||
max-width: 75%;
|
||||
overflow: visible;
|
||||
padding: 0.75rem;
|
||||
position: relative;
|
||||
|
||||
&.is-user {
|
||||
background-color: #6fbedf;
|
||||
margin: 1rem auto 2.5rem 1rem;
|
||||
}
|
||||
|
||||
.item-inner {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
ion-label {
|
||||
overflow: visible;
|
||||
text-overflow: unset;
|
||||
margin: 0 0.75rem;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #1d1e1f;
|
||||
white-space: normal;
|
||||
|
||||
&.timestamp {
|
||||
color: #acacac;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
app/src/pages/chat/chat.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController, NavParams } from 'ionic-angular';
|
||||
|
||||
import { LightboxPage } from '../lightbox/lightbox';
|
||||
|
||||
@Component({
|
||||
selector: 'page-chat',
|
||||
templateUrl: 'chat.html'
|
||||
})
|
||||
|
||||
export class ChatPage {
|
||||
|
||||
profile: any;
|
||||
tabNavEl: any;
|
||||
|
||||
constructor(public navCtrl: NavController, private _params: NavParams) {
|
||||
this.profile = this._params.get('profile');
|
||||
this.tabNavEl = document.querySelector('#tab-nav .tabbar');
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
this.tabNavEl.style.display = 'none';
|
||||
}
|
||||
|
||||
closeChat(event) {
|
||||
this.navCtrl.pop();
|
||||
}
|
||||
|
||||
showLightbox(event, image) {
|
||||
this.navCtrl.push(LightboxPage, {
|
||||
image: image
|
||||
});
|
||||
}
|
||||
}
|
||||
15
app/src/pages/grid/grid.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Urnings</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content no-padding>
|
||||
<ion-grid no-padding>
|
||||
<ion-row align-items-stretch>
|
||||
<ion-col col-4 class="profile" *ngFor="let current of profiles" (tap)="profileTapped($event, current)" (press)="profilePressed($event, current)" [style.backgroundImage]="getBackgroundThumbnail(current.details.pic)">
|
||||
<span class="username" [ngClass]="{ 'online': (current.messages?.length > 0) }">{{current.details.name}}</span>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-content>
|
||||
66
app/src/pages/grid/grid.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
page-grid {
|
||||
|
||||
ion-toolbar {
|
||||
|
||||
.toolbar-title {
|
||||
color: #ffffff;
|
||||
font-size: 2.42em;
|
||||
font-weight: 700;
|
||||
line-height: 1.29;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
|
||||
.row {
|
||||
|
||||
.col {
|
||||
|
||||
&.profile {
|
||||
background-size: cover;
|
||||
border: 1px solid #000000;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0 33% !important;
|
||||
position: relative;
|
||||
|
||||
.username {
|
||||
background-size: cover;
|
||||
bottom: 0.25rem;
|
||||
box-sizing: border-box;
|
||||
color: #ffffff;
|
||||
display: inline-block;
|
||||
left: 0.5rem;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
right: 0.25rem;
|
||||
text-overflow: ellipsis;
|
||||
text-shadow: 0 0 3px rgba(0, 0, 0, 1);
|
||||
white-space: nowrap;
|
||||
|
||||
&::before {
|
||||
border: 0.125rem solid #acacac;
|
||||
border-radius: 1rem;
|
||||
bottom: 0.125rem;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 0.8rem;
|
||||
margin-right: 0.5rem;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 0.8rem;
|
||||
}
|
||||
|
||||
&.online {
|
||||
|
||||
&::before {
|
||||
background-color: #00ff00;
|
||||
border-color: #00ff00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
app/src/pages/grid/grid.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { NavController } from 'ionic-angular';
|
||||
|
||||
import { ChatPage } from '../chat/chat';
|
||||
import { ProfileService } from '../../services/profiles';
|
||||
import { ProfilePage } from '../profile/profile';
|
||||
|
||||
@Component({
|
||||
selector: 'page-grid',
|
||||
templateUrl: 'grid.html',
|
||||
providers: [ ProfileService ]
|
||||
})
|
||||
export class GridPage {
|
||||
|
||||
profiles: any;
|
||||
tabNavEl: any;
|
||||
|
||||
constructor(public navCtrl: NavController, public profileService: ProfileService, private _sanitizer: DomSanitizer) {
|
||||
profileService.loadVerified().then((data) => {
|
||||
this.profiles = data;
|
||||
console.debug('profiles: ', this.profiles);
|
||||
});
|
||||
this.tabNavEl = document.querySelector('#tab-nav .tabbar');
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
this.tabNavEl.style.display = 'flex';
|
||||
}
|
||||
|
||||
doTellStory() {
|
||||
this.navCtrl.push(TellYourStoryPage);
|
||||
}
|
||||
|
||||
getBackgroundThumbnail(pics) {
|
||||
return this._sanitizer.bypassSecurityTrustStyle('url(https://appsby.fitz.guru/urge/' + pics.thumb + ')');
|
||||
}
|
||||
|
||||
profilePressed(event, profile) {
|
||||
if (profile.messages && profile.messages.length) {
|
||||
this.navCtrl.push(ChatPage, {
|
||||
profile: profile
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
profileTapped(event, profile) {
|
||||
this.navCtrl.push(ProfilePage, {
|
||||
profile: profile,
|
||||
});
|
||||
}
|
||||
}
|
||||
20
app/src/pages/information/information.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons right>
|
||||
<button ion-button icon-only (tap)="close($event)">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content padding>
|
||||
<div class="content-box" padding margin>
|
||||
<h3>About this Project</h3>
|
||||
<div class="info-blurb">
|
||||
<p>The app was designed by Nicholas Pfosi and developed by Michael Fitzpatrick, modeled after the popular gay dating app Grindr.</p>
|
||||
<p>Presenting these stories in this form, which is the conduit through which much participation in dating occurs, served multiple purposes. First, it educated the viewer who may not have used Grindr before, how it functions and how it is different from other apps such as Tinder, whereby matching with a person is a prerequisite for conversation. Second, it makes the scope of the project flexible, allowing for the submission of stories from the audience to be slotted into an expandable presentation.</p>
|
||||
<p>Please direct any questions or concerns to Nicholas Pfosi at npfosi@gmail.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
9
app/src/pages/information/information.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
page-information {
|
||||
|
||||
.content-box {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
font-family: 'Helvetica Neue', HelveticaNeue, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
23
app/src/pages/information/information.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'page-information',
|
||||
templateUrl: 'information.html',
|
||||
})
|
||||
export class InformationPage {
|
||||
|
||||
tabNavEl: any;
|
||||
|
||||
constructor(public navCtrl: NavController) {
|
||||
this.tabNavEl = document.querySelector('#tab-nav .tabbar');
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
this.tabNavEl.style.display = 'none';
|
||||
}
|
||||
|
||||
close(event) {
|
||||
this.navCtrl.pop();
|
||||
}
|
||||
}
|
||||
13
app/src/pages/lightbox/lightbox.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons right>
|
||||
<button ion-button icon-only (tap)="close($event)">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content (click)="close($event)">
|
||||
<img class="image-detail" [src]="'https://appsby.fitz.guru/urge/' + this.image">
|
||||
</ion-content>
|
||||
11
app/src/pages/lightbox/lightbox.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
page-lightbox {
|
||||
|
||||
.image-detail {
|
||||
display: block;
|
||||
height: auto;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translate3d(0, -50%, 0);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
26
app/src/pages/lightbox/lightbox.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController, NavParams } from 'ionic-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'page-lightbox',
|
||||
templateUrl: 'lightbox.html'
|
||||
})
|
||||
|
||||
export class LightboxPage {
|
||||
|
||||
image: string;
|
||||
tabNavEl: any;
|
||||
|
||||
constructor(public navCtrl: NavController, private _params: NavParams) {
|
||||
this.image = this._params.get('image');
|
||||
this.tabNavEl = document.querySelector('#tab-nav .tabbar');
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
this.tabNavEl.style.display = 'none';
|
||||
}
|
||||
|
||||
close(event) {
|
||||
this.navCtrl.pop();
|
||||
}
|
||||
}
|
||||
28
app/src/pages/messages/messages.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Urnings</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content no-padding>
|
||||
<ion-list>
|
||||
<ng-container *ngFor="let profile of profiles">
|
||||
<ion-item no-padding *ngIf="profile.messages?.length > 0">
|
||||
<ion-thumbnail padding-left item-start (tap)="profilePictureTapped($event, profile)">
|
||||
<img [src]="'https://appsby.fitz.guru/urge/' + profile.details.pic.thumb">
|
||||
</ion-thumbnail>
|
||||
<ion-grid (tap)="interviewTapped($event, profile)">
|
||||
<ion-row nowrap justify-content-between>
|
||||
<ion-col class="username">
|
||||
{{profile.details.name}}
|
||||
</ion-col>
|
||||
<ion-col class="timestamp" [innerHTML]="getLatestMessageTimestamp(profile.messages)"></ion-col>
|
||||
</ion-row>
|
||||
<ion-row class="latest-message" nowrap>
|
||||
<ion-col [innerHTML]="getLatestMessage(profile.messages)"></ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
47
app/src/pages/messages/messages.scss
Normal file
@@ -0,0 +1,47 @@
|
||||
page-messages {
|
||||
|
||||
ion-toolbar {
|
||||
|
||||
.toolbar-title {
|
||||
color: #ffffff;
|
||||
font-size: 2.42em;
|
||||
font-weight: 700;
|
||||
line-height: 1.29;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
ion-header {
|
||||
|
||||
.button {
|
||||
color: #9e9ea8;
|
||||
}
|
||||
}
|
||||
|
||||
.col {
|
||||
color: #ffffff;
|
||||
font-family: 'Helvetica Neue', HelveticaNeue, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
|
||||
&.username {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&.timestamp {
|
||||
font-size: 0.7em;
|
||||
font-style: italic;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.latest-message {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
|
||||
> .item-block:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
app/src/pages/messages/messages.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
|
||||
import { ProfileService } from '../../services/profiles';
|
||||
import { ProfilePage } from '../profile/profile';
|
||||
import { ChatPage } from '../chat/chat';
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
@Component({
|
||||
selector: 'page-messages',
|
||||
templateUrl: 'messages.html',
|
||||
providers: [ ProfileService ]
|
||||
})
|
||||
export class MessagesPage {
|
||||
|
||||
profiles: any;
|
||||
tabNavEl: any;
|
||||
|
||||
constructor(public navCtrl: NavController, public profileService: ProfileService) {
|
||||
profileService.load().then((data) => {
|
||||
this.profiles = data;
|
||||
});
|
||||
this.tabNavEl = document.querySelector('#tab-nav .tabbar');
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
this.tabNavEl.style.display = 'flex';
|
||||
}
|
||||
|
||||
getLatestMessage(messages) {
|
||||
var latest = messages[(messages.length - 1)];
|
||||
var isUser = latest.isUser;
|
||||
return latest.text ? latest.text : '<em>' + (!isUser ? 'Sent ' : '') + 'Photo' + (isUser ? ' Recieved' : '') + '</em>';
|
||||
}
|
||||
|
||||
getLatestMessageTimestamp(messages) {
|
||||
return moment(messages[(messages.length - 1)].timestamp).fromNow();
|
||||
}
|
||||
|
||||
interviewTapped(event, profile) {
|
||||
this.navCtrl.push(ChatPage, {
|
||||
profile: profile
|
||||
});
|
||||
}
|
||||
|
||||
profilePictureTapped(event, profile) {
|
||||
this.navCtrl.push(ProfilePage, {
|
||||
profile: profile
|
||||
});
|
||||
}
|
||||
}
|
||||
27
app/src/pages/profile/profile.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<ion-content no-padding [style.backgroundImage]="getBackground(profile.details.pic)" (press)="showLightbox($event, profile.details.pic.detail)" on-swipe-left="nextProfile($event)" on-swipe-right="previousProfile($event)">
|
||||
<ion-toolbar class="profile-toolbar">
|
||||
<ion-buttons left>
|
||||
<button ion-button icon-only (tap)="closeProfile($event)">
|
||||
<ion-icon name="arrow-back"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{this.profile.details.name}}</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
<button ion-button icon-only clear large (tap)="openChat($event, this.profile)" class="button-chat">
|
||||
<ion-icon name="ios-chatboxes"></ion-icon>
|
||||
</button>
|
||||
|
||||
<div id="detail-overlay" class="details">
|
||||
<ion-grid>
|
||||
<ion-row nowrap align-items-center justify-content-between>
|
||||
<ion-col col-12 text-center (click)="toggleProfileDetails($event)" class="detail-toggle">
|
||||
<ion-icon name="arrow-down"></ion-icon>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row class="about" *ngIf="this.profile.details.about">
|
||||
<ion-col col-12 [innerHTML]="this.profile.details.about"></ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</div>
|
||||
</ion-content>
|
||||
72
app/src/pages/profile/profile.scss
Normal file
@@ -0,0 +1,72 @@
|
||||
page-profile {
|
||||
|
||||
ion-content {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.scroll-content {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
ion-toolbar {
|
||||
border-bottom: 1px solid #ffffff;
|
||||
transition: opacity 250ms 125ms ease-in-out;
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.toolbar-background {
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
.bar-button,
|
||||
.toolbar-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.button-chat {
|
||||
bottom: 3rem;
|
||||
color: #fdb315;
|
||||
position: absolute;
|
||||
right: 1.5rem;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.detail-toggle {
|
||||
font-size: 2.5em;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.details {
|
||||
bottom: 0;
|
||||
height: 60px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
transition: all 250ms 125ms ease-in-out;
|
||||
|
||||
&.open {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.about {
|
||||
font-family: 'Helvetica Neue', HelveticaNeue, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: right;
|
||||
|
||||
.button-clear {
|
||||
color: #fdb315;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
app/src/pages/profile/profile.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { NavController, NavParams } from 'ionic-angular';
|
||||
|
||||
import { ChatPage } from '../chat/chat';
|
||||
import { LightboxPage } from '../lightbox/lightbox';
|
||||
import { ProfileService } from '../../services/profiles';
|
||||
|
||||
@Component({
|
||||
selector: 'page-profile',
|
||||
templateUrl: 'profile.html',
|
||||
providers: [ ProfileService ]
|
||||
})
|
||||
|
||||
export class ProfilePage {
|
||||
|
||||
detailsOpen: boolean = false;
|
||||
profile: any;
|
||||
tabNavEl: any;
|
||||
|
||||
constructor(public navCtrl: NavController, public navParams: NavParams, public profileService: ProfileService, private _sanitizer: DomSanitizer) {
|
||||
this.profile = navParams.get('profile');
|
||||
this.tabNavEl = document.querySelector('#tab-nav .tabbar');
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
this.tabNavEl.style.display = 'none';
|
||||
}
|
||||
|
||||
closeProfile(event) {
|
||||
this.navCtrl.pop();
|
||||
}
|
||||
|
||||
closeProfileDetails(event) {
|
||||
if (this.detailsOpen) {
|
||||
this.detailsOpen = false;
|
||||
document.querySelector('.profile-toolbar').classList.remove('hidden');
|
||||
document.getElementById('detail-overlay').classList.remove('open');
|
||||
}
|
||||
}
|
||||
|
||||
getBackground(pics) {
|
||||
return this._sanitizer.bypassSecurityTrustStyle('url(https://appsby.fitz.guru/urge/' + pics.detail + ')');
|
||||
}
|
||||
|
||||
markFavorite(event, profile) {
|
||||
console.debug('favorite profile', { event: event, profile: profile });
|
||||
}
|
||||
|
||||
nextProfile(event) {
|
||||
this.profile = this.profileService.getNextProfile(this.profile._id);
|
||||
this.navCtrl.setRoot(this.navCtrl.getActive().component);
|
||||
}
|
||||
|
||||
openChat(event, profile) {
|
||||
this.navCtrl.push(ChatPage, {
|
||||
profile: profile
|
||||
});
|
||||
}
|
||||
|
||||
openProfileDetails(event) {
|
||||
if (!this.detailsOpen) {
|
||||
this.detailsOpen = true;
|
||||
document.querySelector('.profile-toolbar').classList.add('hidden');
|
||||
document.getElementById('detail-overlay').classList.add('open');
|
||||
}
|
||||
}
|
||||
|
||||
previousProfile(event) {
|
||||
this.profile = this.profileService.getPreviousProfile(this.profile._id);
|
||||
this.navCtrl.setRoot(this.navCtrl.getActive().component);
|
||||
}
|
||||
|
||||
showLightbox(event, image) {
|
||||
if (event.target.classList.contains('scroll-content')) {
|
||||
this.navCtrl.push(LightboxPage, {
|
||||
image: image
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
toggleProfileDetails(event) {
|
||||
if (!this.detailsOpen) {
|
||||
this.openProfileDetails(event);
|
||||
} else {
|
||||
this.closeProfileDetails(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
app/src/pages/tabs/tabs.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<ion-tabs id="tab-nav" selectedIndex="0">
|
||||
<ion-tab [root]="tab1Root" tabIcon="contacts"></ion-tab>
|
||||
<ion-tab [root]="tab2Root" tabIcon="compass"></ion-tab>
|
||||
<ion-tab [root]="tab3Root" tabIcon="chatboxes"></ion-tab>
|
||||
<ion-tab [root]="tab4Root" tabIcon="information-circle"></ion-tab>
|
||||
</ion-tabs>
|
||||
21
app/src/pages/tabs/tabs.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { GridPage } from '../grid/grid';
|
||||
import { InformationPage } from '../information/information';
|
||||
import { MessagesPage } from '../messages/messages';
|
||||
import { UsersPage } from '../users/users';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'tabs.html'
|
||||
})
|
||||
export class TabsPage {
|
||||
|
||||
tab1Root = GridPage;
|
||||
tab2Root = UsersPage;
|
||||
tab3Root = MessagesPage;
|
||||
tab4Root = InformationPage;
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
}
|
||||
12
app/src/pages/tell/tell.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons right>
|
||||
<button ion-button icon-only (tap)="close($event)">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
</ion-content>
|
||||
27
app/src/pages/tell/tell.scss
Normal file
@@ -0,0 +1,27 @@
|
||||
page-tell-your-story {
|
||||
|
||||
ion-col {
|
||||
|
||||
&.cruise {
|
||||
background-size: cover;
|
||||
border: 1px solid #000000;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0 37.5% !important;
|
||||
position: relative;
|
||||
|
||||
.placename {
|
||||
bottom: 0.25rem;
|
||||
box-sizing: border-box;
|
||||
color: #acacac;
|
||||
display: inline-block;
|
||||
left: 0.5rem;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
right: 0.25rem;
|
||||
text-overflow: ellipsis;
|
||||
text-shadow: rgba(0, 0, 0, 1);
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
app/src/pages/tell/tell.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
|
||||
import { ProfileService } from '../../services/profiles';
|
||||
|
||||
@Component({
|
||||
selector: 'page-tell-your-story',
|
||||
templateUrl: 'tell.html',
|
||||
providers: [ ProfileService ]
|
||||
})
|
||||
export class TellYourStoryPage {
|
||||
|
||||
tabNavEl: any;
|
||||
|
||||
constructor(public navCtrl: NavController) {
|
||||
this.tabNavEl = document.querySelector('#tab-nav .tabbar');
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
this.tabNavEl.style.display = 'none';
|
||||
}
|
||||
|
||||
close(event) {
|
||||
this.navCtrl.pop();
|
||||
}
|
||||
}
|
||||
20
app/src/pages/users/users.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Urnings</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content no-padding>
|
||||
<ion-grid no-padding>
|
||||
<ion-row align-items-stretch>
|
||||
<ion-col col-4 class="profile tell-your-story">
|
||||
<button ion-button clear large icon-only (tap)="doTellStory()">
|
||||
<ion-icon name="md-person-add"></ion-icon>
|
||||
</button>
|
||||
</ion-col>
|
||||
<ion-col col-4 class="profile" *ngFor="let current of profiles" (tap)="profileTapped($event, current)" (press)="profilePressed($event, current)" [style.backgroundImage]="getBackgroundThumbnail(current.details.pic)">
|
||||
<span class="username" [ngClass]="{ 'online': (current.messages?.length > 0) }">{{current.details.name}}</span>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-content>
|
||||
78
app/src/pages/users/users.scss
Normal file
@@ -0,0 +1,78 @@
|
||||
page-users {
|
||||
|
||||
ion-toolbar {
|
||||
|
||||
.toolbar-title {
|
||||
color: #ffffff;
|
||||
font-size: 2.42em;
|
||||
font-weight: 700;
|
||||
line-height: 1.29;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
|
||||
.row {
|
||||
|
||||
.col {
|
||||
|
||||
&.profile {
|
||||
background-size: cover;
|
||||
border: 1px solid #000000;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0 33% !important;
|
||||
position: relative;
|
||||
|
||||
&.tell-your-story {
|
||||
position: relative;
|
||||
|
||||
button {
|
||||
color: #acacac;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.username {
|
||||
background-size: cover;
|
||||
bottom: 0.25rem;
|
||||
box-sizing: border-box;
|
||||
color: #ffffff;
|
||||
display: inline-block;
|
||||
left: 0.5rem;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
right: 0.25rem;
|
||||
text-overflow: ellipsis;
|
||||
text-shadow: 0 0 3px rgba(0, 0, 0, 1);
|
||||
white-space: nowrap;
|
||||
|
||||
&::before {
|
||||
border: 0.125rem solid #acacac;
|
||||
border-radius: 1rem;
|
||||
bottom: 0.125rem;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 0.8rem;
|
||||
margin-right: 0.5rem;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 0.8rem;
|
||||
}
|
||||
|
||||
&.online {
|
||||
|
||||
&::before {
|
||||
background-color: #00ff00;
|
||||
border-color: #00ff00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
app/src/pages/users/users.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { NavController } from 'ionic-angular';
|
||||
|
||||
import { ChatPage } from '../chat/chat';
|
||||
import { ProfileService } from '../../services/profiles';
|
||||
import { ProfilePage } from '../profile/profile';
|
||||
import { TellYourStoryPage } from '../tell/tell';
|
||||
|
||||
@Component({
|
||||
selector: 'page-users',
|
||||
templateUrl: 'users.html',
|
||||
providers: [ ProfileService ]
|
||||
})
|
||||
export class UsersPage {
|
||||
|
||||
profiles: any;
|
||||
tabNavEl: any;
|
||||
|
||||
constructor(public navCtrl: NavController, public profileService: ProfileService, private _sanitizer: DomSanitizer) {
|
||||
profileService.loadSubmitted().then((data) => {
|
||||
this.profiles = data;
|
||||
});
|
||||
this.tabNavEl = document.querySelector('#tab-nav .tabbar');
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
this.tabNavEl.style.display = 'flex';
|
||||
}
|
||||
|
||||
doTellStory() {
|
||||
this.navCtrl.push(TellYourStoryPage);
|
||||
}
|
||||
|
||||
getBackgroundThumbnail(pics) {
|
||||
return this._sanitizer.bypassSecurityTrustStyle('url(https://appsby.fitz.guru/urge/' + pics.thumb + ')');
|
||||
}
|
||||
|
||||
profilePressed(event, profile) {
|
||||
if (profile.messages && profile.messages.length) {
|
||||
this.navCtrl.push(ChatPage, {
|
||||
profile: profile
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
profileTapped(event, profile) {
|
||||
this.navCtrl.push(ProfilePage, {
|
||||
profile: profile,
|
||||
});
|
||||
}
|
||||
}
|
||||
31
app/src/service-worker.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Check out https://googlechromelabs.github.io/sw-toolbox/ for
|
||||
* more info on how to use sw-toolbox to custom configure your service worker.
|
||||
*/
|
||||
|
||||
|
||||
'use strict';
|
||||
importScripts('./build/sw-toolbox.js');
|
||||
|
||||
self.toolbox.options.cache = {
|
||||
name: 'ionic-cache'
|
||||
};
|
||||
|
||||
// pre-cache our key assets
|
||||
self.toolbox.precache(
|
||||
[
|
||||
'./build/main.js',
|
||||
'./build/vendor.js',
|
||||
'./build/main.css',
|
||||
'./build/polyfills.js',
|
||||
'index.html',
|
||||
'manifest.json'
|
||||
]
|
||||
);
|
||||
|
||||
// dynamically cache any other local assets
|
||||
self.toolbox.router.any('/*', self.toolbox.fastest);
|
||||
|
||||
// for any other requests go to the network, cache,
|
||||
// and then only use that cached resource if your user goes offline
|
||||
self.toolbox.router.default = self.toolbox.networkFirst;
|
||||
97
app/src/services/profiles.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
@Injectable()
|
||||
export class ProfileService {
|
||||
|
||||
endpoint: string = 'https://api.fitz.guru/urnings/profiles';
|
||||
fallback: string = 'assets/data/profiles.json';
|
||||
epSubmitted: string = '/submitted';
|
||||
epVerified: string = '/verified';
|
||||
idMap: any = { all: {}, submitted: {}, verified: {} };
|
||||
profiles: any;
|
||||
|
||||
|
||||
constructor(private http: Http) {
|
||||
this.idMap = {};
|
||||
this.profiles = null;
|
||||
}
|
||||
|
||||
load() {
|
||||
if (this.profiles) {
|
||||
return Promise.resolve(this.profiles);
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.doGetRequest(this.endpoint, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
loadSubmitted() {
|
||||
if (this.profiles && this.profiles.submitted) {
|
||||
return Promise.resolve(this.profiles.submitted);
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.doGetRequest(this.endpoint + this.epSubmitted, resolve, 'submitted');
|
||||
});
|
||||
}
|
||||
|
||||
loadVerified() {
|
||||
if (this.profiles && this.profiles.verified) {
|
||||
return Promise.resolve(this.profiles.verified);
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.doGetRequest(this.endpoint + this.epVerified, resolve, 'verified');
|
||||
});
|
||||
}
|
||||
|
||||
doGetRequest(endpoint, resolve, type = 'all') {
|
||||
this.http.get(endpoint)
|
||||
.map(res => res.json())
|
||||
.subscribe(
|
||||
data => {
|
||||
this.profiles = this.profiles || {};
|
||||
this.profiles[type] = data;
|
||||
this.profiles[type].reduce((map, profile, i) => {
|
||||
map[profile._id] = i;
|
||||
return map;
|
||||
}, this.idMap[type]);
|
||||
resolve(this.profiles[type]);
|
||||
},
|
||||
error => {
|
||||
this.doGetRequest(this.fallback, resolve, type);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
getNextProfile(id, type = 'all') {
|
||||
var nextIdIndex = this.idMap[type][id] + 1;
|
||||
nextIdIndex = nextIdIndex >= this.profiles[type].length ? 0 : nextIdIndex;
|
||||
return this.profiles[type][nextIdIndex];
|
||||
}
|
||||
|
||||
getPreviousProfile(id, type = 'all') {
|
||||
var prevIdIndex = this.idMap[type][id] - 1;
|
||||
prevIdIndex = prevIdIndex < 0 ? (this.profiles[type].length - 1) : prevIdIndex;
|
||||
return this.profiles[type][prevIdIndex];
|
||||
}
|
||||
|
||||
getProfiles() {
|
||||
return this.profiles.all;
|
||||
}
|
||||
|
||||
getProfileById(id) {
|
||||
return this.profiles[this.idMap[id]];
|
||||
}
|
||||
|
||||
getSubmittedProfiles() {
|
||||
return this.profiles.submitted;
|
||||
}
|
||||
|
||||
getVerifiedProfiles() {
|
||||
return this.profiles.verified;
|
||||
}
|
||||
}
|
||||
88
app/src/theme/variables.scss
Normal file
@@ -0,0 +1,88 @@
|
||||
// Ionic Variables and Theming. For more info, please see:
|
||||
// http://ionicframework.com/docs/theming/
|
||||
|
||||
// Font path is used to include ionicons,
|
||||
// roboto, and noto sans fonts
|
||||
$font-path: "../assets/fonts";
|
||||
|
||||
|
||||
// The app direction is used to include
|
||||
// rtl styles in your app. For more info, please see:
|
||||
// http://ionicframework.com/docs/theming/rtl-support/
|
||||
$app-direction: ltr;
|
||||
|
||||
|
||||
@import "ionic.globals";
|
||||
|
||||
|
||||
// Shared Variables
|
||||
// --------------------------------------------------
|
||||
// To customize the look and feel of this app, you can override
|
||||
// the Sass variables found in Ionic's source scss files.
|
||||
// To view all the possible Ionic variables, see:
|
||||
// http://ionicframework.com/docs/theming/overriding-ionic-variables/
|
||||
|
||||
|
||||
|
||||
|
||||
// Named Color Variables
|
||||
// --------------------------------------------------
|
||||
// Named colors makes it easy to reuse colors on various components.
|
||||
// It's highly recommended to change the default colors
|
||||
// to match your app's branding. Ionic uses a Sass map of
|
||||
// colors so you can add, rename and remove colors as needed.
|
||||
// The "primary" color is the only required color in the map.
|
||||
|
||||
$colors: (
|
||||
primary: #488aff,
|
||||
secondary: #32db64,
|
||||
danger: #f53d3d,
|
||||
light: #f4f4f4,
|
||||
dark: #222
|
||||
);
|
||||
|
||||
|
||||
// App iOS Variables
|
||||
// --------------------------------------------------
|
||||
// iOS only Sass variables can go here
|
||||
|
||||
|
||||
|
||||
|
||||
// App Material Design Variables
|
||||
// --------------------------------------------------
|
||||
// Material Design only Sass variables can go here
|
||||
|
||||
|
||||
|
||||
|
||||
// App Windows Variables
|
||||
// --------------------------------------------------
|
||||
// Windows only Sass variables can go here
|
||||
|
||||
|
||||
|
||||
|
||||
// App Theme
|
||||
// --------------------------------------------------
|
||||
// Ionic apps can have different themes applied, which can
|
||||
// then be future customized. This import comes last
|
||||
// so that the above variables are used and Ionic's
|
||||
// default are overridden.
|
||||
|
||||
@import "ionic.theme.default";
|
||||
|
||||
|
||||
// Ionicons
|
||||
// --------------------------------------------------
|
||||
// The premium icon font for Ionic. For more info, please see:
|
||||
// http://ionicframework.com/docs/ionicons/
|
||||
|
||||
@import "ionic.ionicons";
|
||||
|
||||
|
||||
// Fonts
|
||||
// --------------------------------------------------
|
||||
|
||||
@import "roboto";
|
||||
@import "noto-sans";
|
||||
28
app/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
],
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"target": "es5"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/__tests__/*.ts"
|
||||
],
|
||||
"compileOnSave": false,
|
||||
"atom": {
|
||||
"rewriteTsconfig": false
|
||||
}
|
||||
}
|
||||
11
app/tslint.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-duplicate-variable": true,
|
||||
"no-unused-variable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"node_modules/tslint-eslint-rules/dist/rules"
|
||||
]
|
||||
}
|
||||