Skip to content

Commit

Permalink
Add distant chat backlogs and a way to open distant chats
Browse files Browse the repository at this point in the history
  • Loading branch information
zapek committed Feb 26, 2025
1 parent 804808e commit 1a47f5d
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 by David Gerber - https://zapek.com
* Copyright (c) 2019-2025 by David Gerber - https://zapek.com
*
* This file is part of Xeres.
*
Expand All @@ -25,16 +25,19 @@
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.xeres.app.service.IdentityService;
import io.xeres.app.service.LocationService;
import io.xeres.app.xrs.service.chat.ChatBacklogService;
import io.xeres.app.xrs.service.chat.ChatRsService;
import io.xeres.app.xrs.service.chat.RoomFlags;
import io.xeres.common.dto.chat.ChatBacklogDTO;
import io.xeres.common.dto.chat.ChatRoomBacklogDTO;
import io.xeres.common.dto.chat.ChatRoomContextDTO;
import io.xeres.common.dto.location.LocationDTO;
import io.xeres.common.id.LocationIdentifier;
import io.xeres.common.rest.chat.ChatRoomVisibility;
import io.xeres.common.rest.chat.CreateChatRoomRequest;
import io.xeres.common.rest.chat.DistantChatRequest;
import io.xeres.common.rest.chat.InviteToChatRoomRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
Expand All @@ -54,7 +57,11 @@
import java.util.List;
import java.util.stream.Collectors;

import static io.xeres.app.database.model.chat.ChatMapper.*;
import static io.xeres.app.database.model.chat.ChatMapper.fromDistantChatBacklogToChatBacklogDTOs;
import static io.xeres.app.database.model.chat.ChatMapper.toChatBacklogDTOs;
import static io.xeres.app.database.model.chat.ChatMapper.toChatRoomBacklogDTOs;
import static io.xeres.app.database.model.chat.ChatMapper.toDTO;
import static io.xeres.app.database.model.location.LocationMapper.toDTO;
import static io.xeres.common.rest.PathConfig.CHAT_PATH;

@Tag(name = "Chat", description = "Chat service", externalDocs = @ExternalDocumentation(url = "https://xeres.io/docs/api/chat", description = "Chat documentation"))
Expand All @@ -70,12 +77,14 @@ public class ChatController
private final ChatRsService chatRsService;
private final ChatBacklogService chatBacklogService;
private final LocationService locationService;
private final IdentityService identityService;

public ChatController(ChatRsService chatRsService, ChatBacklogService chatBacklogService, LocationService locationService)
public ChatController(ChatRsService chatRsService, ChatBacklogService chatBacklogService, LocationService locationService, IdentityService identityService)
{
this.chatRsService = chatRsService;
this.chatBacklogService = chatBacklogService;
this.locationService = locationService;
this.identityService = identityService;
}

@PostMapping("/rooms")
Expand Down Expand Up @@ -151,4 +160,27 @@ public List<ChatBacklogDTO> getChatMessages(@PathVariable long locationId,
from != null ? from.toInstant(ZoneOffset.UTC) : Instant.now().minus(PRIVATE_CHAT_DEFAULT_DURATION),
maxLines != null ? maxLines : PRIVATE_CHAT_DEFAULT_MAX_LINES));
}

@PostMapping("/distant-chats")
@Operation(summary = "Create a distant chat")
@ApiResponse(responseCode = "200", description = "Request successful")
public LocationDTO createDistantChat(@Valid @RequestBody DistantChatRequest distantChatRequest)
{
var identity = identityService.findById(distantChatRequest.identityId()).orElseThrow();
return toDTO(chatRsService.createDistantChat(identity));
}

@GetMapping("/distant-chats/{gxsId}/messages")
@Operation(summary = "Get the distant chat messages backlog")
@ApiResponse(responseCode = "200", description = "Request successful")
public List<ChatBacklogDTO> getDistantChatMessages(@PathVariable long gxsId,
@RequestParam(value = "maxLines", required = false) @Min(1) @Max(500) Integer maxLines,
@RequestParam(value = "from", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime from)
{
var identity = identityService.findById(gxsId).orElseThrow();
return fromDistantChatBacklogToChatBacklogDTOs(chatBacklogService.getDistantMessages(
identity,
from != null ? from.toInstant(ZoneOffset.UTC) : Instant.now().minus(PRIVATE_CHAT_DEFAULT_DURATION),
maxLines != null ? maxLines : PRIVATE_CHAT_DEFAULT_MAX_LINES));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 by David Gerber - https://zapek.com
* Copyright (c) 2024-2025 by David Gerber - https://zapek.com
*
* This file is part of Xeres.
*
Expand Down Expand Up @@ -80,9 +80,9 @@ public Instant getCreated()
return created;
}

public void setCreated(Instant timestamp)
public void setCreated(Instant created)
{
this.created = timestamp;
this.created = created;
}

public boolean isOwn()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2023 by David Gerber - https://zapek.com
* Copyright (c) 2019-2025 by David Gerber - https://zapek.com
*
* This file is part of Xeres.
*
Expand Down Expand Up @@ -97,4 +97,20 @@ public static ChatBacklogDTO toDTO(ChatBacklog chatBacklog)
chatBacklog.getMessage()
);
}

public static List<ChatBacklogDTO> fromDistantChatBacklogToChatBacklogDTOs(List<DistantChatBacklog> distantChatBacklogList)
{
return emptyIfNull(distantChatBacklogList).stream()
.map(ChatMapper::toDTO)
.toList();
}

