Open source geo based video sharing social app created with Flutter, Supabase and lots of love πŸ’™πŸ’™πŸ’™

Overview

Spot

Take a virtual journey around the world with Spot.

Download from AppStoreGet it on Google Play

Spot is a geo-tagged video sharing app, meaning every video recorded in Spot is saved on a location. Anybody can scroll though the map and find the videos recorded in any location in the world.

DISCOVER With Spot, you can view what is going on anywhere in the world. All videos in Spot are geo-tagged, meaning they are marked at the location they were recorded. That means if you want to take a look at what is going on in New York right now, you just go to NewYork within the app and view what people are posting. Same for LA, Paris, Tokyo or anywhere else in the world!

CREATE You can record Spot videos to let your everyone else know what is happening wherever you are. With our intuitive video recorder, you will be able to capture the moment easily.

ENJOY How you utilise Spot is up to you! You may be able to check out the local festival without actual going there, discover hidden local amazing restaurants, or virtually visit your favorite city in the world. When you visit amazing places, don't forget to save that memory inside Spot so that the world can know about that place!

Spot Screenshots


Figma

https://www.figma.com/file/OBSvD6eG4eDno3aQ76Ovzo/Spot?node-id=2%3A1023


Supabase Database Schema

Please note that you need enable postgis database extensions before run import database schema Also, email confirmation is turned off at the moment.

