Browse Source

added image upload feature - front end only

Rachelle 6 years ago
parent
commit
bb4ff6a60b

+ 1
- 1
MyPassionProject/src/main/java/com/example/demo/Controller/UserController.java View File

@@ -26,7 +26,7 @@ public class UserController {
26 26
     return userService.getUserById(userId);
27 27
   }
28 28
 
29
-  @PutMapping(value = "/users/{userId}")
29
+  @PatchMapping(value = "/users/{userId}")
30 30
   public ResponseEntity<User> updateSummary(@RequestBody User user, @PathVariable Long userId){
31 31
     return userService.updateUserProfile(user, userId);
32 32
   }

+ 20
- 0
MyPassionProject/src/main/java/com/example/demo/Entity/User.java View File

@@ -4,6 +4,11 @@ import lombok.Getter;
4 4
 import lombok.Setter;
5 5
 
6 6
 import javax.persistence.*;
7
+import javax.validation.constraints.NotBlank;
8
+import java.io.File;
9
+import java.io.FileInputStream;
10
+import java.io.FileNotFoundException;
11
+import java.io.IOException;
7 12
 import java.util.HashSet;
8 13
 import java.util.Set;
9 14
 
@@ -29,6 +34,9 @@ public class User {
29 34
             inverseJoinColumns = {@JoinColumn(name = "game_id")})
30 35
     private Set<Game> gameLibrary;
31 36
 
37
+//    @Lob
38
+//    private byte[] profilePic;
39
+
32 40
     public void addGames(Game game){
33 41
         if(gameLibrary==null){
34 42
             this.gameLibrary = new HashSet<Game>();
@@ -51,4 +59,16 @@ public class User {
51 59
     public String getProfileName() {
52 60
         return profileName;
53 61
     }
62
+
63
+//    public void setProfilePic(File input){
64
+//        byte[] picInBytes = new byte[(int)input.length()];
65
+//        try {
66
+//            FileInputStream fileInputStream = new FileInputStream(input);
67
+//            fileInputStream.read(picInBytes);
68
+//            fileInputStream.close();
69
+//        } catch (IOException e) {
70
+//            e.printStackTrace();
71
+//        }
72
+//        profilePic=picInBytes;
73
+//    }
54 74
 }

+ 1
- 0
MyPassionProjectClient/config.xml View File

@@ -81,4 +81,5 @@
81 81
     <plugin name="cordova-plugin-splashscreen" spec="5.0.2" />
82 82
     <plugin name="cordova-plugin-ionic-webview" spec="1.1.19" />
83 83
     <plugin name="cordova-plugin-ionic-keyboard" spec="2.0.5" />
84
+    <plugin name="cordova-plugin-file" spec="^6.0.1" />
84 85
 </widget>

+ 25
- 0
MyPassionProjectClient/package-lock.json View File

@@ -99,11 +99,26 @@
99 99
         "tslib": "^1.7.1"
100 100
       }
101 101
     },
102
+    "@ionic-native/camera": {
103
+      "version": "4.10.0",
104
+      "resolved": "https://registry.npmjs.org/@ionic-native/camera/-/camera-4.10.0.tgz",
105
+      "integrity": "sha512-mH/E1LnNCphnX9dAForzSX85mzkyTQ6mUce1TM1sGHeo0rA6vCvYsJw5cPnbDmm67rDlZhgbFsF5H6bGe/fCnQ=="
106
+    },
102 107
     "@ionic-native/core": {
103 108
       "version": "4.9.2",
104 109
       "resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-4.9.2.tgz",
105 110
       "integrity": "sha512-dWV08EjK82KCDU9CM4UVEZ5yrNj9Uz7GoL818MHeJfDS4uBZT+U7qqcHEklc3+R+lMLBuTnoWe+TX3O5SwmW9A=="
106 111
     },
