[backend/api] Add renote endpoints (ISH-341)

This commit is contained in:
Laura Hausmann 2024-05-23 19:28:06 +02:00
parent 835796ac86
commit 167fd5f0d6
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
4 changed files with 78 additions and 15 deletions

View file

@ -294,7 +294,6 @@ public class StatusController(
var note = await db.Notes.Where(p => p.Id == id)
.IncludeCommonProperties()
.EnsureVisibleFor(user)
.FilterHidden(user, db, filterMutes: false)
.FirstOrDefaultAsync() ??
throw GracefulException.RecordNotFound();
@ -302,10 +301,8 @@ public class StatusController(
? StatusEntity.DecodeVisibility(request.Visibility)
: user.UserSettings?.DefaultRenoteVisibility ?? Note.NoteVisibility.Public;
if (renoteVisibility == Note.NoteVisibility.Specified)
throw GracefulException.BadRequest("Renote visibility must be one of: public, unlisted, private");
renote = await noteSvc.CreateNoteAsync(user, renoteVisibility, renote: note);
renote = await noteSvc.RenoteNoteAsync(note, user, renoteVisibility) ??
throw new Exception("Created renote was null");
note.RenoteCount++; // we do not want to call save changes after this point
}
@ -319,17 +316,14 @@ public class StatusController(
public async Task<IActionResult> UndoRenote(string id)
{
var user = HttpContext.GetUserOrFail();
if (!await db.Notes.Where(p => p.Id == id).EnsureVisibleFor(user).AnyAsync())
throw GracefulException.RecordNotFound();
var renotes = await db.Notes.Where(p => p.RenoteId == id && p.IsPureRenote && p.User == user)
.IncludeCommonProperties()
.ToListAsync();
foreach (var renote in renotes) await noteSvc.DeleteNoteAsync(renote);
renotes[0].Renote!.RenoteCount--; // we do not want to call save changes after this point
var note = await db.Notes.Where(p => p.Id == id)
.IncludeCommonProperties()
.EnsureVisibleFor(user)
.FirstOrDefaultAsync() ??
throw GracefulException.RecordNotFound();
var count = await noteSvc.UnrenoteNoteAsync(note, user);
note.RenoteCount -= (short)count; // we do not want to call save changes after this point
return await GetNote(id);
}

View file

@ -170,6 +170,40 @@ public class NoteController(
return Ok(new ValueResponse(success ? --note.LikeCount : note.LikeCount));
}
[HttpPost("{id}/renote")]
[Authenticate]
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ValueResponse))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ErrorResponse))]
public async Task<IActionResult> RenoteNote(string id, [FromQuery] NoteVisibility? visibility = null)
{
var user = HttpContext.GetUserOrFail();
var note = await db.Notes.Where(p => p.Id == id)
.EnsureVisibleFor(user)
.FirstOrDefaultAsync() ??
throw GracefulException.NotFound("Note not found");
var success = await noteSvc.RenoteNoteAsync(note, user, (Note.NoteVisibility?)visibility);
return Ok(new ValueResponse(success != null ? ++note.RenoteCount : note.RenoteCount));
}
[HttpPost("{id}/unrenote")]
[Authenticate]
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(ValueResponse))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ErrorResponse))]
public async Task<IActionResult> UnrenoteNote(string id)
{
var user = HttpContext.GetUserOrFail();
var note = await db.Notes.Where(p => p.Id == id)
.EnsureVisibleFor(user)
.FirstOrDefaultAsync() ??
throw GracefulException.NotFound("Note not found");
var count = await noteSvc.UnrenoteNoteAsync(note, user);
return Ok(new ValueResponse(note.RenoteCount - count));
}
[HttpPost("{id}/react/{name}")]
[Authenticate]
[Authorize]

View file

@ -1105,6 +1105,31 @@ public class NoteService(
return true;
}
public async Task<Note?> RenoteNoteAsync(Note note, User user, Note.NoteVisibility? visibility = null)
{
visibility ??= user.UserSettings?.DefaultRenoteVisibility ?? Note.NoteVisibility.Public;
if (visibility == Note.NoteVisibility.Specified)
throw GracefulException.BadRequest("Renote visibility must be one of: public, unlisted, private");
if (note.IsPureRenote)
throw GracefulException.BadRequest("Cannot renote a pure renote");
if (!await db.Notes.AnyAsync(p => p.Renote == note && p.IsPureRenote && p.User == user))
return await CreateNoteAsync(user, visibility.Value, renote: note);
return null;
}
public async Task<int> UnrenoteNoteAsync(Note note, User user)
{
var renotes = await db.Notes.Where(p => p.Renote == note && p.IsPureRenote && p.User == user).ToListAsync();
if (renotes.Count == 0) return 0;
foreach (var renote in renotes)
await DeleteNoteAsync(renote);
return renotes.Count;
}
public async Task LikeNoteAsync(ASNote note, User actor)
{
var dbNote = await ResolveNoteAsync(note) ?? throw new Exception("Cannot register like for unknown note");

View file

@ -34,6 +34,16 @@ internal class NoteControllerModel(ApiClient api)
public Task<ValueResponse?> UnlikeNote(string id) =>
api.CallNullable<ValueResponse>(HttpMethod.Post, $"/notes/{id}/unlike");
public Task<ValueResponse?> RenoteNote(string id, NoteVisibility? visibility = null)
{
var query = new QueryString();
if (visibility.HasValue) query.Add("visibility", ((int)visibility.Value).ToString().ToLowerInvariant());
return api.CallNullable<ValueResponse>(HttpMethod.Post, $"/notes/{id}/renote", query);
}
public Task<ValueResponse?> UnrenoteNote(string id) =>
api.CallNullable<ValueResponse>(HttpMethod.Post, $"/notes/{id}/unrenote");
public Task<ValueResponse?> ReactToNote(string id, string name) =>
api.CallNullable<ValueResponse>(HttpMethod.Post, $"/notes/{id}/react/{name}");