= 1) ); comment on table public.comments is 'Holds all of the comments created by the users.'; alter table public.comments enable row level security; create policy "Comments are viewable by everyone. " on public.comments for select using (true); create policy "Can insert comments" on public.comments for insert with check (auth.uid() = user_id); create policy "Can update comments" on public.comments for update using (auth.uid() = user_id) with check (auth.uid() = user_id); create policy "Can delete comments" on public.comments for delete using (auth.uid() = user_id); create table if not exists public.mentions ( id uuid not null primary key DEFAULT uuid_generate_v4 (), comment_id uuid references public.comments on delete cascade not null, user_id uuid references public.users on delete cascade not null ); comment on table public.comments is 'Holds all of the mentions within comments'; alter table public.mentions enable row level security; create policy "Mentions are viewable by everyone. " on public.mentions for select using (true); create policy "Mentions can be inserted by the creator of the comment. " on public.mentions for insert with check (auth.uid() = (select user_id from public.comments where comments.id = mentions.comment_id)); create policy "Mentions can be updated by the creator of the comment." on public.mentions for update using (auth.uid() = (select user_id from public.comments where comments.id = mentions.comment_id)) with check (auth.uid() = (select user_id from public.comments where comments.id = mentions.comment_id)); create policy "Mentions can be deleted by the creator of the comment." on public.mentions for delete using (auth.uid() = (select user_id from public.comments where comments.id = mentions.comment_id)); create table if not exists public.likes ( video_id uuid references public.videos on delete cascade not null, user_id uuid references public.users on delete cascade not null, created_at timestamp with time zone default timezone('utc' :: text, now()) not null, PRIMARY KEY (video_id, user_id) ); comment on table public.likes is 'Holds all of the like data created by thee users.'; alter table public.likes enable row level security; create policy "Likes are viewable by everyone. " on public.likes for select using (true); create policy "Users can insert their own likes." on public.likes for insert with check (auth.uid() = user_id); create policy "Users can delete own likes." on public.likes for delete using (auth.uid() = user_id); create table if not exists public.follow ( following_user_id uuid references public.users on delete cascade not null, followed_user_id uuid references public.users on delete cascade not null, followed_at timestamp with time zone default timezone('utc' :: text, now()) not null, primary key (following_user_id, followed_user_id) ); comment on table public.follow is 'Creates follow follower relationships.'; alter table public.follow enable row level security; create policy "Follows are viewable by everyone. " on public.follow for select using (true); create policy "Users can follow anyone" on public.follow for insert with check (auth.uid() = following_user_id); create policy "Users can unfollow their follows and ssers can remove their followers" on public.follow for delete using (auth.uid() = following_user_id or auth.uid() = followed_user_id); create table if not exists public.blocks ( user_id uuid references public.users on delete cascade not null, blocked_user_id uuid references public.users on delete cascade not null, created_at timestamp with time zone default timezone('utc' :: text, now()) not null, primary key (user_id, blocked_user_id), constraint username_validation check (user_id != blocked_user_id) ); comment on table public.blocks is 'Holds information of who is blocking who.'; alter table public.blocks enable row level security; create policy "Users can view who they are blocking." on public.blocks for select using (auth.uid() = user_id); create policy "Users can block anyone by themselves. " on public.blocks for insert with check (auth.uid() = user_id); create table if not exists public.reports ( id uuid not null primary key DEFAULT uuid_generate_v4 (), user_id uuid references public.users on delete cascade not null, video_id uuid references public.videos on delete cascade not null, reason text not null, created_at timestamp with time zone default timezone('utc' :: text, now()) not null ); comment on table public.reports is 'Who reported which video for what reason.'; alter table public.reports enable row level security; create policy "Users can view their own reports." on public.reports for select using (auth.uid() = user_id); create policy "Users can report a video." on public.reports for insert with check (auth.uid() = user_id); create or replace view video_comments as select comments.id, comments.text, comments.created_at, comments.video_id, users.id as user_id, users.name as user_name, users.description as user_description, users.image_url as user_image_url from comments join users on comments.user_id = users.id; create or replace function nearby_videos(location text, user_id uuid) returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, location text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text) as $func$ select videos.id, videos.url, videos.image_url, videos.thumbnail_url, videos.gif_url, st_astext(videos.location) as location, videos.created_at, videos.description, users.id as user_id, users.name as user_name, users.description as user_description, users.image_url as user_image_url from videos join users on videos.user_id = users.id where users.id not in (select blocked_user_id from blocks where user_id = user_id) order by location <-> st_geogfromtext($1); $func$ language sql; create or replace function videos_in_bouding_box(min_lng decimal, min_lat decimal, max_lng decimal, max_lat decimal, user_id uuid) returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, location text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text) as $func$ select videos.id, videos.url, videos.image_url, videos.thumbnail_url, videos.gif_url, st_astext(videos.location) as location, videos.created_at, videos.description, users.id as user_id, users.name as user_name, users.description as user_description, users.image_url as user_image_url from videos join users on videos.user_id = users.id where users.id not in (select blocked_user_id from blocks where user_id = user_id) and location && ST_SetSRID(ST_MakeBox2D(ST_Point(min_lng, min_lat), ST_Point(max_lng, max_lat)),4326); $func$ language sql; create or replace function get_video_detail(video_id uuid, user_id uuid) returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text, location text, like_count int, comment_count int, have_liked int) as $func$ select videos.id, videos.url, videos.image_url, videos.thumbnail_url, videos.gif_url, videos.created_at, videos.description, users.id as user_id, users.name as user_name, users.description as user_description, users.image_url as user_image_url, st_astext(videos.location) as location, (select count(*) from likes where video_id = videos.id)::int as like_count, (select count(*) from comments where video_id = videos.id)::int as comment_count, (select count(*) from likes where video_id = videos.id and user_id = $2)::int as have_liked from videos join users on videos.user_id = users.id where videos.id = $1; $func$ language sql; create or replace function anonymous_get_video_detail(video_id uuid) returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text, location text, like_count int, comment_count int, have_liked int) as $func$ select videos.id, videos.url, videos.image_url, videos.thumbnail_url, videos.gif_url, videos.created_at, videos.description, users.id as user_id, users.name as user_name, users.description as user_description, users.image_url as user_image_url, st_astext(videos.location) as location, (select count(*) from likes where video_id = videos.id)::int as like_count, (select count(*) from comments where video_id = videos.id)::int as comment_count, (0)::int as have_liked from videos join users on videos.user_id = users.id where videos.id = $1; $func$ language sql; create or replace view notifications as select 'like' as type, videos.user_id as receiver_user_id, null as comment_text, videos.id as video_id, videos.thumbnail_url as video_thumbnail_url, likes.user_id as action_user_id, users.name as action_user_name, users.image_url as action_user_image_url, likes.created_at from likes join users on likes.user_id = users.id join videos on videos.id = likes.video_id union all select 'comment' as type, videos.user_id as receiver_user_id, comments.text as comment_text, videos.id as video_id, videos.thumbnail_url as video_thumbnail_url, comments.user_id as action_user_id, users.name as action_user_name, users.image_url as action_user_image_url, comments.created_at from comments join users on comments.user_id = users.id join videos on videos.id = comments.video_id union all select 'mentioned' as type, mentions.user_id as receiver_user_id, comments.text as comment_text, videos.id as video_id, videos.thumbnail_url as video_thumbnail_url, comments.user_id as action_user_id, users.name as action_user_name, users.image_url as action_user_image_url, comments.created_at from comments join mentions on comments.id = mentions.comment_id join users on comments.user_id = users.id join videos on videos.id = comments.video_id union all select 'follow' as type, follow.followed_user_id as receiver_user_id, null as commennt_text, null as video_id, null as video_thumbnail_url, follow.following_user_id as action_user_id, users.name as action_user_name, users.image_url as action_user_image_url, follow.followed_at as created_at from follow join users on follow.following_user_id = users.id order by created_at desc; -- Configure storage insert into storage.buckets (id, name) values ('videos', 'videos'); insert into storage.buckets (id, name) values ('profiles', 'profiles'); create policy "Videos buckets are public" on storage.objects for select using (bucket_id = 'videos'); create policy "Profiles buckets are public" on storage.objects for select using (bucket_id = 'profiles'); create policy "uid has to be the first element in path_tokens" on storage.objects for insert with check (auth.uid()::text = path_tokens[1] and array_length(path_tokens, 1) = 2); -- Needed to use extensions from the app grant usage on schema extensions to anon; grant usage on schema extensions to authenticated; -- Migrations -- 2021/08/28 alter table public.follow add constraint fk_following foreign key(following_user_id) references users(id); alter table public.follow add constraint fk_followed foreign key(followed_user_id) references users(id); alter table public.follow add constraint follow_validation check (following_user_id != followed_user_id); drop function public.nearby_videos(text, uuid); create or replace function public.nearby_videos(location text, user_id uuid) returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, location text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text, is_following bool) as $func$ select videos.id, videos.url, videos.image_url, videos.thumbnail_url, videos.gif_url, st_astext(videos.location) as location, videos.created_at, videos.description, users.id as user_id, users.name as user_name, users.description as user_description, users.image_url as user_image_url, (select cast(case when EXISTS ( SELECT * FROM follow WHERE follow.followed_user_id = videos.user_id and follow.following_user_id = $2 ) then true else false end as bool)) as is_following from videos join users on videos.user_id = users.id left join follow on videos.user_id = follow.followed_user_id where users.id not in (select blocked_user_id from blocks where user_id = $2) order by location <-> st_geogfromtext($1); $func$ language sql; drop function videos_in_bouding_box(decimal, decimal, decimal, decimal, uuid); create or replace function videos_in_bouding_box(min_lng decimal, min_lat decimal, max_lng decimal, max_lat decimal, user_id uuid) returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, location text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text, is_following bool) as $func$ select videos.id, videos.url, videos.image_url, videos.thumbnail_url, videos.gif_url, st_astext(videos.location) as location, videos.created_at, videos.description, users.id as user_id, users.name as user_name, users.description as user_description, users.image_url as user_image_url, (select cast(case when EXISTS ( SELECT * FROM follow WHERE follow.followed_user_id = videos.user_id and follow.following_user_id = $5 ) then true else false end as bool)) as is_following from videos join users on videos.user_id = users.id where users.id not in (select blocked_user_id from blocks where user_id = user_id) and location && ST_SetSRID(ST_MakeBox2D(ST_Point(min_lng, min_lat), ST_Point(max_lng, max_lat)),4326); $func$ language sql; create or replace function profile_detail(my_user_id uuid, target_user_id uuid) returns table(id uuid, name text, description text, image_url text, follower_count bigint, following_count bigint, like_count bigint, is_following bool) as $func$ select id, name, description, image_url, (select count(*) from follow where followed_user_id = $2) as follower_count, (select count(*) from follow where following_user_id = $2) as following_count, (select count(*) from likes join videos on videos.id = likes.video_id where videos.user_id = $2) as like_count, (select cast(case when EXISTS ( SELECT * FROM follow WHERE follow.followed_user_id = $2 and follow.following_user_id = $1 ) then true else false end as bool)) as is_following from users where id = $2; $func$ language sql; create or replace view liked_videos as select videos.id, videos.user_id, videos.created_at, videos.url, videos.image_url, videos.thumbnail_url, videos.gif_url, videos.description, likes.user_id as liked_by, likes.created_at as liked_at from videos join likes on videos.id = likes.video_id; -- create a view for followed users create or replace function followers(my_user_id uuid, target_user_id uuid) returns table(id uuid, name text, description text, image_url text, is_following bool) as $func$ select users.id, users.name, users.description, users.image_url, (select cast(case when EXISTS ( SELECT * FROM follow WHERE followed_user_id = p_follow.following_user_id and following_user_id = $1 ) then true else false end as bool)) as is_following from users join follow p_follow on users.id = p_follow.following_user_id where p_follow.followed_user_id = $2 order by p_follow.followed_at desc; $func$ language sql; create or replace function followings(my_user_id uuid, target_user_id uuid) returns table(id uuid, name text, description text, image_url text, is_following bool) as $func$ select users.id, users.name, users.description, users.image_url, (select cast(case when EXISTS ( SELECT * FROM follow WHERE followed_user_id = p_follow.followed_user_id and following_user_id = $1 ) then true else false end as bool)) as is_following from users join follow p_follow on users.id = p_follow.followed_user_id where p_follow.following_user_id = $2 order by p_follow.followed_at desc; $func$ language sql; -- 2021/09/16 update storage.buckets set public = true where id = 'videos'; update storage.buckets set public = true where id = 'profiles'; ">
create table if not exists public.users (
  id uuid references auth.users on delete cascade not null primary key,
  name varchar(18) not null unique,
  description varchar(320) not null,
  image_url text,

  constraint username_validation check (char_length(name) >= 1)
);
comment on table public.users is 'Holds all of users profile information';