112
+    "@ionic-native/file": {
113
+      "version": "4.10.0",
114
+      "resolved": "https://registry.npmjs.org/@ionic-native/file/-/file-4.10.0.tgz",
115
+      "integrity": "sha512-W3Yap6vd/f3bYG2zNG+6vjRymUuT8DrVOW/uyOJt4YfG8CdTauj0nsg501+BKXIvFu8nv1yD3t4qTyOKintKIQ=="
116
+    },
117
+    "@ionic-native/file-path": {
118
+      "version": "4.10.0",
119
+      "resolved": "https://registry.npmjs.org/@ionic-native/file-path/-/file-path-4.10.0.tgz",
120
+      "integrity": "sha512-ipGzLk3uPifxH53yLQIKavuICr2TSwcqWf1YXHUbTRQFZPX5FJxAhs137wtcx3sEYsKnysvFieTNnlIB/S6Ugg=="
121
+    },
107 122
     "@ionic-native/splash-screen": {
108 123
       "version": "4.9.2",
109 124
       "resolved": "https://registry.npmjs.org/@ionic-native/splash-screen/-/splash-screen-4.9.2.tgz",
@@ -114,6 +129,11 @@
114 129
       "resolved": "https://registry.npmjs.org/@ionic-native/status-bar/-/status-bar-4.9.2.tgz",
115 130
       "integrity": "sha512-k3ZJDAdCwBhubjw29gsRlNhEcA06JIxm5xeSpSWzhrvY+3+eHUgTwNMYGkkVurmMYFcuWPEOCpNZ2oelMqYA0Q=="
116 131
     },
132
+    "@ionic-native/transfer": {
133
+      "version": "3.14.0",
134
+      "resolved": "https://registry.npmjs.org/@ionic-native/transfer/-/transfer-3.14.0.tgz",
135
+      "integrity": "sha1-Q/t7kScvJFEzBjsKGllHknsJsho="
136
+    },
117 137
     "@ionic/app-scripts": {
118 138
       "version": "3.1.11",
119 139
       "resolved": "https://registry.npmjs.org/@ionic/app-scripts/-/app-scripts-3.1.11.tgz",
@@ -1104,6 +1124,11 @@
1104 1124
       "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
1105 1125
       "dev": true
1106 1126
     },
1127
+    "cordova-plugin-file": {
1128
+      "version": "6.0.1",
1129
+      "resolved": "https://registry.npmjs.org/cordova-plugin-file/-/cordova-plugin-file-6.0.1.tgz",
1130
+      "integrity": "sha1-SWBrjBWlaI1HKPkuSnMloGHeB/U="
1131
+    },
1107 1132
     "core-util-is": {
1108 1133
       "version": "1.0.2",
1109 1134
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",

+ 11
- 1
MyPassionProjectClient/package.json View File

@@ -20,10 +20,15 @@
20 20
     "@angular/http": "5.2.11",
21 21
     "@angular/platform-browser": "5.2.11",
22 22
     "@angular/platform-browser-dynamic": "5.2.11",
23
+    "@ionic-native/camera": "^4.10.0",
23 24
     "@ionic-native/core": "~4.9.2",
25
+    "@ionic-native/file": "^4.10.0",
26
+    "@ionic-native/file-path": "^4.10.0",
24 27
     "@ionic-native/splash-screen": "~4.9.2",
25 28
     "@ionic-native/status-bar": "~4.9.2",
29
+    "@ionic-native/transfer": "^3.14.0",
26 30
     "@ionic/storage": "2.1.3",
31
+    "cordova-plugin-file": "^6.0.1",
27 32
     "ionic-angular": "3.9.2",
28 33
     "ionicons": "3.0.0",
29 34
     "rxjs": "5.5.11",
@@ -34,5 +39,10 @@
34 39
     "@ionic/app-scripts": "3.1.11",
35 40
     "typescript": "~2.6.2"
36 41
   },
37
-  "description": "An Ionic project"
42
+  "description": "An Ionic project",
43
+  "cordova": {
44
+    "plugins": {
45
+      "cordova-plugin-file": {}
46
+    }
47
+  }
38 48
 }

+ 12
- 1
MyPassionProjectClient/src/app/app.module.ts View File

@@ -8,12 +8,18 @@ import {MenuPageModule} from "../pages/menu/menu.module";
8 8
 import { HttpClientModule } from '../../node_modules/@angular/common/http';
9 9
 import { UsersService } from '../providers/users-service';
10 10
 import { ProfileModalPage } from '../pages/settings/profile-modal';
11
+import { PictureModalPage } from '../pages/settings/picture-modal';
12
+import { Transfer } from '../../node_modules/@ionic-native/transfer';
13
+import { Camera } from '../../node_modules/@ionic-native/camera';
14
+import { FilePath } from '../../node_modules/@ionic-native/file-path';
15
+import {File} from '../../node_modules/@ionic-native/file';
11 16
 
12 17
 
