Skip to content

Commit 542b4d7

Browse files
authored
Merge pull request #40 from rhuanbarros/master
Example code update with CustomSupabaseSessionHandler and new page example with RLS
2 parents 1be2eb0 + ec53520 commit 542b4d7

File tree

13 files changed

+422
-65
lines changed

13 files changed

+422
-65
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Launch and Debug Standalone Blazor WebAssembly App",
6+
"type": "blazorwasm",
7+
"request": "launch",
8+
"cwd": "${workspaceFolder}"
9+
}
10+
]
11+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "build",
6+
"command": "dotnet",
7+
"type": "process",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}/BlazorWebAssemblySupabaseTemplate.csproj",
11+
"/property:GenerateFullPaths=true",
12+
"/consoleloggerparameters:NoSummary"
13+
],
14+
"problemMatcher": "$msCompile"
15+
},
16+
{
17+
"label": "publish",
18+
"command": "dotnet",
19+
"type": "process",
20+
"args": [
21+
"publish",
22+
"${workspaceFolder}/BlazorWebAssemblySupabaseTemplate.csproj",
23+
"/property:GenerateFullPaths=true",
24+
"/consoleloggerparameters:NoSummary"
25+
],
26+
"problemMatcher": "$msCompile"
27+
},
28+
{
29+
"label": "watch",
30+
"command": "dotnet",
31+
"type": "process",
32+
"args": [
33+
"watch",
34+
"run",
35+
"--project",
36+
"${workspaceFolder}/BlazorWebAssemblySupabaseTemplate.csproj"
37+
],
38+
"problemMatcher": "$msCompile"
39+
}
40+
]
41+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using Postgrest.Attributes;
3+
using Postgrest.Models;
4+
using Supabase;
5+
6+
namespace BlazorWebAssemblySupabaseTemplate.Dtos;
7+
8+
[Table("TodoPrivate")]
9+
public class TodoPrivate : BaseModel
10+
{
11+
[PrimaryKey("id", false)] // Key is Autogenerated
12+
public int Id { get; set; }
13+
14+
[Column("title")]
15+
public string? Title { get; set; }
16+
17+
[Column("user_id")]
18+
public string User_id { get; set; }
19+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
@page "/crud-private"
2+
@using Dtos
3+
@using Blazored.LocalStorage
4+
5+
@inject DatabaseService DatabaseService
6+
@inject NavigationManager NavigationManager
7+
@inject ISnackbar Snackbar
8+
@inject ILocalStorageService localStorage
9+
10+
<div class="pa-16">
11+
12+
<MudText Typo="Typo.h5">Todos private by RLS</MudText>
13+
<br>
14+
<MudForm @ref="form" @bind-IsValid="@success" @bind-Errors="@errors" ValidationDelay="0">
15+
<MudCard>
16+
<MudCardHeader>
17+
<CardHeaderContent>
18+
<h4>New item</h4>
19+
</CardHeaderContent>
20+
</MudCardHeader>
21+
<MudCardContent>
22+
<MudTextField Label="Title" @bind-Value="model.Title" For="@(() => model.Title)" Required="true"
23+
Immediate="true" />
24+
</MudCardContent>
25+
<MudCardActions>
26+
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!success)" Class="ml-auto"
27+
OnClick="OnClickSave">
28+
@if (_processingNewItem)
29+
{
30+
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true" />
31+
<MudText Class="ms-2">Processing</MudText>
32+
}
33+
else
34+
{
35+
<MudText>Save</MudText>
36+
}
37+
</MudButton>
38+
</MudCardActions>
39+
40+
</MudCard>
41+
</MudForm>
42+
43+
<br>
44+
45+
@if (_todoListFiltered == null)
46+
{
47+
<MudSimpleTable>
48+
<thead>
49+
<tr>
50+
<th>Title</th>
51+
<th>Action</th>
52+
</tr>
53+
</thead>
54+
<tbody>
55+
<tr>
56+
<td><MudSkeleton /></td>
57+
<td><MudSkeleton /></td>
58+
</tr>
59+
</tbody>
60+
</MudSimpleTable>
61+
}
62+
else if (_todoListFiltered.Count == 0)
63+
{
64+
<MudSimpleTable>
65+
<thead>
66+
<tr>
67+
<th>Title</th>
68+
<th>Action</th>
69+
</tr>
70+
</thead>
71+
<tbody>
72+
<br>
73+
<MudText Typo="Typo.body1" Align="Align.Center">There is no items in this table.</MudText>
74+
<br>
75+
</tbody>
76+
</MudSimpleTable>
77+
}
78+
else
79+
{
80+
<MudTable Items="@_todoListFiltered" @ref="table" Elevation="1" Bordered="false" Striped="true" Hover="true"
81+
SortLabel="Sort By" T="TodoPrivate">
82+
<ToolBarContent>
83+
<MudTextField T="string" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search"
84+
IconSize="MudBlazor.Size.Medium" Class="mt-0" Clearable="true"
85+
ValueChanged="@(s=>OnValueChangedSearch(s))" Placeholder="Search" />
86+
</ToolBarContent>
87+
<HeaderContent>
88+
<MudTh>
89+
<MudTableSortLabel SortBy="new Func<TodoPrivate, object>(x=>x.Title)">
90+
Title
91+
</MudTableSortLabel>
92+
<MudTableSortLabel SortBy="new Func<TodoPrivate, object>(x=>x.User_id)">
93+
User_id
94+
</MudTableSortLabel>
95+
</MudTh>
96+
<MudTh>
97+
Action
98+
</MudTh>
99+
</HeaderContent>
100+
<RowTemplate>
101+
<MudTd DataLabel="Title">@context?.Title</MudTd>
102+
<MudTd DataLabel="User_id">@context?.User_id</MudTd>
103+
<MudTd DataLabel="Action">
104+
@* <MudIconButton Icon="@Icons.Material.Filled.Edit" aria-label="edit"
105+
Size="MudBlazor.Size.Small"
106+
OnClick="@( (e) => {NavigationManager.NavigateTo($"/todoitems/{context?.Todo?.Id}");})"
107+
/> *@
108+
<MudIconButton Icon="@Icons.Material.Filled.Delete" aria-label="delete" Size="MudBlazor.Size.Small"
109+
OnClick="@(async (e) => {await OnClickDelete(context);})" />
110+
</MudTd>
111+
</RowTemplate>
112+
<PagerContent>
113+
<MudTablePager PageSizeOptions="new int[]{50, 100}" />
114+
</PagerContent>
115+
</MudTable>
116+
}
117+
118+
</div>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using BlazorWebAssemblySupabaseTemplate.Dtos;
2+
using BlazorWebAssemblySupabaseTemplate.Services;
3+
using MudBlazor;
4+
5+
namespace BlazorWebAssemblySupabaseTemplate.Pages.CrudPrivate;
6+
7+
public partial class CrudPagePrivate
8+
{
9+
protected override async Task OnInitializedAsync()
10+
{
11+
await GetTable();
12+
}
13+
14+
// ---------------- SELECT TABLE
15+
private IReadOnlyList<TodoPrivate>? _todoList { get; set; }
16+
private IReadOnlyList<TodoPrivate>? _todoListFiltered { get; set; }
17+
private MudTable<TodoPrivate>? table;
18+
protected async Task GetTable()
19+
{
20+
// await Task.Delay(10000);
21+
IReadOnlyList<TodoPrivate> todos = await DatabaseService.From<TodoPrivate>();
22+
_todoList = todos;
23+
_todoListFiltered = todos;
24+
await InvokeAsync(StateHasChanged);
25+
}
26+
27+
// ---------------- SEARCH
28+
private void OnValueChangedSearch(string text)
29+
{
30+
_todoListFiltered = _todoList?.Where(row => row.Title.Contains(text)).ToList();
31+
}
32+
33+
// ---------------- DELETE
34+
private async Task OnClickDelete(TodoPrivate item)
35+
{
36+
await DatabaseService.Delete<TodoPrivate>(item);
37+
await GetTable();
38+
}
39+
40+
// ---------------- CREATE NEW
41+
42+
protected TodoPrivate model = new();
43+
private bool success = false;
44+
string[] errors = { };
45+
MudForm? form;
46+
private bool _processingNewItem = false;
47+
private async Task OnClickSave()
48+
{
49+
string user_id = await localStorage.GetItemAsync<string>("user_id");
50+
51+
model.User_id = user_id;
52+
_processingNewItem = true;
53+
await DatabaseService.Insert<TodoPrivate>(model);
54+
model = new();
55+
await GetTable();
56+
success = false;
57+
_processingNewItem = false;
58+
}
59+
}