alter table public.users enable row level security;
create policy "Public profiles are viewable by everyone." on public.users for select using (true);
create policy "Can insert user" on public.users for insert with check (auth.uid() = id);
create policy "Can update user" on public.users for update using (auth.uid() = id) with check (auth.uid() = id);
create policy "Can delete user" on public.users for delete using (auth.uid() = id);


create table if not exists public.videos (
    id uuid not null primary key DEFAULT uuid_generate_v4 (),
    user_id uuid references public.users on delete cascade not null,
    created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
    url text not null,
    image_url text not null,
    thumbnail_url text not null,
    gif_url text not null,
    description varchar(320) not null,
    location geography(POINT) not null
);
comment on table public.videos is 'Holds all the video videos.';

alter table public.videos enable row level security;
create policy "Videos are viewable by everyone. " on public.videos for select using (true);
create policy "Can insert videos" on public.videos for insert with check (auth.uid() = user_id);
create policy "Can update videos" on public.videos for update using (auth.uid() = user_id) with check (auth.uid() = user_id);
create policy "Can delete videos" on public.videos for delete using (auth.uid() = user_id);


create table if not exists public.comments (
    id uuid not null primary key DEFAULT uuid_generate_v4 (),
    video_id uuid references public.videos on delete cascade not null,
    user_id uuid references public.users on delete cascade not null,
    created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
    text varchar(320) not null,

    constraint comment_length check (char_length(text) >= 1)
);
comment on table public.comments is 'Holds all of the comments created by the users.';