public static ChatBacklogDTO toDTO(DistantChatBacklog distantChatBacklog)
{
return new ChatBacklogDTO(
distantChatBacklog.getCreated(),
distantChatBacklog.isOwn(),
distantChatBacklog.getMessage()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2025 by David Gerber - https://zapek.com
*
* This file is part of Xeres.
*
* Xeres is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Xeres is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xeres. If not, see <http://www.gnu.org/licenses/>.
*/

package io.xeres.app.database.model.chat;

import io.xeres.app.xrs.service.identity.item.IdentityGroupItem;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;

import java.time.Instant;

@Entity
public class DistantChatBacklog
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "identity_id", nullable = false)
private IdentityGroupItem identityGroupItem;

@CreationTimestamp
private Instant created;

private boolean own;

private String message;

protected DistantChatBacklog()
{

}

public DistantChatBacklog(IdentityGroupItem identityGroupItem, boolean own, String message)
{
this.identityGroupItem = identityGroupItem;
this.own = own;
this.message = message;
}

public long getId()
{
return id;
}

public void setId(long id)
{
this.id = id;
}

public IdentityGroupItem getIdentityGroupItem()
{
return identityGroupItem;
}

public void setIdentityGroupItem(IdentityGroupItem identityGroupItem)
{
this.identityGroupItem = identityGroupItem;
}

public Instant getCreated()
{
return created;
}

public void setCreated(Instant created)
{
this.created = created;
}

public boolean isOwn()
{
return own;
}

public void setOwn(boolean own)
{
this.own = own;
}

public String getMessage()
{
return message;
}

public void setMessage(String message)
{
this.message = message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2025 by David Gerber - https://zapek.com
*
* This file is part of Xeres.
*
* Xeres is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Xeres is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Xeres. If not, see <http://www.gnu.org/licenses/>.
*/

package io.xeres.app.database.repository;

import io.xeres.app.database.model.chat.DistantChatBacklog;
import io.xeres.app.xrs.service.identity.item.IdentityGroupItem;
import org.springframework.data.domain.Limit;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.List;

@Transactional(readOnly = true)
public interface DistantChatBacklogRepository extends JpaRepository<DistantChatBacklog, Long>
{
List<DistantChatBacklog> findAllByIdentityGroupItemAndCreatedAfterOrderByCreatedDesc(IdentityGroupItem identityGroupItem, Instant from, Limit limit);

void deleteAllByCreatedBefore(Instant from);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024 by David Gerber - https://zapek.com
* Copyright (c) 2024-2025 by David Gerber - https://zapek.com
*
* This file is part of Xeres.
*
Expand All @@ -21,11 +21,10 @@

import io.xeres.app.database.model.chat.ChatBacklog;
import io.xeres.app.database.model.chat.ChatRoomBacklog;
import io.xeres.app.database.model.chat.DistantChatBacklog;
import io.xeres.app.database.model.location.Location;
import io.xeres.app.database.repository.ChatBacklogRepository;
import io.xeres.app.database.repository.ChatRoomBacklogRepository;
import io.xeres.app.database.repository.ChatRoomRepository;
import io.xeres.app.database.repository.LocationRepository;
import io.xeres.app.database.repository.*;
import io.xeres.app.xrs.service.identity.item.IdentityGroupItem;
import io.xeres.common.id.GxsId;
import io.xeres.common.id.LocationIdentifier;
import org.springframework.data.domain.Limit;
Expand All @@ -43,15 +42,19 @@ public class ChatBacklogService

private final ChatBacklogRepository chatBacklogRepository;
private final ChatRoomBacklogRepository chatRoomBacklogRepository;
private final DistantChatBacklogRepository distantChatBacklogRepository;
private final LocationRepository locationRepository;
private final ChatRoomRepository chatRoomRepository;
private final GxsIdentityRepository gxsIdentityRepository;

ChatBacklogService(ChatBacklogRepository chatBacklogRepository, ChatRoomBacklogRepository chatRoomBacklogRepository, LocationRepository locationRepository, ChatRoomRepository chatRoomRepository)
ChatBacklogService(ChatBacklogRepository chatBacklogRepository, ChatRoomBacklogRepository chatRoomBacklogRepository, DistantChatBacklogRepository distantChatBacklogRepository, LocationRepository locationRepository, ChatRoomRepository chatRoomRepository, GxsIdentityRepository gxsIdentityRepository)
{
this.chatBacklogRepository = chatBacklogRepository;
this.chatRoomBacklogRepository = chatRoomBacklogRepository;
this.distantChatBacklogRepository = distantChatBacklogRepository;
this.locationRepository = locationRepository;
this.chatRoomRepository = chatRoomRepository;
this.gxsIdentityRepository = gxsIdentityRepository;
}

@Transactional
Expand Down Expand Up @@ -94,14 +97,23 @@ public List<ChatBacklog> getMessages(Location with, Instant from, int maxLines)
return chatBacklogRepository.findAllByLocationAndCreatedAfterOrderByCreatedDesc(with, from, Limit.of(maxLines)).reversed();
}

@Transactional
public void storeIncomingDistantMessage(GxsId from, String message)
{

var gxsId = gxsIdentityRepository.findByGxsId(from).orElseThrow();
distantChatBacklogRepository.save(new DistantChatBacklog(gxsId, false, message));
}

@Transactional
public void storeOutgoingDistantMessage(GxsId to, String message)
{
var gxsId = gxsIdentityRepository.findByGxsId(to).orElseThrow();
distantChatBacklogRepository.save(new DistantChatBacklog(gxsId, true, message));
}

public List<DistantChatBacklog> getDistantMessages(IdentityGroupItem with, Instant from, int maxLines)
{
return distantChatBacklogRepository.findAllByIdentityGroupItemAndCreatedAfterOrderByCreatedDesc(with, from, Limit.of(maxLines)).reversed();
}

@Transactional
Expand Down
Loading

0 comments on commit 1a47f5d

Please sign in to comment.