Skip to content

Commit

Permalink
feat: pastebin improvements (#71)
Browse files Browse the repository at this point in the history
* fix: fix being unable to click the Save button after submitting a previous paste

* style: change pastebin preview theme

* feat(pastebin): allow users to choose expiration time

* feat(pastebin): add more language support

* feat(pastebin): improve result box

* style(pastebin): move encrypt option to a new line

* style: update header logo

* feat(pastebin): add title to paste

* style(pastebin): show dashboard button when user is logged in
  • Loading branch information
neumanf authored Sep 29, 2024
1 parent 4150d67 commit 22caf4a
Show file tree
Hide file tree
Showing 24 changed files with 261 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.mally.api.pastebin.entities.PasteSyntax;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -11,6 +12,8 @@
@AllArgsConstructor
@NoArgsConstructor
public class CreatePasteDTO {
private String title;

@NotBlank
private String text;

Expand All @@ -19,4 +22,7 @@ public class CreatePasteDTO {

@NotNull
private boolean encrypted;

@Pattern(regexp = "1h|6h|1d|3d|7d|1m", message = "Expiration time is not valid")
private String expiration;
}
30 changes: 30 additions & 0 deletions apps/api/src/main/java/com/mally/api/pastebin/entities/Paste.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class Paste {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column
@ColumnDefault("Untitled")
@NotNull
private String title;

@Column(unique = true)
@NotNull
private String slug;
Expand Down Expand Up @@ -51,5 +56,30 @@ public class Paste {
@Column(name = "expires_at")
@NotNull
private ZonedDateTime expiresAt;

public static class PasteBuilder {
private ZonedDateTime expiresAt;

public PasteBuilder expiresAt(String expirationTime) {
ZonedDateTime now = ZonedDateTime.now();

switch (expirationTime) {
case "6h": { expiresAt = now.plusHours(6); break; }
case "1d": { expiresAt = now.plusDays(1); break; }
case "3d": { expiresAt = now.plusDays(3); break; }
case "7d": { expiresAt = now.plusDays(7); break; }
case "1m": { expiresAt = now.plusMonths(1); break; }
case "1h":
default: expiresAt = now.plusHours(1);
}

return this;
}

public PasteBuilder expiresAt(ZonedDateTime expirationTime) {
this.expiresAt = expirationTime;
return this;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,14 @@ public enum PasteSyntax {
PYTHON,
PHP,
SHELL,
RUBY
RUBY,
HTML,
MD,
SQL,
VBSCRIPT,
SWIFT,
DIFF,
CLOJURE,
HASKELL,
OCAML
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
package com.mally.api.pastebin.services;

import com.mally.api.pastebin.dtos.CreatePasteDTO;
import com.mally.api.pastebin.dtos.SearchPastesDTO;
import com.mally.api.pastebin.entities.Paste;
import com.mally.api.pastebin.repositories.PastebinRepository;
import com.mally.api.shared.utils.PaginationUtils;
import io.github.thibaultmeyer.cuid.CUID;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@AllArgsConstructor
@Service
public class PastebinService {
private static final Integer EXPIRES_IN_DAYS = 7;

private final PastebinRepository pastebinRepository;

private final EntityManager entityManager;
Expand All @@ -40,13 +31,14 @@ public Paste create(CreatePasteDTO dto, String userId) {
final CUID slug = CUID.randomCUID2(22);

Paste paste = Paste.builder()
.title(dto.getTitle())
.text(dto.getText())
.syntax(dto.getSyntax())
.slug(slug.toString())
.encrypted(dto.isEncrypted())
.userId(userId)
.createdAt(now)
.expiresAt(now.plusDays(EXPIRES_IN_DAYS))
.expiresAt(dto.getExpiration())
.build();

return pastebinRepository.save(paste);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ databaseChangeLog:
file: 'db/changelog/scripts/v3-add-encrypted-field-to-paste.yaml'
- include:
file: 'db/changelog/scripts/v4-add-user-id-to-url-and-paste.yaml'
- include:
file: 'db/changelog/scripts/v5-add-title-to-paste.yaml'
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
databaseChangeLog:
- changeSet:
id: 5-1
author: Neuman F.
changes:
- addColumn:
tableName: paste
columns:
- column:
name: title
type: VARCHAR
constraints:
nullable: false
defaultValue: Untitled
9 changes: 9 additions & 0 deletions apps/ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ import { PrimeNGConfig } from 'primeng/api';
php: () => import('highlight.js/lib/languages/php'),
shell: () => import('highlight.js/lib/languages/shell'),
ruby: () => import('highlight.js/lib/languages/ruby'),
html: () => import('highlight.js/lib/languages/vbscript-html'),
md: () => import('highlight.js/lib/languages/markdown'),
sql: () => import('highlight.js/lib/languages/sql'),
vbscript: () => import('highlight.js/lib/languages/vbscript'),
swift: () => import('highlight.js/lib/languages/swift'),
diff: () => import('highlight.js/lib/languages/diff'),
clojure: () => import('highlight.js/lib/languages/clojure'),
haskell: () => import('highlight.js/lib/languages/haskell'),
ocaml: () => import('highlight.js/lib/languages/ocaml'),
},
}),
{
Expand Down
4 changes: 4 additions & 0 deletions apps/ui/src/app/auth/services/keycloak.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export class KeycloakService {
}
}

isAuthenticated() {
return !!this.user;
}

login() {
return this.keycloak?.login({
redirectUri: environment.clientUrl + '/dashboard',
Expand Down
8 changes: 7 additions & 1 deletion apps/ui/src/app/dashboard/pages/pastes/pastes.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
<th style="width: 4rem">
<p-tableHeaderCheckbox />
</th>
<th style="min-width: 8rem" pSortableColumn="slug">
Title <p-sortIcon field="title" />
</th>
<th style="min-width: 8rem" pSortableColumn="slug">
URL <p-sortIcon field="slug" />
</th>
Expand All @@ -66,6 +69,9 @@
<td>
<p-tableCheckbox [value]="paste" />
</td>
<td class="truncate max-w-96">
<code>{{ paste.title }}</code>
</td>
<td>
<a
class="text-red-400"
Expand All @@ -85,7 +91,7 @@
{{ paste.createdAt | date: 'short' }}
</td>
<td>
<span [pTooltip]="pasteExpiresIn(paste.expiresAt, 'long')" tooltipPosition="top">{{ pasteExpiresIn(paste.expiresAt, 'humanized') }}</span>
<span [pTooltip]="DateUtils.expiresIn(paste.expiresAt, 'long')" tooltipPosition="top">{{ DateUtils.expiresIn(paste.expiresAt, 'humanized') }}</span>
</td>
<td>
<p-menu #options [model]="optionsItems" [popup]="true" appendTo="body" />
Expand Down
17 changes: 3 additions & 14 deletions apps/ui/src/app/dashboard/pages/pastes/pastes.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
MenuItem,
MenuItemCommandEvent,
} from 'primeng/api';
import dayjs from 'dayjs';
import { Menu } from 'primeng/menu';
import { ToastService } from '../../../shared/services/toast/toast.service';
import { Page } from '../../../shared/interfaces/http';
Expand All @@ -16,6 +15,7 @@ import {
Paste,
PastebinService,
} from '../../../pastebin/services/pastebin.service';
import { DateUtils } from '../../../shared/utils/date';

@Component({
selector: 'app-pastes',
Expand Down Expand Up @@ -101,19 +101,6 @@ export class PastesComponent {
});
}

pasteExpiresIn(expiresAt: string, format: 'humanized' | 'long') {
if (format === 'humanized') {
const expiration = dayjs(expiresAt);
if (expiration < dayjs()) {
return 'Expired';
} else {
return expiration.fromNow();
}
} else {
return dayjs(expiresAt).format('MMMM D, YYYY h:mm A');
}
}

delete(id: number) {
this.pastebinService.delete(id).subscribe({
next: () => {
Expand Down Expand Up @@ -166,4 +153,6 @@ export class PastesComponent {
});
menu.toggle(event);
}

protected readonly DateUtils = DateUtils;
}
2 changes: 1 addition & 1 deletion apps/ui/src/app/dashboard/pages/urls/urls.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
{{ url.createdAt | date: 'short' }}
</td>
<td>
<span [pTooltip]="urlExpiresIn(url.expiresAt, 'long')" tooltipPosition="top">{{ urlExpiresIn(url.expiresAt, 'humanized') }}</span>
<span [pTooltip]="DateUtils.expiresIn(url.expiresAt, 'long')" tooltipPosition="top">{{ DateUtils.expiresIn(url.expiresAt, 'humanized') }}</span>
</td>
<td>
<p-menu #options [model]="optionsItems" [popup]="true" appendTo="body" />
Expand Down
19 changes: 4 additions & 15 deletions apps/ui/src/app/dashboard/pages/urls/urls.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { Component, TemplateRef, ViewChild } from '@angular/core';
import {
PaginationParams,
UrlShortenerService,
Expand All @@ -10,11 +10,11 @@ import {
MenuItem,
MenuItemCommandEvent,
} from 'primeng/api';
import dayjs from 'dayjs';
import { Menu } from 'primeng/menu';
import { ToastService } from '../../../shared/services/toast/toast.service';
import { Page } from '../../../shared/interfaces/http';
import { TableLazyLoadEvent } from 'primeng/table';
import { DateUtils } from '../../../shared/utils/date';

@Component({
selector: 'app-urls',
Expand Down Expand Up @@ -100,19 +100,6 @@ export class UrlsComponent {
});
}

urlExpiresIn(expiresAt: string, format: 'humanized' | 'long') {
if (format === 'humanized') {
const expiration = dayjs(expiresAt);
if (expiration < dayjs()) {
return 'Expired';
} else {
return expiration.fromNow();
}
} else {
return dayjs(expiresAt).format('MMMM D, YYYY h:mm A');
}
}

delete(id: number) {
this.urlShortenerService.delete(id).subscribe({
next: () => {
Expand Down Expand Up @@ -163,4 +150,6 @@ export class UrlsComponent {
});
menu.toggle(event);
}

protected readonly DateUtils = DateUtils;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<img
ngSrc="assets/logo.svg"
loading="lazy"
alt="Photo by Fakurian Design"
alt="Logo"
width="300"
height="400"
/>
Expand Down
36 changes: 36 additions & 0 deletions apps/ui/src/app/pastebin/pages/index/constants/syntaxes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,40 @@ export const SYNTAXES = [
label: 'Ruby',
value: 'RUBY',
},
{
label: 'HTML',
value: 'HTML',
},
{
label: 'Markdown',
value: 'MD',
},
{
label: 'SQL',
value: 'SQL',
},
{
label: 'VBScript',
value: 'VBSCRIPT',
},
{
label: 'Swift',
value: 'SWIFT',
},
{
label: 'Diff',
value: 'DIFF',
},
{
label: 'Clojure',
value: 'CLOJURE',
},
{
label: 'Haskell',
value: 'HASKELL',
},
{
label: 'OCaml',
value: 'OCAML',
},
];
Loading

0 comments on commit 22caf4a

Please sign in to comment.