alter table public.comments enable row level security;
create policy "Comments are viewable by everyone. " on public.comments for select using (true);
create policy "Can insert comments" on public.comments for insert with check (auth.uid() = user_id);
create policy "Can update comments" on public.comments for update using (auth.uid() = user_id) with check (auth.uid() = user_id);
create policy "Can delete comments" on public.comments for delete using (auth.uid() = user_id);

create table if not exists public.mentions (
    id uuid not null primary key DEFAULT uuid_generate_v4 (),
    comment_id uuid references public.comments on delete cascade not null,
    user_id uuid references public.users on delete cascade not null
);
comment on table public.comments is 'Holds all of the mentions within comments';

alter table public.mentions enable row level security;
create policy "Mentions are viewable by everyone. " on public.mentions for select using (true);
create policy "Mentions can be inserted by the creator of the comment. " on public.mentions for insert with check (auth.uid() = (select user_id from public.comments where comments.id = mentions.comment_id));
create policy "Mentions can be updated by the creator of the comment." on public.mentions for update using (auth.uid() = (select user_id from public.comments where comments.id = mentions.comment_id)) with check (auth.uid() = (select user_id from public.comments where comments.id = mentions.comment_id));
create policy "Mentions can be deleted by the creator of the comment." on public.mentions for delete using (auth.uid() = (select user_id from public.comments where comments.id = mentions.comment_id));


create table if not exists public.likes (
    video_id uuid references public.videos on delete cascade not null,
    user_id uuid references public.users on delete cascade not null,
    created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
    PRIMARY KEY (video_id, user_id)
);
comment on table public.likes is 'Holds all of the like data created by thee users.';

alter table public.likes enable row level security;
create policy "Likes are viewable by everyone. " on public.likes for select using (true);
create policy "Users can insert their own likes." on public.likes for insert with check (auth.uid() = user_id);
create policy "Users can delete own likes." on public.likes for delete using (auth.uid() = user_id);


create table if not exists public.follow (
    following_user_id uuid references public.users on delete cascade not null,
    followed_user_id uuid references public.users on delete cascade not null,
    followed_at timestamp with time zone default timezone('utc' :: text, now()) not null,
    primary key (following_user_id, followed_user_id)
);
comment on table public.follow is 'Creates follow follower relationships.';

alter table public.follow enable row level security;
create policy "Follows are viewable by everyone. " on public.follow for select using (true);
create policy "Users can follow anyone" on public.follow for insert with check (auth.uid() = following_user_id);
create policy "Users can unfollow their follows and ssers can remove their followers" on public.follow for delete using (auth.uid() = following_user_id or auth.uid() = followed_user_id);

create table if not exists public.blocks (
    user_id uuid references public.users on delete cascade not null,
    blocked_user_id uuid references public.users on delete cascade not null,
    created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
    primary key (user_id, blocked_user_id),

    constraint username_validation check (user_id != blocked_user_id)
);
comment on table public.blocks is 'Holds information of who is blocking who.';

alter table public.blocks enable row level security;
create policy "Users can view who they are blocking." on public.blocks for select using (auth.uid() = user_id);
create policy "Users can block anyone by themselves. " on public.blocks for insert with check (auth.uid() = user_id);

create table if not exists public.reports (
    id uuid not null primary key DEFAULT uuid_generate_v4 (),
    user_id uuid references public.users on delete cascade not null,
    video_id uuid references public.videos on delete cascade not null,
    reason text not null,
    created_at timestamp with time zone default timezone('utc' :: text, now()) not null
);
comment on table public.reports is 'Who reported which video for what reason.';

alter table public.reports enable row level security;
create policy "Users can view their own reports." on public.reports for select using (auth.uid() = user_id);
create policy "Users can report a video." on public.reports for insert with check (auth.uid() = user_id);


create or replace view video_comments
as
    select
        comments.id,
        comments.text,
        comments.created_at,
        comments.video_id,
        users.id as user_id,
        users.name as user_name,
        users.description as user_description,
        users.image_url as user_image_url
    from comments
    join users on comments.user_id = users.id;