Examples/BlazorWebAssemblySupabaseTemplate/Program.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,37 @@
2828

2929

3030
// ---------- BLAZOR AUTH
31-
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
31+
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>(
32+
provider => new CustomAuthStateProvider(
33+
provider.GetRequiredService<ILocalStorageService>(),
34+
provider.GetRequiredService<Supabase.Client>()
35+
)
36+
)
37+
;
3238
builder.Services.AddAuthorizationCore();
3339

34-
35-
3640
// ---------- SUPABASE
3741
var url = "https://pylnesfgmytjegzzculn.supabase.co";
3842
var key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InB5bG5lc2ZnbXl0amVnenpjdWxuIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NjgyOTMwMzcsImV4cCI6MTk4Mzg2OTAzN30.kI29Q_qYWDH5SD6oi5NTwHG6Pxy1e1AUfR8s_ga45lE";
3943

40-
builder.Services.AddScoped<ISupabaseClient<User, Session, Socket, Channel, Bucket, FileObject>>(args => new Supabase.Client(url, key, new Supabase.SupabaseOptions { AutoConnectRealtime = true }));
44+
builder.Services.AddScoped<Supabase.Client>(
45+
provider => new Supabase.Client(
46+
url,
47+
key,
48+
new Supabase.SupabaseOptions
49+
{
50+
AutoRefreshToken = true,
51+
AutoConnectRealtime = true,
52+
PersistSession = true,
53+
SessionHandler = new CustomSupabaseSessionHandler(
54+
provider.GetRequiredService<ILocalStorageService>(),
55+
provider.GetRequiredService<ILogger<CustomSupabaseSessionHandler>>()
56+
)
57+
}
58+
)
59+
);
60+
61+
// builder.Services.AddScoped<ISupabaseClient<User, Session, Socket, Channel, Bucket, FileObject>>(args => new Supabase.Client(url, key, new Supabase.SupabaseOptions { AutoConnectRealtime = true }));
4162
builder.Services.AddScoped<AuthService>();
4263
builder.Services.AddScoped<DatabaseService>();
4364

