Completely implement all basic requirements for application

master
Joshua Sigona 4 years ago
parent d3ae365ed9
commit 2a4d72c848
  1. 45
      family-tracker/src/App.css
  2. 173
      family-tracker/src/App.js
  3. 46
      src/main/java/sig/family/Endpoints.java
  4. 166
      src/main/java/sig/family/FamilyApp.java
  5. 5
      src/main/java/sig/family/FamilyContainer.java
  6. 46
      src/main/java/sig/family/FamilyMemberContainer.java
  7. 24
      src/main/java/sig/family/GPSUser.java
  8. 19
      src/main/java/sig/family/Location.java
  9. 4
      src/main/java/sig/family/LocationRepository.java
  10. 5
      src/main/java/sig/family/NotificationRepository.java

@ -22,6 +22,51 @@
} }
} }
.fadein {
animation-name: fadein;
animation-duration: 4s;
}
.badfadein {
animation-name: badfadein;
animation-duration: 4s;
background-color:#faa;
}
.badchildfadein {
animation-name: badchildfadein;
animation-duration: 4s;
background-color:#fae;
}
.tinytime{
font-size:12px;
position:absolute;
bottom:4px;
right:7px;
/*z-index:-10000;*/
color:#666;
font-style:italic;
/*background-color:rgba(240,240,255,0.8);*/
}
@keyframes fadein {
0% {opacity:0;
background-color:#6ef;}
100% {opacity:1;
background-color:#fff;}
}
@keyframes badfadein {
0% {opacity:0;
background-color:#f00;}
100% {opacity:1;
background-color:#faa;}
}
@keyframes badchildfadein {
0% {opacity:0;
background-color:#f0a;}
100% {opacity:1;
background-color:#fae;}
}
.App-header { .App-header {
background-color: #282c34; background-color: #282c34;
min-height: 100vh; min-height: 100vh;

@ -12,20 +12,183 @@ import {
const axios = require('axios'); const axios = require('axios');
var mapX=-110.267
var mapY=31.544
var mapXScale=0.00005
var mapYScale=0.00005
var ctx=undefined,locations=[],mousePos={x:0,y:0},members=[],requiresUpdate=false;
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
setInterval(()=>{
if (document.getElementById("map")!==null) {
var c = document.getElementById("map");
//console.log("Update map.")
requiresUpdate=!requiresUpdate;
if (ctx===undefined||ctx===null) {
ctx = c.getContext("2d");
//Draw all known locations.
axios.get("http://localhost:8080/knownlocation")
.then((data)=>{locations=data.data})
} else {
ctx.clearRect(0, 0, c.width, c.height);
for (var loc of locations) {
ctx.beginPath();
if (loc.safe) {
ctx.fillStyle="#6a6"
} else {
ctx.fillStyle="#a66"
}
ctx.arc((loc.x-mapX)/mapXScale,(loc.y-mapY)/mapYScale,10,0,2*Math.PI)
ctx.fill();
}
for (var m of members) {
ctx.beginPath();
ctx.fillStyle="#22f"
ctx.arc((m.x-mapX)/mapXScale,(m.y-mapY)/mapYScale,5,0,2*Math.PI);
ctx.fill();
}
ctx.font = "22px Verdana";
for (var loc of locations) {
if (mousePos.x>=(loc.x-mapX)/mapXScale-7 && mousePos.x<=(loc.x-mapX)/mapXScale+7 &&
mousePos.y>=(loc.y-mapY)/mapYScale-7 && mousePos.y<=(loc.y-mapY)/mapYScale+7) {
var size = ctx.measureText(loc.name)
ctx.fillStyle="#eef"
ctx.fillRect(mousePos.x-size.width/2-2,mousePos.y-30,size.width+4,24)
ctx.fillStyle="#000"
ctx.fillText(loc.name,mousePos.x-size.width/2,mousePos.y-8)
}
}
var labelY = -30;
ctx.font = "14px Verdana";
for (var m of members) {
if (mousePos.x>=(m.x-mapX)/mapXScale-7 && mousePos.x<=(m.x-mapX)/mapXScale+7 &&
mousePos.y>=(m.y-mapY)/mapYScale-7 && mousePos.y<=(m.y-mapY)/mapYScale+7) {
var size = ctx.measureText(m.firstName+" "+m.lastName)
ctx.fillStyle="#eef"
ctx.fillRect(mousePos.x-size.width/2-2,mousePos.y-20+labelY,size.width+4,14)
ctx.fillStyle="#000"
ctx.fillText(m.firstName+" "+m.lastName,mousePos.x-size.width/2,mousePos.y-8+labelY)
labelY-=20;
}
}
}
} else {
ctx=null;
}
},50)
function Map(p) { function Map(p) {
const [familyId,setFamilyId] = useState(0)
const [family,setFamily] = useState([])
useEffect(()=>{
p.setActive("/map") p.setActive("/map")
})
useEffect(()=>{
if (p.family[familyId]) {
members=p.family[familyId].members
//console.log(members)
}
},[p.family])
useEffect(()=>{
if (family[familyId]) {
members=family[familyId].members
//console.log(members)
}
},[family])
useEffect(()=>{
const interval = setInterval(()=>{
axios.get("http://localhost:8080/family")
.then((data)=>{setFamily(data.data)})
},50)
return () => clearInterval(interval);
},[requiresUpdate])
return ( return (
<> <>
<h1>Map View</h1> <h1>Map View</h1>
<select id="family" name="family" value={familyId} onChange={(e)=>{setFamilyId(e.target.value);
members=p.family[e.target.value].members}}>
{p.family.map((fam)=><option key={fam.id-1} value={fam.id-1}>{fam.name}</option>)}
</select>
<canvas id="map" width="800" height="600" onMouseMove={(e)=>{
mousePos=getMousePos(e.target,e)
}
}/>
</> </>
); );
} }
function Message(p) { function Message(p) {
const [familyId,setFamilyId] = useState(0)
const [memberId,setMemberId] = useState(null)
const [notifications,setNotifications] = useState([])
const [timestamp,setTimestamp] = useState("")
function displayTimestamp(date) {
var seconds = Math.floor((Date.now()-new Date(date))/1000);
seconds-=32400
var minutes = Math.floor(seconds/60);
//console.log(Date.now()-new Date(date))
//console.log(Date.now())
var hours = Math.floor(minutes/60)
if (seconds<60) {return <>{seconds} {"second"+((seconds!==1)?"s":"")} ago</>} else
if (minutes<60) {return <>{minutes} {"minute"+((minutes!==1)?"s":"")} ago</>} else {
return <>{hours} {"hour"+((hours!==1)?"s":"")} ago</>
}
}
useEffect(()=>{
p.setActive("/messages") p.setActive("/messages")
})
useEffect(()=>{
if (memberId!==null) {
axios.get("http://localhost:8080/notification/"+memberId)
.then((data)=>{
setNotifications(data.data)
})
} else {
setNotifications([])
}
const interval = setInterval(()=>{
if (memberId!==null) {
axios.get("http://localhost:8080/notification/"+memberId)
.then((data)=>{
setNotifications(data.data)
//console.log(data.data)
})
}
},50)
return () => clearInterval(interval);
},[memberId,familyId])
useEffect(()=>{
if (p.family[0]) {
setMemberId(p.family[0].members[0].id)
}
},[p.family])
return ( return (
<> <>
<h1>Message View</h1> <h1>Message View</h1>
<select id="family" name="family" value={familyId} onChange={(e)=>{setFamilyId(e.target.value);
members=p.family[e.target.value].members;
if (p.family[e.target.value].members.length>0){setMemberId(p.family[e.target.value].members[0].id)} else {setMemberId(null)}}}>
{p.family.map((fam)=><option key={fam.id-1} value={fam.id-1}>{fam.name}</option>)}
</select>
<select id="member" name="member" value={memberId} onChange={(e)=>{setMemberId(e.target.value)}}>
{(p.family[familyId] && p.family[familyId].members)?p.family[familyId].members.map((member)=><option key={member.id} value={member.id}>{member.firstName+" "+member.lastName}</option>):<></>}
</select>
<br/>
<br/>
{notifications.map((notification)=><React.Fragment key={notification.id}>{(notification.notificationType===0)?<div className="fadein alert alert-info">{notification.message}<span className="tinytime">{displayTimestamp(notification.date)}</span></div>:(notification.message.includes("You are"))?<div className="badfadein alert alert-danger">{notification.message}<span className="tinytime">{displayTimestamp(notification.date)}</span></div>:<div className="badchildfadein alert alert-warning">{notification.message}<span className="tinytime">{displayTimestamp(notification.date)}</span></div>}</React.Fragment>)}
</> </>
); );
} }
@ -76,7 +239,8 @@ function Member(p) {
setMobileDevice("") setMobileDevice("")
setRelationship("") setRelationship("")
setDisabled(false) setDisabled(false)
})}}>Add</button> })
.catch(setDisabled(false))}}>Add</button>
</div> </div>
</div> </div>
</> </>
@ -113,6 +277,7 @@ function Editable(p) {
{(editing)?<><input type="text" style={style} value={value} disabled={disabled} onChange={(e)=>{setValue(e.target.value);setStyle({})}} onKeyPress={(e)=>{ {(editing)?<><input type="text" style={style} value={value} disabled={disabled} onChange={(e)=>{setValue(e.target.value);setStyle({})}} onKeyPress={(e)=>{
if (e.key==='Enter') { if (e.key==='Enter') {
if (value.length===0) { if (value.length===0) {
setStyle({border:"1px solid red",background:"#fdd"})
return new Error("Cannot accept 0 length string.") return new Error("Cannot accept 0 length string.")
} }
setDisabled(true) setDisabled(true)
@ -126,7 +291,7 @@ function Editable(p) {
}) })
.catch((err)=>{ .catch((err)=>{
setDisabled(false) setDisabled(false)
setStyle({border:"1px solid red"}) setStyle({border:"1px solid red",background:"#fdd"})
}) })
} }
} }
@ -215,10 +380,10 @@ function App() {
<div className="col-sm-9 order-1 order-sm-1 order-md-2"> <div className="col-sm-9 order-1 order-sm-1 order-md-2">
<Switch> <Switch>
<Route path="/map"> <Route path="/map">
<Map setActive={setActive}/> <Map setActive={setActive} family={family}/>
</Route> </Route>
<Route path="/messages"> <Route path="/messages">
<Message setActive={setActive}/> <Message setActive={setActive} family={family}/>
</Route> </Route>
<Route path="/members"> <Route path="/members">
<Member setActive={setActive} family={family} update={update} setUpdate={setUpdate}/> <Member setActive={setActive} family={family} update={update} setUpdate={setUpdate}/>

@ -1,5 +1,8 @@
package sig.family; package sig.family;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
@ -39,12 +42,12 @@ import javax.websocket.server.PathParam;
@RestController @RestController
public class Endpoints { public class Endpoints {
FamilyRepository families; public static FamilyRepository families;
FamilyMemberRepository members; public static FamilyMemberRepository members;
FamilyRelationshipRepository relationships; public static FamilyRelationshipRepository relationships;
LocationRepository locations; public static LocationRepository locations;
KnownLocationRepository knownlocations; public static KnownLocationRepository knownlocations;
NotificationRepository notifications; public static NotificationRepository notifications;
RestTemplate connection = new RestTemplate(); RestTemplate connection = new RestTemplate();
public Endpoints(FamilyRepository families, public Endpoints(FamilyRepository families,
@ -53,19 +56,19 @@ public class Endpoints {
LocationRepository locations, LocationRepository locations,
KnownLocationRepository knownlocations, KnownLocationRepository knownlocations,
NotificationRepository notifications) { NotificationRepository notifications) {
this.families=families; Endpoints.families=families;
this.members=members; Endpoints.members=members;
this.relationships=relationships; Endpoints.relationships=relationships;
this.locations=locations; Endpoints.locations=locations;
this.knownlocations=knownlocations; Endpoints.knownlocations=knownlocations;
this.notifications=notifications; Endpoints.notifications=notifications;
} }
@GetMapping("/family") @GetMapping("/family")
public List<FamilyContainer> _1() { public List<FamilyContainer> _1() {
List<FamilyContainer> list = new ArrayList<>(); List<FamilyContainer> list = new ArrayList<>();
for (Family f : families.findAll()) { for (Family f : families.findAll()) {
list.add(new FamilyContainer(f.getName(),families,relationships,members)); list.add(new FamilyContainer(f.getName(),families,relationships,members,locations));
} }
return list; return list;
} }
@ -74,7 +77,7 @@ public class Endpoints {
public FamilyContainer _2(@PathVariable Long id) { public FamilyContainer _2(@PathVariable Long id) {
if (families.existsById(id)) { if (families.existsById(id)) {
Family f = families.findById(id).get(); Family f = families.findById(id).get();
return new FamilyContainer(f.getName(),families,relationships,members); return new FamilyContainer(f.getName(),families,relationships,members,locations);
} else { } else {
return null; return null;
} }
@ -256,6 +259,16 @@ public class Endpoints {
} }
} }
@GetMapping("/knownlocation")
public Iterable<KnownLocation> _13() {
return knownlocations.findAll();
}
@GetMapping("/location/{memberid}")
public Location _13(@PathVariable Long memberid) {
return locations.findTopByMemberIdOrderByIdDesc(memberid).get(0);
}
@PostMapping("/knownlocation") @PostMapping("/knownlocation")
/** /**
* @RequestBody requires: * @RequestBody requires:
@ -287,7 +300,10 @@ public class Endpoints {
@GetMapping("/notification/{id}") @GetMapping("/notification/{id}")
public List<Notification> _10(@PathVariable Long id) { public List<Notification> _10(@PathVariable Long id) {
return notifications.findByMemberId(id); //return notifications.findByMemberIdOrderByDateDesc(id);
//Page<Example> findByValidIsTrue(Pageable pageable);
//List<Example> result = repository.findByValidIsTrue(new PageRequest(0, N))
return notifications.findByMemberIdOrderByDateDesc(id,PageRequest.of(0,30,Sort.by(Direction.DESC,"date")));
} }
@PostMapping("/notification") @PostMapping("/notification")

@ -1,16 +1,182 @@
package sig.family; package sig.family;
import java.net.URI;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
@SpringBootApplication @SpringBootApplication
@Service
public class FamilyApp { public class FamilyApp {
static HashMap<Long,GPSUser> map = new HashMap<>();
static RestTemplate connection = new RestTemplate();
public final static int WAITMULT=3;
public static Location postMessage(Message message) {
// Construct a URI from a template
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:8080/location")
.buildAndExpand("")
.toUri();
// Create the request object
RequestEntity<?> request = RequestEntity.post(uri)
.body(message);
// Execute the request
ResponseEntity<Location> response = connection.exchange(
request,
Location.class // Declare the _type_ of the response
);
// Get the deserialized response body
return response.getBody();
}
@RequestMapping
static class Message{
Long member;
double x;
double y;
public Long getMember() {
return member;
}
public void setMember(Long member) {
this.member = member;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
}
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(FamilyApp.class, args); SpringApplication.run(FamilyApp.class, args);
Timer t2 = new Timer();
t2.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
for (FamilyMember m : Endpoints.members.findAll()) {
if (!map.containsKey(m.getId())) {
GPSUser g = new GPSUser(m.getId());
if (m.getLastLocationId()!=null) {
g.x = Endpoints.knownlocations.findById(m.getLastLocationId()).get().getX();
g.y = Endpoints.knownlocations.findById(m.getLastLocationId()).get().getY();
}
else {
//Choose a random known location to start at.
Long count = (long)(Math.random()*Endpoints.knownlocations.count());
for (KnownLocation l : Endpoints.knownlocations.findAll()) {
if (count>0) {
count--;
} else {
g.x=l.getX();
g.y=l.getY();
//System.out.println(connection.postForObject("http://localhost:8080", String.class, "6ba5969a",q));
break;
}
}
}
g.waitTime=((int)Math.random()*28)+2;
//Choose a random known location to go to.
Long count = (long)(Math.random()*Endpoints.knownlocations.count());
for (KnownLocation l : Endpoints.knownlocations.findAll()) {
if (count>0) {
count--;
} else {
g.targetX=l.getX();
g.targetY=l.getY();
//System.out.println(connection.postForObject("http://localhost:8080", String.class, "6ba5969a",q));
break;
}
}
g.postLocation();
map.put(m.getId(), g);
}
}
for (Long id : map.keySet()) {
GPSUser u = map.get(id);
if (u.waitTime>0) {
u.waitTime--;
} else {
if (u.targetX!=u.x || u.targetY!=u.y) {
switch ((int)(Math.random()*6)) {
case 0:{
//Sometimes go a random direction.
switch ((int)(Math.random()*4)) {
case 0:{
u.x+=0.001;
}break;
case 1:{
u.x-=0.001;
}break;
case 2:{
u.y+=0.001;
}break;
case 3:{
u.y-=0.001;
}break;
}
}break;
default:{
//Move towards.
if (Math.random()<0.5) {
u.x+=Math.signum(u.targetX-u.x)*0.001;
}
if (Math.random()<0.5) {
u.y+=Math.signum(u.targetY-u.y)*0.001;
}
}
}
u.waitTime=((int)Math.random()*6*WAITMULT)+2*WAITMULT;
} else {
//Select a new target.
//Choose a random known location to go to.
Long count = (long)(Math.random()*Endpoints.knownlocations.count());
for (KnownLocation l : Endpoints.knownlocations.findAll()) {
if (count>0) {
count--;
} else {
u.targetX=l.getX();
u.targetY=l.getY();
//System.out.println(connection.postForObject("http://localhost:8080", String.class, "6ba5969a",q));
break;
}
}
u.waitTime=((int)Math.random()*28*WAITMULT)+30*WAITMULT;
}
u.postLocation();
}
}
}
}, 0l, 1000l);
} }
} }

@ -26,13 +26,14 @@ public class FamilyContainer extends Family{
FamilyContainer(String name FamilyContainer(String name
,FamilyRepository families ,FamilyRepository families
,FamilyRelationshipRepository relationships ,FamilyRelationshipRepository relationships
,FamilyMemberRepository members) { ,FamilyMemberRepository members
,LocationRepository locations) {
super(name); super(name);
id = families.findByName(name).get(0).getId(); id = families.findByName(name).get(0).getId();
List<FamilyRelationship> relations = relationships.findByFamilyId(id); List<FamilyRelationship> relations = relationships.findByFamilyId(id);
for (FamilyRelationship r : relations) { for (FamilyRelationship r : relations) {
FamilyMember m = members.findById(r.getMemberId()).get(); FamilyMember m = members.findById(r.getMemberId()).get();
this.members.add(new FamilyMemberContainer(m,relationships)); this.members.add(new FamilyMemberContainer(m,relationships,locations));
} }
} }

@ -1,5 +1,7 @@
package sig.family; package sig.family;
import java.util.List;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType; import javax.persistence.GenerationType;
@ -10,6 +12,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import sig.family.FamilyApp.Message;
@RequestMapping @RequestMapping
public class FamilyMemberContainer extends FamilyMember{ public class FamilyMemberContainer extends FamilyMember{
Long id; Long id;
@ -22,15 +26,39 @@ public class FamilyMemberContainer extends FamilyMember{
String relationship; String relationship;
double x,y;
FamilyMemberContainer(){} FamilyMemberContainer(){}
public FamilyMemberContainer(FamilyMember m, public FamilyMemberContainer(FamilyMember m,
FamilyRelationshipRepository relationships) { FamilyRelationshipRepository relationships,
LocationRepository locations) {
this.firstName = m.firstName; this.firstName = m.firstName;
this.lastName = m.lastName; this.lastName = m.lastName;
this.mobileDeviceId = m.mobileDeviceId; this.mobileDeviceId = m.mobileDeviceId;
this.id=m.id; this.id=m.id;
relationship = relationships.findByMemberId(m.getId()).get(0).getRelationship(); relationship = relationships.findByMemberId(m.getId()).get(0).getRelationship();
List<Location> locs = locations.findTopByMemberIdOrderByIdDesc(m.getId());
if (locs.size()>0) {
Location l = locs.get(0);
this.x=l.getX();
this.y=l.getY();
} else {
//Use their assigned location.
if (this.getLastLocationId()==null) {
this.x=-110.253;
this.y=31.554;
Message mm = new Message();
mm.member=m.getId();
mm.setX(this.x);
mm.setY(this.y);
FamilyApp.postMessage(mm);
} else {
Location l = locations.findById(this.getLastLocationId()).get();
this.x=l.getX();
this.y=l.getY();
}
}
} }
public Long getId() { public Long getId() {
@ -73,4 +101,20 @@ public class FamilyMemberContainer extends FamilyMember{
public void setRelationship(String relationship) { public void setRelationship(String relationship) {
this.relationship = relationship; this.relationship = relationship;
} }
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
} }

@ -0,0 +1,24 @@
package sig.family;
import sig.family.FamilyApp.Message;
public class GPSUser {
Long id;
double x,y;
int waitTime=0;
double targetX,targetY;
GPSUser(Long id) {
this.id=id;
}
public Location postLocation() {
Message mm = new Message();
mm.member=id;
mm.x=x;
mm.y=y;
Location res = FamilyApp.postMessage(mm);
System.out.println(res);
return res;
}
}

@ -1,5 +1,6 @@
package sig.family; package sig.family;
import java.lang.reflect.Field;
import java.util.Date; import java.util.Date;
import javax.persistence.Column; import javax.persistence.Column;
@ -70,4 +71,22 @@ public class Location {
this.date = date; this.date = date;
} }
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName()+"(");
boolean first=true;
for (Field f : this.getClass().getDeclaredFields()) {
if (!first) {
sb.append(",");
}
try {
sb.append(f.getName()+"="+f.get(this));
first=false;
} catch (IllegalArgumentException|IllegalAccessException e) {
e.printStackTrace();
}
}
sb.append(")");
return sb.toString();
}
} }

@ -1,7 +1,9 @@
package sig.family; package sig.family;
import java.util.List;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
public interface LocationRepository extends CrudRepository<Location,Long>{ public interface LocationRepository extends CrudRepository<Location,Long>{
List<Location> findTopByMemberIdOrderByIdDesc(Long memberId);
} }

@ -2,8 +2,11 @@ package sig.family;
import java.util.List; import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
public interface NotificationRepository extends CrudRepository<Notification,Long>{ public interface NotificationRepository extends CrudRepository<Notification,Long>{
List<Notification> findByMemberId(Long memberid); List<Notification> findByMemberIdOrderByDateDesc(Long memberid);
List<Notification> findByMemberIdOrderByDateDesc(Long memberid,Pageable p);
List<Notification> OrderByDateDesc();
} }

Loading…
Cancel
Save