create or replace function nearby_videos(location text, user_id uuid)
returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, location text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text)
as
$func$
    select
        videos.id,
        videos.url,
        videos.image_url,
        videos.thumbnail_url,
        videos.gif_url,
        st_astext(videos.location) as location,
        videos.created_at,
        videos.description,
        users.id as user_id,
        users.name as user_name,
        users.description as user_description,
        users.image_url as user_image_url
    from videos
    join users on videos.user_id = users.id
    where users.id not in (select blocked_user_id from blocks where user_id = user_id)
    order by location <-> st_geogfromtext($1);
$func$
language sql;



create or replace function videos_in_bouding_box(min_lng decimal, min_lat decimal, max_lng decimal, max_lat decimal, user_id uuid)
returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, location text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text)
as
$func$
    select
        videos.id,
        videos.url,
        videos.image_url,
        videos.thumbnail_url,
        videos.gif_url,
        st_astext(videos.location) as location,
        videos.created_at,
        videos.description,
        users.id as user_id,
        users.name as user_name,
        users.description as user_description,
        users.image_url as user_image_url
    from videos
    join users on videos.user_id = users.id
    where users.id not in (select blocked_user_id from blocks where user_id = user_id)
    and location && ST_SetSRID(ST_MakeBox2D(ST_Point(min_lng, min_lat), ST_Point(max_lng, max_lat)),4326);
$func$
language sql;



create or replace function get_video_detail(video_id uuid, user_id uuid)
returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text, location text, like_count int, comment_count int, have_liked int)
as
$func$
    select
        videos.id,
        videos.url,
        videos.image_url,
        videos.thumbnail_url,
        videos.gif_url,
        videos.created_at,
        videos.description,
        users.id as user_id,
        users.name as user_name,
        users.description as user_description,
        users.image_url as user_image_url,
        st_astext(videos.location) as location,
        (select count(*) from likes where video_id = videos.id)::int as like_count,
        (select count(*) from comments where video_id = videos.id)::int as comment_count,
        (select count(*) from likes where video_id = videos.id and user_id = $2)::int as have_liked
    from videos
    join users on videos.user_id = users.id
    where videos.id = $1;
$func$
language sql;

create or replace function anonymous_get_video_detail(video_id uuid)
returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text, location text, like_count int, comment_count int, have_liked int)
as
$func$
    select
        videos.id,
        videos.url,
        videos.image_url,
        videos.thumbnail_url,
        videos.gif_url,
        videos.created_at,
        videos.description,
        users.id as user_id,
        users.name as user_name,
        users.description as user_description,
        users.image_url as user_image_url,
        st_astext(videos.location) as location,
        (select count(*) from likes where video_id = videos.id)::int as like_count,
        (select count(*) from comments where video_id = videos.id)::int as comment_count,
        (0)::int as have_liked
    from videos
    join users on videos.user_id = users.id
    where videos.id = $1;
$func$
language sql;

create or replace view notifications
as
    select
        'like' as type,
        videos.user_id as receiver_user_id,
        null as comment_text,
        videos.id as video_id,
        videos.thumbnail_url as video_thumbnail_url,
        likes.user_id as action_user_id,
        users.name as action_user_name,
        users.image_url as action_user_image_url,
        likes.created_at
    from likes
    join users on likes.user_id = users.id
    join videos on videos.id = likes.video_id
    union all
    select
        'comment' as type,
        videos.user_id as receiver_user_id,
        comments.text as comment_text,
        videos.id as video_id,
        videos.thumbnail_url as video_thumbnail_url,
        comments.user_id as action_user_id,
        users.name as action_user_name,
        users.image_url as action_user_image_url,
        comments.created_at
    from comments
    join users on comments.user_id = users.id
    join videos on videos.id = comments.video_id
    union all
    select
        'mentioned' as type,
        mentions.user_id as receiver_user_id,
        comments.text as comment_text,
        videos.id as video_id,
        videos.thumbnail_url as video_thumbnail_url,
        comments.user_id as action_user_id,
        users.name as action_user_name,
        users.image_url as action_user_image_url,
        comments.created_at
    from comments
    join mentions on comments.id = mentions.comment_id
    join users on comments.user_id = users.id
    join videos on videos.id = comments.video_id
    union all
    select
        'follow' as type,
        follow.followed_user_id as receiver_user_id,
        null as commennt_text,
        null as video_id,
        null as video_thumbnail_url,
        follow.following_user_id as action_user_id,
        users.name as action_user_name,
        users.image_url as action_user_image_url,
        follow.followed_at as created_at
    from follow
    join users on follow.following_user_id = users.id
    order by created_at desc;

-- Configure storage
insert into storage.buckets (id, name) values ('videos', 'videos');
insert into storage.buckets (id, name) values ('profiles', 'profiles');
create policy "Videos buckets are public" on storage.objects for select using (bucket_id = 'videos');
create policy "Profiles buckets are public" on storage.objects for select using (bucket_id = 'profiles');
create policy "uid has to be the first element in path_tokens" on storage.objects for insert with check (auth.uid()::text = path_tokens[1] and array_length(path_tokens, 1) = 2);


-- Needed to use extensions from the app
grant usage on schema extensions to anon;
grant usage on schema extensions to authenticated;