Examples/BlazorWebAssemblySupabaseTemplate/Providers/CustomAuthStateProvider.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,35 @@ namespace BlazorWebAssemblySupabaseTemplate.Providers;
1111
public class CustomAuthStateProvider : AuthenticationStateProvider
1212
{
1313
private readonly ILocalStorageService _localStorage;
14+
private readonly Supabase.Client _client;
1415
// private readonly IHttpClientFactory httpClientFactory;
1516

1617
// private readonly HttpClient _http;
1718

1819
public CustomAuthStateProvider(
19-
ILocalStorageService localStorage
20+
ILocalStorageService localStorage,
21+
Supabase.Client client
2022
// IHttpClientFactory httpClientFactory
2123
// HttpClient http
2224
)
2325
{
2426
_localStorage = localStorage;
27+
_client = client;
2528
// this.httpClientFactory = httpClientFactory;
2629
// _http = http;
2730
}
2831

2932
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
3033
{
31-
string token = await _localStorage.GetItemAsStringAsync("token");
32-
// string token = "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTUxMiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiVG9ueSBTdGFyayIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6Iklyb24gTWFuIiwiZXhwIjozMTY4NTQwMDAwfQ.IbVQa1lNYYOzwso69xYfsMOHnQfO3VLvVqV2SOXS7sTtyyZ8DEf5jmmwz2FGLJJvZnQKZuieHnmHkg7CGkDbvA";
33-
34+
// Sets client auth and connects to realtime (if enabled)
35+
await _client.InitializeAsync();
36+
3437
var identity = new ClaimsIdentity();
3538
// _http.DefaultRequestHeaders.Authorization = null;
3639

37-
if (!string.IsNullOrEmpty(token))
40+
if (!string.IsNullOrEmpty(_client.Auth.CurrentSession?.AccessToken))
3841
{
39-
identity = new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt");
42+
identity = new ClaimsIdentity(ParseClaimsFromJwt(_client.Auth.CurrentSession.AccessToken), "jwt");
4043
// _http.DefaultRequestHeaders.Authorization =
4144
// new AuthenticationHeaderValue("Bearer", token.Replace("\"", ""));
4245
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Blazored.LocalStorage;
2+
using Supabase.Gotrue;
3+
using Supabase.Interfaces;
4+
5+
namespace BlazorWebAssemblySupabaseTemplate.Providers;
6+
7+
public class CustomSupabaseSessionHandler : ISupabaseSessionHandler
8+
{
9+
private readonly ILocalStorageService localStorage;
10+
private readonly ILogger<CustomSupabaseSessionHandler> logger;
11+
private static string SESSION_KEY = "SUPABASE_SESSION";
12+
13+
public CustomSupabaseSessionHandler(
14+
ILocalStorageService localStorage,
15+
ILogger<CustomSupabaseSessionHandler> logger
16+
)
17+
{
18+
logger.LogInformation("------------------- CONSTRUCTOR -------------------");
19+
this.localStorage = localStorage;
20+
this.logger = logger;
21+
}
22+
23+
public async Task<bool> SessionDestroyer()
24+
{
25+
logger.LogInformation("------------------- SessionDestroyer -------------------");
26+
await localStorage.RemoveItemAsync(SESSION_KEY);
27+
return true;
28+
}
29+
30+
public async Task<bool> SessionPersistor<TSession>(TSession session) where TSession : Session
31+
{
32+
logger.LogInformation("------------------- SessionPersistor -------------------");
33+
await localStorage.SetItemAsync(SESSION_KEY, session);
34+
return true;
35+
}
36+
37+
public async Task<TSession?> SessionRetriever<TSession>() where TSession : Session
38+
{
39+
logger.LogInformation("------------------- SessionRetriever -------------------");
40+
return (TSession?) await localStorage.GetItemAsync<Session>(SESSION_KEY);
41+
}
42+
}

0 commit comments

Comments
 (0)