13 18
 @NgModule({
14 19
   declarations: [
15 20
     MyApp,
16
-    ProfileModalPage
21
+    ProfileModalPage,
22
+    PictureModalPage
17 23
   ],
18 24
   imports: [
19 25
     BrowserModule,
@@ -25,11 +31,16 @@ import { ProfileModalPage } from '../pages/settings/profile-modal';
25 31
   entryComponents: [
26 32
     MyApp,
27 33
     ProfileModalPage,
34
+    PictureModalPage
28 35
    ],
29 36
   providers: [
30 37
     StatusBar,
31 38
     SplashScreen,
32 39
     UsersService,
40
+    File,
41
+    Transfer,
42
+    Camera,
43
+    FilePath,
33 44
     {provide: ErrorHandler, useClass: IonicErrorHandler}
34 45
   ]
35 46
 })

+ 0
- 1
MyPassionProjectClient/src/pages/profile/profile.html View File

@@ -18,7 +18,6 @@
18 18
     <ion-card-header>
19 19
       <strong>My Profile</strong>
20 20
     </ion-card-header>
21
-
22 21
     <img src="/assets/imgs/avatar.png"/>
23 22
 
24 23
     <ion-card-content>

+ 29
- 0
MyPassionProjectClient/src/pages/settings/picture-modal.html View File

@@ -0,0 +1,29 @@
1
+<ion-header>
2
+    <ion-navbar>
3
+        <ion-title>Upload Picture</ion-title>
4
+        <ion-buttons end>
5
+                <button ion-button (click)="dismiss()">
6
+                  <span ion-text showWhen="ios,core">Cancel</span>
7
+                  <ion-icon name="md-close" showWhen="android,windows"></ion-icon>
8
+                </button>
9
+              </ion-buttons>
10
+    </ion-navbar>
11
+</ion-header>
12
+       
13
+      <ion-content padding>
14
+        <img src="{{pathForImage(lastImage)}}" style="width: 100%" [hidden]="lastImage === null">
15
+        <h3 [hidden]="lastImage !== null">Please Select Image!</h3>
16
+      </ion-content>
17
+       
18
+      <ion-footer>
19
+        <ion-toolbar>
20
+          <ion-buttons>
21
+            <button ion-button icon-left (click)="presentActionSheet()">
22
+              <ion-icon name="camera"></ion-icon>Select Image
23
+            </button>
24
+            <button ion-button icon-left (click)="uploadImage()" [disabled]="lastImage === null">
25
+              <ion-icon name="cloud-upload"></ion-icon>Upload
26
+            </button>
27
+          </ion-buttons>
28
+        </ion-toolbar>
29
+      </ion-footer>

+ 150
- 0
MyPassionProjectClient/src/pages/settings/picture-modal.ts View File

@@ -0,0 +1,150 @@
1
+import { Component } from "../../../node_modules/@angular/core";
2
+import { NavController, ActionSheetController, ToastController, Platform, LoadingController, Loading, ViewController } from 'ionic-angular';
3
+import { File } from '@ionic-native/file';
4
+import { Camera } from '@ionic-native/camera';
5
+import { Transfer, TransferObject } from '@ionic-native/transfer';
6
+import { FilePath } from '@ionic-native/file-path';
7
+
8
+declare var cordova: any;
9
+
10
+@Component({
11
+    selector: 'page-picture-modal',
12
+    templateUrl: './picture-modal.html'
13
+})
14
+
15
+export class PictureModalPage{
16
+    lastImage: string = null;
17
+    loading: Loading;
18
+    
19
+    constructor(public viewCtrl: ViewController, public navCtrl: NavController, private camera: Camera, private transfer: Transfer, private file: File, private filePath: FilePath, public actionSheetCtrl: ActionSheetController, public toastCtrl: ToastController, public platform: Platform, public loadingCtrl: LoadingController){
20
+    }
21
+
22
+    public presentActionSheet() {
23
+        let actionSheet = this.actionSheetCtrl.create({
24
+          title: 'Select Image Source',
25
+          buttons: [
26
+            {
27
+              text: 'Load from Library',
28
+              handler: () => {
29
+                this.takePicture(this.camera.PictureSourceType.PHOTOLIBRARY);
30
+              }
31
+            },
32
+            {
33
+              text: 'Use Camera',
34
+              handler: () => {
35
+                this.takePicture(this.camera.PictureSourceType.CAMERA);
36
+              }
37
+            },
38
+            {
39
+              text: 'Cancel',
40
+              role: 'cancel'
41
+            }
42
+          ]
43
+        });
44
+        actionSheet.present();
45
+      }
46
+
47
+      public takePicture(sourceType) {
48
+        // Create options for the Camera Dialog
49
+        var options = {
50
+          quality: 100,
51
+          sourceType: sourceType,
52
+          saveToPhotoAlbum: false,
53
+          correctOrientation: true
54
+        };
55
+       
56
+        // Get the data of an image
57
+        this.camera.getPicture(options).then((imagePath) => {
58
+          // Special handling for Android library
59
+          if (this.platform.is('android') && sourceType === this.camera.PictureSourceType.PHOTOLIBRARY) {
60
+            this.filePath.resolveNativePath(imagePath)
61
+              .then(filePath => {
62
+                let correctPath = filePath.substr(0, filePath.lastIndexOf('/') + 1);
63
+                let currentName = imagePath.substring(imagePath.lastIndexOf('/') + 1, imagePath.lastIndexOf('?'));
64
+                this.copyFileToLocalDir(correctPath, currentName, this.createFileName());
65
+              });
66
+          } else {
67
+            var currentName = imagePath.substr(imagePath.lastIndexOf('/') + 1);
68
+            var correctPath = imagePath.substr(0, imagePath.lastIndexOf('/') + 1);
69
+            this.copyFileToLocalDir(correctPath, currentName, this.createFileName());
70
+          }
71
+        }, (err) => {
72
+          this.presentToast('Error while selecting image.');
73
+        });
74
+      }
75
+
76
+      // Create a new name for the image
77
+    private createFileName() {
78
+        var d = new Date(),
79
+        n = d.getTime(),
80
+        newFileName =  n + ".jpg";
81
+        return newFileName;
82
+    }
83
+
84
+        // Copy the image to a local folder
85
+    private copyFileToLocalDir(namePath, currentName, newFileName) {
86
+        this.file.copyFile(namePath, currentName, cordova.file.dataDirectory, newFileName).then(success => {
87
+            this.lastImage = newFileName;
88
+        }, error => {
89
+            this.presentToast('Error while storing file.');
90
+        });
91
+    }
92
+
93
+    private presentToast(text) {
94
+        let toast = this.toastCtrl.create({
95
+            message: text,
96
+            duration: 3000,
97
+            position: 'top'
98
+        });
99
+        toast.present();
100
+    }
101
+
102
+        // Always get the accurate path to your apps folder
103
+    public pathForImage(img) {
104
+        if (img === null) {
105
+            return '';
106
+        } else {
107
+            return cordova.file.dataDirectory + img;
108
+        }
109
+    }
110
+
111
+    public uploadImage() {
112
+        // Destination URL
113
+        var url = "http://localhost:8080/upload";
114
+       
115
+        // File for Upload
116
+        var targetPath = this.pathForImage(this.lastImage);
117
+       
118
+        // File name only
119
+        var filename = this.lastImage;
120
+       
121
+        var options = {
122
+          fileKey: "file",
123
+          fileName: filename,
124
+          chunkedMode: false,
125
+          mimeType: "multipart/form-data",
126
+          params : {'fileName': filename}
127
+        };
128
+       
129
+        const fileTransfer: TransferObject = this.transfer.create();
130
+       
131
+        this.loading = this.loadingCtrl.create({
132
+          content: 'Uploading...',
133
+        });
134
+        this.loading.present();
135
+       
136
+        // Use the FileTransfer to upload the image
137
+        fileTransfer.upload(targetPath, url, options).then(data => {
138
+          this.loading.dismissAll()
139
+          this.presentToast('Image succesful uploaded.');
140
+        }, err => {
141
+          this.loading.dismissAll()
142
+          this.presentToast('Error while uploading file.');
143
+        });
144
+      }
145
+
146
+      dismiss() {
147
+        this.viewCtrl.dismiss();
148
+      }
149
+}
150
+

+ 27
- 29
MyPassionProjectClient/src/pages/settings/profile-modal.html View File

@@ -1,30 +1,28 @@
1 1
 <ion-header>
2
-
3
-        <ion-navbar>
4
-          <ion-title>Update Profile</ion-title>
5
-          <ion-buttons end>
6
-                <button ion-button (click)="dismiss()">
7
-                    <span ion-text showWhen="ios,core">Cancel</span>
8
-                    <ion-icon name="md-close" showWhen="android,windows"></ion-icon>
9
-                </button>
10
-            </ion-buttons>
11
-        </ion-navbar>
12
-      
13
-      </ion-header>   
14
-      <ion-content padding>
15
-        <form (submit)=updateProfile()>
16
-          <ion-list>
17
-            <ion-item>
18
-              <ion-label stacked>Profile Name</ion-label>
19
-              <ion-input type="text" [(ngModel)]="user.profileName" name="profilename"></ion-input>
20
-            </ion-item>
21
-            <ion-item>
22
-                <ion-label stacked>Summary</ion-label>
23
-                <ion-textarea type="text" [(ngModel)]="user.summary" name="summary"></ion-textarea>
24
-            </ion-item>
25
-            <div padding>
26
-                <button ion-button color="primary" block clear>Update</button>
27
-            </div>
28
-          </ion-list>
29
-        </form>
30
-      </ion-content>
2
+  <ion-navbar>
3
+      <ion-title>Update Profile</ion-title>
4
+      <ion-buttons end>
5
+        <button ion-button (click)="dismiss()">
6
+          <span ion-text showWhen="ios,core">Cancel</span>
7
+          <ion-icon name="md-close" showWhen="android,windows"></ion-icon>
8
+        </button>
9
+      </ion-buttons>
10
+  </ion-navbar>    
11
+</ion-header>   
12
+<ion-content padding>
13
+  <form (submit)=updateProfile()>
14
+    <ion-list>
15
+      <ion-item>
16
+        <ion-label stacked>Profile Name</ion-label>
17
+        <ion-input type="text" [(ngModel)]="user.profileName" name="profilename"></ion-input>
18
+      </ion-item>
19
+      <ion-item>
20
+        <ion-label stacked>Summary</ion-label>
21
+        <ion-textarea type="text" [(ngModel)]="user.summary" name="summary"></ion-textarea>
22
+      </ion-item>
23
+      <div padding>
24
+        <button ion-button color="primary" block clear>Update</button>
25
+      </div>
26
+    </ion-list>
27
+  </form>
28
+</ion-content>

+ 9
- 0
MyPassionProjectClient/src/pages/settings/settings.html View File

@@ -1,6 +1,12 @@
1 1
 <ion-header>
2 2
     <ion-navbar>
3 3
       <ion-title>Settings</ion-title>
4
+      <ion-buttons end>
5
+          <button ion-button (click)="dismiss()">
6
+            <span ion-text showWhen="ios,core">Done</span>
7
+            <ion-icon name="md-close" showWhen="android,windows"></ion-icon>
8
+          </button>
9
+        </ion-buttons>
4 10
     </ion-navbar>
5 11
   </ion-header>
6 12
   <ion-content padding>
@@ -8,6 +14,9 @@
8 14
           <button ion-item (click)="updateProfile()">
9 15
             Update Profile
10 16
           </button>  
17
+          <button ion-item (click)="uploadPicture()">
18
+              Update Profile Picture
19
+            </button> 
11 20
       </ion-list>
12 21
   </ion-content>
13 22
 

+ 12
- 1
MyPassionProjectClient/src/pages/settings/settings.ts View File

@@ -1,6 +1,8 @@
1 1
 import { Component } from '@angular/core';
2
-import { IonicPage, NavController, NavParams, ModalController } from 'ionic-angular';
2
+import { IonicPage, NavController, NavParams, ModalController, ViewController } from 'ionic-angular';
3 3
 import { ProfileModalPage } from './profile-modal';
4
+import { PictureModalPage } from './picture-modal';
5
+import { ProfilePage } from '../profile/profile';
4 6
 
5 7
 @IonicPage()
6 8
 @Component({
@@ -21,4 +23,13 @@ export class SettingsPage {
21 23
     modal.present();
22 24
   }
23 25
 
26
+  uploadPicture() {
27
+    let modal = this.modalCtrl.create(PictureModalPage);
28
+    modal.present();
29
+  }
30
+
31
+  dismiss() {
32
+    this.navCtrl.setRoot(ProfilePage);
33
+  }
34
+
24 35
 }

+ 1
- 1
MyPassionProjectClient/src/providers/users-service.ts View File

@@ -21,7 +21,7 @@ export class UsersService{
21 21
   }
22 22
 
23 23
   updateProfile(profile): Observable<any>{
24
-    return this.http.put(this.USERS_API + "/1", profile);
24
+    return this.http.patch(this.USERS_API + "/1", profile);
25 25
   }
26 26
 
27 27
   getUserProfile(){