-- Migrations

-- 2021/08/28
alter table public.follow
    add constraint fk_following
    foreign key(following_user_id)
    references users(id);
alter table public.follow
    add constraint fk_followed
    foreign key(followed_user_id)
    references users(id);
alter table public.follow
    add constraint follow_validation
    check (following_user_id != followed_user_id);

drop function public.nearby_videos(text, uuid);

create or replace function public.nearby_videos(location text, user_id uuid)
returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, location text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text, is_following bool)
as
$func$
    select
        videos.id,
        videos.url,
        videos.image_url,
        videos.thumbnail_url,
        videos.gif_url,
        st_astext(videos.location) as location,
        videos.created_at,
        videos.description,
        users.id as user_id,
        users.name as user_name,
        users.description as user_description,
        users.image_url as user_image_url,
        (select cast(case when EXISTS ( SELECT * FROM follow WHERE follow.followed_user_id = videos.user_id and follow.following_user_id = $2 ) then true else false end as bool)) as is_following
    from videos
        join users on videos.user_id = users.id
        left join follow on videos.user_id = follow.followed_user_id
    where users.id not in (select blocked_user_id from blocks where user_id = $2)
    order by location <-> st_geogfromtext($1);
$func$
language sql;

drop function videos_in_bouding_box(decimal, decimal, decimal, decimal, uuid);

create or replace function videos_in_bouding_box(min_lng decimal, min_lat decimal, max_lng decimal, max_lat decimal, user_id uuid)
returns table(id uuid, url text, image_url text, thumbnail_url text, gif_url text, location text, created_at timestamptz, description text, user_id uuid, user_name text, user_description text, user_image_url text, is_following bool)
as
$func$
    select
        videos.id,
        videos.url,
        videos.image_url,
        videos.thumbnail_url,
        videos.gif_url,
        st_astext(videos.location) as location,
        videos.created_at,
        videos.description,
        users.id as user_id,
        users.name as user_name,
        users.description as user_description,
        users.image_url as user_image_url,
        (select cast(case when EXISTS ( SELECT * FROM follow WHERE follow.followed_user_id = videos.user_id and follow.following_user_id = $5 ) then true else false end as bool)) as is_following
    from videos
    join users on videos.user_id = users.id
    where users.id not in (select blocked_user_id from blocks where user_id = user_id)
    and location && ST_SetSRID(ST_MakeBox2D(ST_Point(min_lng, min_lat), ST_Point(max_lng, max_lat)),4326);
$func$
language sql;

create or replace function profile_detail(my_user_id uuid, target_user_id uuid)
returns table(id uuid, name text, description text, image_url text, follower_count bigint, following_count bigint, like_count bigint, is_following bool)
as
$func$
    select
        id,
        name,
        description,
        image_url,
        (select count(*) from follow where followed_user_id = $2) as follower_count,
        (select count(*) from follow where following_user_id = $2) as following_count,
        (select count(*) from likes join videos on videos.id = likes.video_id where videos.user_id = $2) as like_count,
        (select cast(case when EXISTS ( SELECT * FROM follow WHERE follow.followed_user_id = $2 and follow.following_user_id = $1 ) then true else false end as bool)) as is_following
    from users
    where id = $2;
$func$
language sql;

create or replace view liked_videos
as
    select
        videos.id,
        videos.user_id,
        videos.created_at,
        videos.url,
        videos.image_url,
        videos.thumbnail_url,
        videos.gif_url,
        videos.description,
        likes.user_id as liked_by,
        likes.created_at as liked_at
    from videos
    join likes on videos.id = likes.video_id;

-- create a view for followed users
create or replace function followers(my_user_id uuid, target_user_id uuid)
returns table(id uuid, name text, description text, image_url text, is_following bool)
as
$func$
    select
        users.id,
        users.name,
        users.description,
        users.image_url,
        (select cast(case when EXISTS ( SELECT * FROM follow WHERE followed_user_id = p_follow.following_user_id and following_user_id = $1 ) then true else false end as bool)) as is_following
    from users
    join follow p_follow on users.id = p_follow.following_user_id
    where p_follow.followed_user_id = $2
    order by p_follow.followed_at desc;
$func$
language sql;

create or replace function followings(my_user_id uuid, target_user_id uuid)
returns table(id uuid, name text, description text, image_url text, is_following bool)
as
$func$
    select
        users.id,
        users.name,
        users.description,
        users.image_url,
        (select cast(case when EXISTS ( SELECT * FROM follow WHERE followed_user_id = p_follow.followed_user_id and following_user_id = $1 ) then true else false end as bool)) as is_following
    from users
    join follow p_follow on users.id = p_follow.followed_user_id
    where p_follow.following_user_id = $2
    order by p_follow.followed_at desc;
$func$
language sql;

-- 2021/09/16
update storage.buckets set public = true where id = 'videos';
update storage.buckets set public = true where id = 'profiles';


Android CD https://medium.com/flutter-community/automating-publishing-your-flutter-apps-to-google-play-using-github-actions-2f67ac582032


Comments
  • Help with setup

    Help with setup

    Hey i am new to Supabase. I love your project and want to improve on it. Could you please provide a very detail doc or video as I could understand it better . The doc or vid should contain stuff like columns adding,add code etc. Overall a comprehensive to setup the spot. PLEASE REPLY AS IT WOULD BE REALLY HELPFULL FOR COLEEGE PROJECT

    opened by ritam749 13
  • Error while sign up

    Error while sign up

    Whenever i press signup button in the app it shows a error in both your and mine supabase projects The error is(image provided below) 2021-06-12 (6)

    Another error in signin page 2021-06-12 (5)

    Sorry for continuously troubling you

    opened by ritam749 5
  • Not working with my Supabase Credentials

    Not working with my Supabase Credentials

    When I run the app using my Supabase credentials, the Profile tab does nothing, therefore I can't create a new account or log in. I made a tab that links directly to the login_page and I was able to create an account and update my profile. But then it was just stuck.

    I restarted the app and profile tab still broken, but was able to make a video. The database entry worked and the files got uploaded but then I couldn't view them in the app. Just getting lot's of random errors and exceptions. I can't quite pinpoint it.

    I tried to record another video and It recorded but then when I went to save it I got an exception: Invalid statusCode: 400

    Changed back to your Supabase credentials and all works well.

    To confirm, the steps are:

    Supabase: Enable GIS extensions Disable Enable Email Confirmations Run your provided SQL

    Flutter: Clone the spot repo Open launch.json and change to my Supabase settings SUPABASE_URL (My Supabase Config URL) SUPABASE_ANNON_KEY (My Supabase Project Key anon/public) Run/Debug

    Is there anything I'm missing??

    Thanks

    opened by jtkeyva 4
  • Schema Syntax Error

    Schema Syntax Error

    I'm getting this when pasting in your query:

    syntax error at or near ")"

    It' highlighting the error at line 88. Any help appreciated. Thanks

    opened by jtkeyva 3
  • Google map integration

    Google map integration

    I am very thankful for you making a video to help me with supabase. Just one more question, where do i put google map api key and are there other api keys i need to put. And is there anythong else i need to use to make this work as my own project, to show to the teachers. Thank you in advance

    opened by ritam749 2
  • feat: Use better player as the video player

    feat: Use better player as the video player

    Description

    Use better player as the video player to leverage caching.

    Type of Change

    • [x] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by dshukertjr 1
  • Fix hero animation on iOS

    Fix hero animation on iOS

    Hero animation on iOS of map page starts from a weird location, so fixing that.

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [x] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by dshukertjr 1
  • Error when building release apk

    Error when building release apk

    Sorry for again troubling you, see when i run command:

    flutter build apk --release --flavor production --target lib/main.dart --dart-define=SUPABASE_URL=https://hiafvrhvwgytmljrbjwx.supabase.co --dart-define=SUPABASE_ANNON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYyMzUwNTMxNSwiZXhwIjoxOTM5MDgxMzE1fQ.Oi1d6ygWatzXNlTbSzMGM34MQZESIwNdRfTgN2TsMvU

    It shows error:

    `FAILURE: Build failed with an exception.

    • What went wrong: Execution failed for task ':app:lintVitalProductionRelease'.

    Could not resolve all artifacts for configuration ':app:stagingReleaseRuntimeClasspath'. Failed to transform libs.jar to match attributes {artifactType=processed-jar, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime}. > Execution failed for JetifyTransform: C:\spot\build\app\intermediates\flutter\stagingRelease\libs.jar. > Transform's input file does not exist: C:\spot\build\app\intermediates\flutter\stagingRelease\libs.jar. (See https://issuetracker.google.com/issues/158753935)

    • Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

    • Get more help at https://help.gradle.org

    BUILD FAILED in 11s Running Gradle task 'assembleProductionRelease'...
    Running Gradle task 'assembleProductionRelease'... Done 12.4s Gradle task assembleProductionRelease failed with exit code 1

    I tried searching on google and tried all the answers on stack overflow but none worked .Could please help me, I would be obliged. Thank you in advance

    opened by ritam749 1
  • add enable postgis database extensions in README

    add enable postgis database extensions in README

    Description

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [X] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by WingCH 1
  • fix: initial camera will load to initially loaded videos.

    fix: initial camera will load to initially loaded videos.

    Description

    Related #52

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [x] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by dshukertjr 0
  • feat: Add comments on every public member

    feat: Add comments on every public member

    Description

    Added comments on every public members, so that new users can easily understand what each class, methods, or variable is doing.

    Type of Change

    • [ ] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [x] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by dshukertjr 0
  • feat: Redirect the user to home screen once upload of a video starts

    feat: Redirect the user to home screen once upload of a video starts

    UX improvement. Kind of like how Instagram does it. User should be free to do whatever they want to do while uploading the video instead of just staring at loader.

    opened by dshukertjr 0
  • feat: Add app link handling

    feat: Add app link handling

    Description

    The app will now go to the post page when an app link was opened.

    Type of Change

    • [x] ✨ New feature (non-breaking change which adds functionality)
    • [ ] πŸ› οΈ Bug fix (non-breaking change which fixes an issue)
    • [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
    • [ ] 🧹 Code refactor
    • [ ] βœ… Build configuration change
    • [ ] πŸ“ Documentation
    • [ ] πŸ—‘οΈ Chore
    opened by dshukertjr 0
  • UserID

    UserID

    I'm adding some custom features, but are most used to firebase auth. Can you help me with the how to identify the user in a new page? Ps. Love your work!

    opened by daremeapp 4
Releases(1.3.1)
Owner
Tyler
Developer based in Tokyo Japan. Supabase, Firebase, Flutter, Next.js Speak Japanese and English Discord: dshukertjr#2719
Tyler
Advanced video player based on video_player and Chewie for flutter

Better Player Advanced video player based on video_player and Chewie. It's solves many typical use cases and it's easy to run. Introduction This plugi

Ahmed Mahmoud 1 Dec 22, 2021
Flutter Music Player - A complete and open source music player designed in flutter.

Flutter Music Player A complete and open source music player designed in flutter. It is first complete music player designed in flutter. This app expl

Nabraj Khadka 3 Aug 20, 2022
A view for video based on video_player and provides many basic functions.

flutter_video_view A view for video based on video_player and provides many basic functions. Getting Started This project is a starting point for a Fl

LiWeNHuI 3 Dec 9, 2022
Virlow Flutter Recorder - an open-source Flutter application that can transcribe recorded audio

The Virlow Flutter Recorder is an open-source Flutter application that can transcribe recorded audio, plus it includes TL;DR and Short Hand Notes for your transcription. It also consists of a rich text editor that allows you to edit the transcription plus add any additional notes you require.

null 12 Dec 26, 2022
Open source audiobooks player

Audiobooks Open source audiobooks player Getting Started ?? This project contains 3 flavors: development staging production To run the desired flavor

Roy Matero 6 May 13, 2022
Social media for super cool developers πŸ§‘πŸΎβ€πŸ’»πŸ‘‹πŸ½

Develove ????‍?? Demo link: Develove App release: Develove.apk Develove is a super cool social media forum that aims to bring in the best features of

Luxecraft 21 Dec 9, 2022
Official Flutter SDK for LiveKit. Easily add real-time video and audio to your Flutter apps.

LiveKit Flutter SDK Official Flutter SDK for LiveKit. Easily add real-time video and audio to your Flutter apps. This package is published to pub.dev

LiveKit 116 Dec 14, 2022
video call with WebRTC and Flutter

Video Call Flutter App ?? Description: This is sandbox video call application using Flutter and WebRTC, you can call from browser to browser, phone to

Dao Hong Vinh 21 Nov 9, 2022
Video call with WebRTC and Flutter

This is sandbox video call application using Flutter and WebRTC, you can call from browser to browser, phone to phone, browser to phone and opposite.

Dao Hong Vinh 21 Nov 9, 2022
Sandbox video call application using Flutter and WebRTC

Video Call Flutter App ?? Description: This is sandbox video call application using Flutter and WebRTC, you can call from browser to browser, phone to

Dao Hong Vinh 21 Nov 9, 2022
This is a flutter package of video player. it's a very simple and easy to use.

This is a flutter package of video player. it's a very simple and easy to use.

εˆε†¬ 184 Nov 18, 2022
A fully-functional video streaming app made in Flutter using Custom Nodejs backend.

LAVENDER ?? A fully-functional video streaming app like netflix made in Flutter using Custom Nodejs backend. How To Run This Project ??‍♂️ Clone the r

null 71 Jan 10, 2023
Flutter plugin for use Video.js in flutter web

Flutter Video.js player Flutter plugin for use Video.js in flutter web Installation Add it to your package's pubspec.yaml file dependencies: video_j

null 15 Oct 17, 2022
The video player for Flutter with a heart of gold

chewie The video player for Flutter with a heart of gold. The video_player plugin provides low-level access to video playback. Chewie uses the video_p

Brian Egan 1.6k Jan 7, 2023
Flutter video trimmer package

A Flutter package for trimming videos Features Customizable video trimmer Video playback control Retrieving and storing video file Also, supports conv

Souvik Biswas 349 Jan 3, 2023
Better video player for Flutter, with multiple configuration options. Solving typical use cases!

Better video player for Flutter, with multiple configuration options. Solving typical use cases!

Jakub 732 Jan 2, 2023
Fleo - A video calling application developed using flutter🀠

Fleo ?? Video Calling Application developed using flutter Light and Dark Modes ?? Join using Room Codes ?? One room can accomodate upto 4 persons Powe

Madhav Pruthi 41 Dec 25, 2022
Base on Vap to play alpha video animation

Backdrop Transparent video animation is currently one of the more popular implementations of animation. Major manufacturers have also open source

null 89 Dec 27, 2022
A cloudinatry video url sample project

cloudinary_media_sample A new Flutter project for Cloudinary video url. Getting Started This project is a starting point for a Flutter application. A

null 0 Nov 4, 2021